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)