Scripting the Win32 API - F# and IronPython FFI
Mainly a worked example for reminding me of how the syntax goes, reimplementing sn -k
in F# and IronPython:
open System | |
open System.Diagnostics | |
open System.IO | |
open System.Runtime.InteropServices | |
#nowarn "51" | |
// "The use of native pointers may result in unverifiable .NET IL code" -- | |
// for arguments &&keyBlob, &&generatedSize in the call to StrongNameKeyGenEx | |
module NativeMethods = | |
[<DllImport("mscoree.dll")>] | |
extern int StrongNameErrorInfo() | |
[<DllImport("mscoree.dll")>] | |
extern bool StrongNameKeyGenEx( | |
[<MarshalAs(UnmanagedType.LPWStr)>]String wszKeyContainer, | |
int dwFlags, int dwKeySize, IntPtr* ppbKeyBlob, int* pcbKeyBlob) | |
[<DllImport("mscoree.dll")>] | |
extern void StrongNameFreeBuffer(IntPtr pbMemory) | |
let GetLastStrongNameError() = | |
Marshal.GetExceptionForHR(NativeMethods.StrongNameErrorInfo()).Message | |
let GenerateKeyPair (keyK : int) = | |
if keyK < 0 || keyK > 2 then | |
raise (new ArgumentOutOfRangeException("keyK", keyK, "Invalid Key Size -- should be 1 or 2")) | |
// variables that hold the unmanaged key | |
let mutable keyBlob = IntPtr.Zero | |
let mutable generatedSize = 0 | |
// create the key | |
let createdKey = NativeMethods.StrongNameKeyGenEx( | |
null, 0, 1024 * keyK, | |
&&keyBlob, &&generatedSize) | |
try | |
// if there was a problem, translate it and report it | |
if not createdKey || keyBlob = IntPtr.Zero then | |
raise (new InvalidOperationException(GetLastStrongNameError())) | |
// make sure the key size makes sense | |
if generatedSize <= 0 || generatedSize > Int32.MaxValue then | |
raise (new InvalidOperationException("InternalError")) | |
// get the key into managed memory | |
let key = Array.create generatedSize 0uy | |
Marshal.Copy(keyBlob, key, 0, generatedSize) | |
key | |
finally | |
// release the unmanaged memory the key resides in | |
if keyBlob <> IntPtr.Zero then | |
NativeMethods.StrongNameFreeBuffer(keyBlob) | |
// path to generate new string name key | |
let keypath = fsi.CommandLineArgs.[1] | |
// write the key to the specified file if it is not already present | |
if not (File.Exists(keypath)) then | |
let key = GenerateKeyPair 1 | |
use snkStream = new FileStream(keypath, FileMode.Create, FileAccess.Write) | |
use snkWriter = new BinaryWriter(snkStream) | |
snkWriter.Write(key) |
This about halfway between how you'd just call the APIs naturally in C++/CLI, and the full process of C# P/Invoke. You do need to do your own extern
method declarations, whereas IronPython is something else again:
from System import Int32, Byte, IntPtr | |
import System | |
from System.IO import * | |
from System.Runtime.InteropServices import Marshal | |
from ctypes import * | |
import sys | |
mscoree = windll.mscoree | |
def GetLastStrongNameError(): | |
error = Marshal.GetExceptionForHR(mscoree.StrongNameErrorInfo()) | |
return error.Message | |
def GenerateKeyPair (keyK): | |
if keyK < 0 or keyK > 2 : | |
raise System.ArgumentOutOfRangeException( | |
"keyK", keyK, "Invalid Key Size -- should be 1 or 2") | |
# variables that hold the unmanaged key | |
keyBlob = c_void_p() | |
generatedSize = c_uint(0) | |
# create the key | |
createdKey = mscoree.StrongNameKeyGenEx( | |
None, | |
c_uint(0), | |
c_uint(1024 * keyK), | |
byref(keyBlob), | |
byref(generatedSize)) | |
try: | |
# if there was a problem, translate it and report it | |
if not createdKey or keyBlob.value == 0: | |
raise System.InvalidOperationException(GetLastStrongNameError()) | |
#make sure the key size makes sense | |
generatedSize = generatedSize.value | |
if generatedSize <= 0 or generatedSize > Int32.MaxValue: | |
raise System.InvalidOperationException("InternalError") | |
# get the key into managed memory | |
key = System.Array.CreateInstance(Byte, generatedSize) | |
Marshal.Copy(IntPtr(keyBlob.value), key, 0, generatedSize) | |
return key | |
finally: | |
# release the unmanaged memory the key resides in | |
if keyBlob.value != 0: | |
mscoree.StrongNameFreeBuffer(keyBlob) | |
# write the key to the specified file if it is not already present | |
keypath = sys.argv[1] | |
if not (File.Exists(keypath)): | |
print 'Generating key at %s' % (keypath) | |
key = GenerateKeyPair (1) | |
# should do this using a 'with' | |
snkStream = FileStream(keypath, FileMode.Create, FileAccess.Write) | |
snkWriter = BinaryWriter(snkStream) | |
snkWriter.Write(key) | |
snkWriter.Close() |
where it was easier to fudge the pointer value for the key buffer into an IntPtr
than to try and dereference it via ctypes
and make a manual copy.
The code here is an adapted subset of a managed API for strong-name keys; the use-case being a contingent key generation as part of a build process.
No comments :
Post a Comment