X509Certificate2.Import with NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG - c#

I have a PFX certificate with CNG key inside (KSP provider information is specified in the PFX). I can't find a way to import the certificate in .NET in the way that would allow private key export in plain text (MS-CAPI format).
var cert = new X509Certificate2(pfxBytes,password,X509KeyStorageFlags.Exportable);
then I use this handle to acquire private key context by calling CryptAcquireCertificatePrivateKey function with enabled flag to allow CNG keys. The call succeeds.
When I call NCryptExportKey, the call fails with 0x8009000b error:
Key not valid for use in specified state.
To debug this, I called NCryptGetProperty function to get export policy and indeed, NCRYPT_ALLOW_EXPORT_FLAG flag is enabled, but NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG is not. Tried to call the NCryptSetProperty function to enable this flag in Export Policy property, but the call fails with the same 0x8009000b error.
The question: how do I import pfx file in .NET from file without persisting the key and enabled NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG flag for CNG keys?
p.s. for some reasons I'm limited to .NET 4.0/4.5.

The best flow I've found:
Open the PFX Exportable (sets the exportable bit, but not the plaintext exportable bit)
Export encrypted PKCS#8
Import encrypted PKCS#8 with overwrite, no finalize
Change the export policy
Finalize (to commit the overwrite)
Now if you ask the cert for its key it's plaintext exportable.
In net45 this takes a lot of code (thankfully Export private key (PKCS#8) of CNG RSA certificate with oldschool .NET did a lot of the work for me). netcoreapp30 would do better, except that the import+alter+finalize would still need to be manual P/Invokes.
Tested with ECDsa, since that was the easiest way to force the CNG->CAPI bridge to not be utilized:
internal static partial class Program
{
internal static void Main(string[] args)
{
X509Certificate2 cert = ImportExportable(ECDsaP256_DigitalSignature_Pfx_Windows, "Test", machineScope: false);
try
{
bool gotKey = NativeMethods.Crypt32.CryptAcquireCertificatePrivateKey(
cert.Handle,
NativeMethods.Crypt32.AcquireCertificateKeyOptions.CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG,
IntPtr.Zero,
out SafeNCryptKeyHandle keyHandle,
out int keySpec,
out bool callerFree);
using (CngKey cngKey = CngKey.Open(keyHandle, 0))
{
Console.WriteLine(cngKey.ExportPolicy);
Console.WriteLine(
Convert.ToBase64String(
cngKey.Export(CngKeyBlobFormat.Pkcs8PrivateBlob)));
}
}
finally
{
cert.Reset();
}
}
private static X509Certificate2 ImportExportable(byte[] pfxBytes, string password, bool machineScope)
{
X509KeyStorageFlags flags = X509KeyStorageFlags.Exportable;
if (machineScope)
{
flags |= X509KeyStorageFlags.MachineKeySet;
}
else
{
flags |= X509KeyStorageFlags.UserKeySet;
}
X509Certificate2 cert = new X509Certificate2(pfxBytes, password, flags);
try
{
bool gotKey = NativeMethods.Crypt32.CryptAcquireCertificatePrivateKey(
cert.Handle,
NativeMethods.Crypt32.AcquireCertificateKeyOptions.CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG,
IntPtr.Zero,
out SafeNCryptKeyHandle keyHandle,
out int keySpec,
out bool callerFree);
if (!gotKey)
{
keyHandle.Dispose();
throw new InvalidOperationException("No private key");
}
if (!callerFree)
{
keyHandle.SetHandleAsInvalid();
keyHandle.Dispose();
throw new InvalidOperationException("Key is not persisted");
}
using (keyHandle)
{
// -1 == CNG, otherwise CAPI
if (keySpec == -1)
{
using (CngKey cngKey = CngKey.Open(keyHandle, CngKeyHandleOpenOptions.None))
{
// If the CNG->CAPI bridge opened the key then AllowPlaintextExport is already set.
if ((cngKey.ExportPolicy & CngExportPolicies.AllowPlaintextExport) == 0)
{
FixExportability(cngKey, machineScope);
}
}
}
}
}
catch
{
cert.Reset();
throw;
}
return cert;
}
internal static void FixExportability(CngKey cngKey, bool machineScope)
{
string password = nameof(NativeMethods.Crypt32.AcquireCertificateKeyOptions);
byte[] encryptedPkcs8 = ExportEncryptedPkcs8(cngKey, password, 1);
string keyName = cngKey.KeyName;
using (SafeNCryptProviderHandle provHandle = cngKey.ProviderHandle)
{
ImportEncryptedPkcs8Overwrite(
encryptedPkcs8,
keyName,
provHandle,
machineScope,
password);
}
}
internal const string NCRYPT_PKCS8_PRIVATE_KEY_BLOB = "PKCS8_PRIVATEKEY";
private static readonly byte[] s_pkcs12TripleDesOidBytes =
System.Text.Encoding.ASCII.GetBytes("1.2.840.113549.1.12.1.3\0");
private static unsafe byte[] ExportEncryptedPkcs8(
CngKey cngKey,
string password,
int kdfCount)
{
var pbeParams = new NativeMethods.NCrypt.PbeParams();
NativeMethods.NCrypt.PbeParams* pbeParamsPtr = &pbeParams;
byte[] salt = new byte[NativeMethods.NCrypt.PbeParams.RgbSaltSize];
using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
{
rng.GetBytes(salt);
}
pbeParams.Params.cbSalt = salt.Length;
Marshal.Copy(salt, 0, (IntPtr)pbeParams.rgbSalt, salt.Length);
pbeParams.Params.iIterations = kdfCount;
fixed (char* stringPtr = password)
fixed (byte* oidPtr = s_pkcs12TripleDesOidBytes)
{
NativeMethods.NCrypt.NCryptBuffer* buffers =
stackalloc NativeMethods.NCrypt.NCryptBuffer[3];
buffers[0] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsSecret,
cbBuffer = checked(2 * (password.Length + 1)),
pvBuffer = (IntPtr)stringPtr,
};
if (buffers[0].pvBuffer == IntPtr.Zero)
{
buffers[0].cbBuffer = 0;
}
buffers[1] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsAlgOid,
cbBuffer = s_pkcs12TripleDesOidBytes.Length,
pvBuffer = (IntPtr)oidPtr,
};
buffers[2] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsAlgParam,
cbBuffer = sizeof(NativeMethods.NCrypt.PbeParams),
pvBuffer = (IntPtr)pbeParamsPtr,
};
var desc = new NativeMethods.NCrypt.NCryptBufferDesc
{
cBuffers = 3,
pBuffers = (IntPtr)buffers,
ulVersion = 0,
};
using (var keyHandle = cngKey.Handle)
{
int result = NativeMethods.NCrypt.NCryptExportKey(
keyHandle,
IntPtr.Zero,
NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
ref desc,
null,
0,
out int bytesNeeded,
0);
if (result != 0)
{
throw new Win32Exception(result);
}
byte[] exported = new byte[bytesNeeded];
result = NativeMethods.NCrypt.NCryptExportKey(
keyHandle,
IntPtr.Zero,
NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
ref desc,
exported,
exported.Length,
out bytesNeeded,
0);
if (result != 0)
{
throw new Win32Exception(result);
}
if (bytesNeeded != exported.Length)
{
Array.Resize(ref exported, bytesNeeded);
}
return exported;
}
}
}
private static unsafe void ImportEncryptedPkcs8Overwrite(
byte[] encryptedPkcs8,
string keyName,
SafeNCryptProviderHandle provHandle,
bool machineScope,
string password)
{
SafeNCryptKeyHandle keyHandle;
fixed (char* passwordPtr = password)
fixed (char* keyNamePtr = keyName)
fixed (byte* blobPtr = encryptedPkcs8)
{
NativeMethods.NCrypt.NCryptBuffer* buffers = stackalloc NativeMethods.NCrypt.NCryptBuffer[2];
buffers[0] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsSecret,
cbBuffer = checked(2 * (password.Length + 1)),
pvBuffer = new IntPtr(passwordPtr),
};
if (buffers[0].pvBuffer == IntPtr.Zero)
{
buffers[0].cbBuffer = 0;
}
buffers[1] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsName,
cbBuffer = checked(2 * (keyName.Length + 1)),
pvBuffer = new IntPtr(keyNamePtr),
};
NativeMethods.NCrypt.NCryptBufferDesc desc = new NativeMethods.NCrypt.NCryptBufferDesc
{
cBuffers = 2,
pBuffers = (IntPtr)buffers,
ulVersion = 0,
};
NativeMethods.NCrypt.NCryptImportFlags flags =
NativeMethods.NCrypt.NCryptImportFlags.NCRYPT_OVERWRITE_KEY_FLAG |
NativeMethods.NCrypt.NCryptImportFlags.NCRYPT_DO_NOT_FINALIZE_FLAG;
if (machineScope)
{
flags |= NativeMethods.NCrypt.NCryptImportFlags.NCRYPT_MACHINE_KEY_FLAG;
}
int errorCode = NativeMethods.NCrypt.NCryptImportKey(
provHandle,
IntPtr.Zero,
NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
ref desc,
out keyHandle,
new IntPtr(blobPtr),
encryptedPkcs8.Length,
flags);
if (errorCode != 0)
{
keyHandle.Dispose();
throw new Win32Exception(errorCode);
}
using (keyHandle)
using (CngKey cngKey = CngKey.Open(keyHandle, CngKeyHandleOpenOptions.None))
{
const CngExportPolicies desiredPolicies =
CngExportPolicies.AllowExport | CngExportPolicies.AllowPlaintextExport;
cngKey.SetProperty(
new CngProperty(
"Export Policy",
BitConverter.GetBytes((int)desiredPolicies),
CngPropertyOptions.Persist));
int error = NativeMethods.NCrypt.NCryptFinalizeKey(keyHandle, 0);
if (error != 0)
{
throw new Win32Exception(error);
}
}
}
}
}
internal static class NativeMethods
{
internal static class Crypt32
{
internal enum AcquireCertificateKeyOptions
{
None = 0x00000000,
CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG = 0x00040000,
}
[DllImport("crypt32.dll", SetLastError = true)]
internal static extern bool CryptAcquireCertificatePrivateKey(
IntPtr pCert,
AcquireCertificateKeyOptions dwFlags,
IntPtr pvReserved,
out SafeNCryptKeyHandle phCryptProvOrNCryptKey,
out int dwKeySpec,
out bool pfCallerFreeProvOrNCryptKey);
}
internal static class NCrypt
{
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern int NCryptExportKey(
SafeNCryptKeyHandle hKey,
IntPtr hExportKey,
string pszBlobType,
ref NCryptBufferDesc pParameterList,
byte[] pbOutput,
int cbOutput,
[Out] out int pcbResult,
int dwFlags);
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct PbeParams
{
internal const int RgbSaltSize = 8;
internal CryptPkcs12PbeParams Params;
internal fixed byte rgbSalt[RgbSaltSize];
}
[StructLayout(LayoutKind.Sequential)]
internal struct CryptPkcs12PbeParams
{
internal int iIterations;
internal int cbSalt;
}
[StructLayout(LayoutKind.Sequential)]
internal struct NCryptBufferDesc
{
public int ulVersion;
public int cBuffers;
public IntPtr pBuffers;
}
[StructLayout(LayoutKind.Sequential)]
internal struct NCryptBuffer
{
public int cbBuffer;
public BufferType BufferType;
public IntPtr pvBuffer;
}
internal enum BufferType
{
PkcsAlgOid = 41,
PkcsAlgParam = 42,
PkcsName = 45,
PkcsSecret = 46,
}
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern int NCryptOpenStorageProvider(
out SafeNCryptProviderHandle phProvider,
string pszProviderName,
int dwFlags);
internal enum NCryptImportFlags
{
None = 0,
NCRYPT_MACHINE_KEY_FLAG = 0x00000020,
NCRYPT_OVERWRITE_KEY_FLAG = 0x00000080,
NCRYPT_DO_NOT_FINALIZE_FLAG = 0x00000400,
}
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern int NCryptImportKey(
SafeNCryptProviderHandle hProvider,
IntPtr hImportKey,
string pszBlobType,
ref NCryptBufferDesc pParameterList,
out SafeNCryptKeyHandle phKey,
IntPtr pbData,
int cbData,
NCryptImportFlags dwFlags);
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern int NCryptFinalizeKey(SafeNCryptKeyHandle hKey, int dwFlags);
}
}

Related

ReadProcessMemory Read string until NULL

I'm trying to read a string "845120" from a process memory but I have some trouble...
I know "845120" is a numeric value, but in some cases it can be alphanumeric, that's why it's a string and not a 4 byte int.
Here is my Memory class, where I have all functions that deal with memory:
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out IntPtr lpNumberOfBytesRead);
public static IntPtr FindDMAAddy(IntPtr hProc, IntPtr ptr, int[] offsets)
{
var buffer = new byte[IntPtr.Size];
foreach (int i in offsets)
{
ReadProcessMemory(hProc, ptr, buffer, buffer.Length, out var read);
ptr = (IntPtr.Size == 4)
? IntPtr.Add(new IntPtr(BitConverter.ToInt32(buffer, 0)), i)
: ptr = IntPtr.Add(new IntPtr(BitConverter.ToInt64(buffer, 0)), i);
}
return ptr;
}
public static IntPtr GetModuleBaseAddress(Process proc, string modName)
{
IntPtr addr = IntPtr.Zero;
foreach (ProcessModule m in proc.Modules)
{
if (m.ModuleName == modName)
{
addr = m.BaseAddress;
break;
}
}
return addr;
}
public static string ReadStringUntilNULL(string EXENAME, int Address)
{
string value = "";
bool endOfString = false;
int counter = 0;
while (!endOfString)
{
if (ReadInt8(EXENAME, Address + counter) > (byte)0)
{
value += (char)ReadInt8(EXENAME, Address + counter);
}
else
{
return value;
}
counter++;
}
return value;
}
And here's the code that I'm using to invoke that functions:
Process process = null;
while(process == null)
{
process = Process.GetProcessesByName("client_dx").FirstOrDefault();
}
var hProc = Memory.OpenProcess(0x00000010, false, process.Id);
var modBase = Memory.GetModuleBaseAddress(process, "client_dx.exe");
var addr = Memory.FindDMAAddy(hProc, (IntPtr)(modBase + 0x003393AC), new int[] { 0x30, 0x374, 0x2C, 0x0, 0x14, 0x48, 0x10 });
var acc = Memory.ReadStringUntilNULL("client_dx.exe", addr);
Debug.WriteLine(acc);
It's working perfectly until this line:
var acc = Memory.ReadStringUntilNULL("client_dx.exe", addr);
So var addr have the correct address but var acc it's not getting the expected results.
Here I'm getting this error: cannot convert from 'System.IntPtr' to 'int'
Ok, so it expects an integer where I'm giving a pointer... so I tested with ToInt32()
var acc = Memory.ReadStringUntilNULL("client_dx.exe", addr.ToInt32());
The addr.ToInt32() operation returns 262959880 and as far as I know that's not even an address
I'm getting an empty string, the ReadStringUntilNULL function from Memory class it's only looping once..
Values are: addr 0x0fac7308 System.IntPtr and acc "" string
How can I read that string from memory? Or how can I pass the parameter correctly?
I finally wrote a class that lets me read strings until null:
public class NewMem
{
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(int hProcess, int lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out IntPtr lpNumberOfBytesRead);
public Process Process { get; set; }
public static IntPtr GetModuleBaseAddress(Process proc, string modName)
{
IntPtr addr = IntPtr.Zero;
foreach (ProcessModule m in proc.Modules)
{
if (m.ModuleName == modName)
{
addr = m.BaseAddress;
break;
}
}
return addr;
}
public static IntPtr FindDMAAddy(IntPtr hProc, IntPtr ptr, int[] offsets)
{
var buffer = new byte[IntPtr.Size];
foreach (int i in offsets)
{
ReadProcessMemory(hProc, ptr, buffer, buffer.Length, out var read);
ptr = (IntPtr.Size == 4)
? IntPtr.Add(new IntPtr(BitConverter.ToInt32(buffer, 0)), i)
: ptr = IntPtr.Add(new IntPtr(BitConverter.ToInt64(buffer, 0)), i);
}
return ptr;
}
public string ReadStringASCII(IntPtr address)
{
var myString = "";
for (int i = 1; i < 50; i++)
{
var bytes = ReadMemory(address, i);
if (bytes[(i-1)] == 0)
{
return myString;
}
myString = Encoding.ASCII.GetString(bytes);
}
return myString;
}
public byte[] ReadMemory(IntPtr address, int size)
{
var buffer = new byte[size];
var bytesRead = 0;
ReadProcessMemory((int)Process.Handle, (int)address, buffer, buffer.Length, ref bytesRead);
return buffer;
}
}
This is my code:
NewMem MClass = new NewMem();
var client = Process.GetProcessesByName("client_dx").FirstOrDefault();
MClass.Process = client;
// Get handle to process
var hProc = NewMem.OpenProcess(0x00000010, false, client.Id);
// Get base module
var modBase = NewMem.GetModuleBaseAddress(client, "client_dx.exe");
// Get relative base address
var vBasePointer = NewMem.FindDMAAddy(hProc, (IntPtr)(modBase + 0x55F870), new int[] { 0 });
// Get string
if (vBasePointer != IntPtr.Zero)
{
var vNameAddress = vBasePointer + 0x20;
var vName = MClass.ReadStringASCII(vNameAddress);
}
It's stopping reading when finds a '0', but you can always set up some exceptions or tricks, I didn't find a cleaner way to do this but it's working :)

Cannot re-import private key with NCryptExportKey and NCryptImportKey

I'm trying to reload a certificate private key with different export policies to fix this issue. I reused the code from this answer to export the private key, and then import it and set the export policy to AllowPlainTextExport. With that I should be able to reconstruct the original certificate with reimported private key and export its parameters if necessary. Here is the code that I have now:
using Microsoft.Win32.SafeHandles;
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace TestRsaCngExportImport
{
class Program
{
internal const string NcryptPkcs8PrivateKeyBlob = "PKCS8_PRIVATEKEY";
private const int NcryptDoNotFinalizeFlag = 0x00000400;
public const string MicrosoftSoftwareKeyStorageProvider = "Microsoft Software Key Storage Provider";
private static readonly byte[] pkcs12TripleDesOidBytes = Encoding.ASCII.GetBytes("1.2.840.113549.1.12.1.3\0");
static void Main(string[] args)
{
var certificate = CreateCertificate();
FixPrivateKey(certificate);
}
public static void FixPrivateKey(X509Certificate2 certificate)
{
var cngKey = (RSACng)RSACertificateExtensions.GetRSAPrivateKey(certificate);
var exported = ExportPkcs8KeyBlob(cngKey.Key.Handle, "", 1);
var importedKeyName = ImportPkcs8KeyBlob(exported, "", 1);
// Attempt #1
CspParameters parameters = new CspParameters();
parameters.KeyContainerName = importedKeyName;
var rsaKey = new RSACryptoServiceProvider(parameters);
certificate.PrivateKey = rsaKey; // public key doesn't match the private key
// Attempt #2
var rsaCngKey = new RSACng(CngKey.Open(importedKeyName));
certificate.PrivateKey = rsaCngKey; // Only asymmetric keys that implement ICspAsymmetricAlgorithm are supported.
// Attempt #3
certificate.PrivateKey = null;
X509Certificate2 certWithKey = certificate.CopyWithPrivateKey(rsaKey); // The provided key does not match the public key for this certificate.
}
private static X509Certificate2 CreateCertificate()
{
var keyParams = new CngKeyCreationParameters();
keyParams.KeyUsage = CngKeyUsages.Signing;
keyParams.Provider = CngProvider.MicrosoftSoftwareKeyStorageProvider;
keyParams.ExportPolicy = CngExportPolicies.AllowExport; // here I don't have AllowPlaintextExport
keyParams.Parameters.Add(new CngProperty("Length", BitConverter.GetBytes(2048), CngPropertyOptions.None));
var cngKey = CngKey.Create(CngAlgorithm.Rsa, Guid.NewGuid().ToString(), keyParams);
var rsaKey = new RSACng(cngKey);
var req = new CertificateRequest("cn=mah_cert", rsaKey, HashAlgorithmName.SHA256, RSASignaturePadding.Pss); // requires .net 4.7.2
var cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(5));
return cert;
}
private unsafe static string ImportPkcs8KeyBlob(byte[] exported, string password, int kdfCount)
{
var pbeParams = new NativeMethods.NCrypt.PbeParams();
var pbeParamsPtr = &pbeParams;
var salt = new byte[NativeMethods.NCrypt.PbeParams.RgbSaltSize];
using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
rng.GetBytes(salt);
pbeParams.Params.cbSalt = salt.Length;
Marshal.Copy(salt, 0, (IntPtr)pbeParams.rgbSalt, salt.Length);
pbeParams.Params.iIterations = kdfCount;
var keyName = Guid.NewGuid().ToString("D");
fixed (char* passwordPtr = password)
fixed (char* keyNamePtr = keyName)
fixed (byte* oidPtr = pkcs12TripleDesOidBytes)
{
NativeMethods.NCrypt.NCryptOpenStorageProvider(out var safeNCryptProviderHandle, MicrosoftSoftwareKeyStorageProvider, 0);
NativeMethods.NCrypt.NCryptBuffer* buffers = stackalloc NativeMethods.NCrypt.NCryptBuffer[4];
buffers[0] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsSecret,
cbBuffer = checked(2 * (password.Length + 1)),
pvBuffer = (IntPtr)passwordPtr,
};
if (buffers[0].pvBuffer == IntPtr.Zero)
{
buffers[0].cbBuffer = 0;
}
buffers[1] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsAlgOid,
cbBuffer = pkcs12TripleDesOidBytes.Length,
pvBuffer = (IntPtr)oidPtr,
};
buffers[2] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsAlgParam,
cbBuffer = sizeof(NativeMethods.NCrypt.PbeParams),
pvBuffer = (IntPtr)pbeParamsPtr,
};
buffers[3] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsKeyName,
cbBuffer = checked(2 * (keyName.Length + 1)),
pvBuffer = (IntPtr)keyNamePtr,
};
var desc2 = new NativeMethods.NCrypt.NCryptBufferDesc
{
cBuffers = 4,
pBuffers = (IntPtr)buffers,
ulVersion = 0,
};
var result = NativeMethods.NCrypt.NCryptImportKey(safeNCryptProviderHandle, IntPtr.Zero, NcryptPkcs8PrivateKeyBlob, ref desc2, out var safeNCryptKeyHandle, exported, exported.Length, NcryptDoNotFinalizeFlag);
if (result != 0)
throw new Win32Exception(result);
var exportPolicyBytes = BitConverter.GetBytes(
(int)(CngExportPolicies.AllowExport |
CngExportPolicies.AllowPlaintextExport |
CngExportPolicies.AllowArchiving |
CngExportPolicies.AllowPlaintextArchiving));
NativeMethods.NCrypt.NCryptSetProperty(safeNCryptKeyHandle, "Export Policy", exportPolicyBytes, exportPolicyBytes.Length, CngPropertyOptions.Persist);
NativeMethods.NCrypt.NCryptFinalizeKey(safeNCryptKeyHandle, 0);
return keyName;
}
}
private static unsafe byte[] ExportPkcs8KeyBlob(SafeNCryptKeyHandle keyHandle, string password, int kdfCount)
{
var pbeParams = new NativeMethods.NCrypt.PbeParams();
var pbeParamsPtr = &pbeParams;
var salt = new byte[NativeMethods.NCrypt.PbeParams.RgbSaltSize];
using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
rng.GetBytes(salt);
pbeParams.Params.cbSalt = salt.Length;
Marshal.Copy(salt, 0, (IntPtr)pbeParams.rgbSalt, salt.Length);
pbeParams.Params.iIterations = kdfCount;
fixed (char* stringPtr = password)
fixed (byte* oidPtr = pkcs12TripleDesOidBytes)
{
NativeMethods.NCrypt.NCryptBuffer* buffers =
stackalloc NativeMethods.NCrypt.NCryptBuffer[3];
buffers[0] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsSecret,
cbBuffer = checked(2 * (password.Length + 1)),
pvBuffer = (IntPtr)stringPtr,
};
if (buffers[0].pvBuffer == IntPtr.Zero)
{
buffers[0].cbBuffer = 0;
}
buffers[1] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsAlgOid,
cbBuffer = pkcs12TripleDesOidBytes.Length,
pvBuffer = (IntPtr)oidPtr,
};
buffers[2] = new NativeMethods.NCrypt.NCryptBuffer
{
BufferType = NativeMethods.NCrypt.BufferType.PkcsAlgParam,
cbBuffer = sizeof(NativeMethods.NCrypt.PbeParams),
pvBuffer = (IntPtr)pbeParamsPtr,
};
var desc = new NativeMethods.NCrypt.NCryptBufferDesc
{
cBuffers = 3,
pBuffers = (IntPtr)buffers,
ulVersion = 0,
};
int result = NativeMethods.NCrypt.NCryptExportKey(keyHandle, IntPtr.Zero, NcryptPkcs8PrivateKeyBlob, ref desc, null, 0, out int bytesNeeded, 0);
if (result != 0)
throw new Win32Exception(result);
byte[] exported = new byte[bytesNeeded];
result = NativeMethods.NCrypt.NCryptExportKey(keyHandle, IntPtr.Zero, NcryptPkcs8PrivateKeyBlob, ref desc, exported, exported.Length, out bytesNeeded, 0);
if (result != 0)
throw new Win32Exception(result);
if (bytesNeeded != exported.Length)
Array.Resize(ref exported, bytesNeeded);
return exported;
}
}
private static class NativeMethods
{
internal static class NCrypt
{
public const string NCryptLibraryName = "ncrypt.dll";
[DllImport(NCryptLibraryName, CharSet = CharSet.Unicode)]
internal static extern int NCryptCreatePersistedKey(SafeNCryptProviderHandle hProvider, [Out] out SafeNCryptKeyHandle phKey, string pszAlgId, string pszKeyName, int dwLegacyKeySpec, CngKeyCreationOptions dwFlags);
[DllImport(NCryptLibraryName, CharSet = CharSet.Unicode)]
internal static extern int NCryptOpenStorageProvider([Out] out SafeNCryptProviderHandle phProvider, [MarshalAs(UnmanagedType.LPWStr)] string pszProviderName, int dwFlags);
[DllImport(NCryptLibraryName, CharSet = CharSet.Unicode)]
internal static extern int NCryptExportKey(SafeNCryptKeyHandle hKey, IntPtr hExportKey, string pszBlobType, ref NCryptBufferDesc pParameterList, byte[] pbOutput, int cbOutput, [Out] out int pcbResult, int dwFlags);
[DllImport(NCryptLibraryName, CharSet = CharSet.Unicode)]
internal static extern int NCryptImportKey(SafeNCryptProviderHandle hProvider, IntPtr hImportKey, string pszBlobType, ref NCryptBufferDesc pParameterList, [Out] out SafeNCryptKeyHandle phKey, [MarshalAs(UnmanagedType.LPArray)] byte[] pbData, int cbData, int dwFlags);
[DllImport(NCryptLibraryName, CharSet = CharSet.Unicode)]
internal static extern int NCryptSetProperty(SafeNCryptHandle hObject, string pszProperty, [MarshalAs(UnmanagedType.LPArray)] byte[] pbInput, int cbInput, CngPropertyOptions dwFlags);
[DllImport(NCryptLibraryName, CharSet = CharSet.Unicode)]
internal static extern int NCryptSetProperty(SafeNCryptHandle hObject, string pszProperty, string pbInput, int cbInput, CngPropertyOptions dwFlags);
[DllImport(NCryptLibraryName, CharSet = CharSet.Unicode)]
internal static extern int NCryptSetProperty(SafeNCryptHandle hObject, string pszProperty, IntPtr pbInput, int cbInput, CngPropertyOptions dwFlags);
[DllImport(NCryptLibraryName, CharSet = CharSet.Unicode)]
internal static extern int NCryptFinalizeKey(SafeNCryptKeyHandle hKey, int dwFlags);
[DllImport(NCryptLibraryName, CharSet = CharSet.Unicode)]
internal static extern int NCryptExportKey(SafeNCryptKeyHandle hKey, IntPtr hExportKey, string pszBlobType, IntPtr pParameterList, byte[] pbOutput, int cbOutput, [Out] out int pcbResult, int dwFlags);
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct PbeParams
{
internal const int RgbSaltSize = 8;
internal CryptPkcs12PbeParams Params;
internal fixed byte rgbSalt[RgbSaltSize];
}
[StructLayout(LayoutKind.Sequential)]
internal struct CryptPkcs12PbeParams
{
internal int iIterations;
internal int cbSalt;
}
[StructLayout(LayoutKind.Sequential)]
internal struct NCryptBufferDesc
{
public int ulVersion;
public int cBuffers;
public IntPtr pBuffers;
}
[StructLayout(LayoutKind.Sequential)]
internal struct NCryptBuffer
{
public int cbBuffer;
public BufferType BufferType;
public IntPtr pvBuffer;
}
internal enum BufferType
{
PkcsAlgOid = 41,
PkcsAlgParam = 42,
PkcsAlgId = 43,
PkcsKeyName = 45,
PkcsSecret = 46,
}
}
}
}
}
The certificate gets exported and then imported. However, the imported private key cannot be reassigned to the original certificate. I'm getting either "The provided key does not match the public key for this certificate" or "Only asymmetric keys that implement ICspAsymmetricAlgorithm are supported". Is there anything I'm doing wrong?
// Attempt #1
CspParameters parameters = new CspParameters();
parameters.KeyContainerName = importedKeyName;
var rsaKey = new RSACryptoServiceProvider(parameters);
certificate.PrivateKey = rsaKey; // public key doesn't match the private key
CAPI (the library behind CspParameters) can't understand keys in CNG at all on Windows 7 or 8.1; it (theoretically) has support for it on 10, but you definitely have to tell it that the key lives in CNG (CspParameters.ProviderName).
The code here made a new CAPI key in "Microsoft RSA and AES Enhanced Cryptographic Service Provider" with ProviderType 24 that just happened to have the same local key name as your CNG key.
You didn't specify the flag UseExistingOnly, and the key didn't exist, so it made a new one... and that's why the public key didn't match what's in the certificate.
// Attempt #2
var rsaCngKey = new RSACng(CngKey.Open(importedKeyName));
certificate.PrivateKey = rsaCngKey; // Only asymmetric keys that implement ICspAsymmetricAlgorithm are supported.
The PrivateKey property only supports CAPI, either in get or set. The set is really dangerous to ever use, because it doesn't modify the cert object, it modifies the state of the cert in the Windows Certificate Store system... which means it also affects any other now or future objects operating on the same (Windows) cert.
// Attempt #3
certificate.PrivateKey = null;
X509Certificate2 certWithKey = certificate.CopyWithPrivateKey(rsaKey); // The provided key does not match the public key for this certificate.
This is the same new random key created from Attempt 1.
If you remove Attempt 1, then merge 2 and 3, you should end up with
var rsaCngKey = new RSACng(CngKey.Open(importedKeyName));
X509Certificate2 certWithKey = certificate.CopyWithPrivateKey(rsaCngKey);
And that should work. (If you've already imported the cert into the cert store, you can just add certWithKey into the cert store, which will have the same "everyone suddenly knows about this" updating change as cert.set_PrivateKey, except it's way more obvious that you asked the cert store to take a change)

RegEnumKeyEx - Access violation writing location

The situation:
Need to fetch a list of all subkeys of a particular registry key.
Need to access both 32bit and 64bit software keys, so I cannot use the Registry namespace.
Using CSharp in .Net 3.5, and registry functionality from advapi32.dll
I have most of the functionality working but I'm stuck on an error. When it reaches a key that contains values, it will either skip it or throw the following error:
"Unhandled exception at 0x00C819CD in xxxxx.exe: 0xC0000005: Access violation writing location 0x00720077."
If the error occurs, it does not land in either of my catch statements. It hard crashes the program. From what I've read on the forums, I believe it may be an issue with it writing to protected memory but all of the examples I see are for C++
My Declaration (from P/Invoke Interop Assistant):
[DllImportAttribute("advapi32.dll", EntryPoint = "RegEnumKeyExW")]
public static extern int RegEnumKeyExW(
[InAttribute()] IntPtr hKey,
uint dwIndex,
[OutAttribute()] [MarshalAsAttribute(UnmanagedType.LPWStr)] StringBuilder lpName,
ref uint lpcchName,
IntPtr lpReserved,
IntPtr lpClass,
IntPtr lpcchClass,
IntPtr lpftLastWriteTime);
My Function (obviously a work in progress so it's a bit messy):
static private List<string> GetSubKeys(UIntPtr inHive, String inKeyName, RegSAM in32or64key) {
int hkey = 0;
uint dwIndex = 0;
long enumStatus = 0;
List<string> keys = new List<string>();
try {
uint lResult = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
inKeyName,
0,
(int)RegSAM.QueryValue | (int)RegSAM.EnumerateSubKeys | (int)in32or64key,
out hkey);
if (lResult == 0) {
while (enumStatus == ERROR_SUCCESS) {
StringBuilder lpName = new StringBuilder();
uint lpcchName = 256;
IntPtr lpReserved = IntPtr.Zero;
IntPtr lpClass = IntPtr.Zero;
IntPtr lpcchClass = IntPtr.Zero;
IntPtr lpftLastWriteTime = IntPtr.Zero;
enumStatus = RegEnumKeyExW(
(IntPtr)hkey,
dwIndex,
lpName,
ref lpcchName,
lpReserved,
lpClass,
lpcchClass,
lpftLastWriteTime);
switch (enumStatus) {
case ERROR_SUCCESS:
Console.WriteLine(string.Format("Key Found: {0}", lpName.ToString()));
break;
case ERROR_NO_MORE_ITEMS:
break;
default:
string error = new System.ComponentModel.Win32Exception((int)enumStatus).Message;
Console.WriteLine(string.Format("RegEnumKeyEx Error: {0}", error));
break;
}
dwIndex++;
}
} else {
Console.WriteLine(string.Format("RegOpenKey Error: {0}", lResult));
}
} catch (System.Runtime.InteropServices.COMException ex) {
Console.WriteLine(string.Format("COM Error: {0}", ex.Message));
} catch (Exception ex) {
Console.WriteLine(string.Format("Managed Error: {0}", ex.Message));
} finally {
if (0 != hkey) RegCloseKey(hkey);
}
return keys;
}
#endregion
StringBuilder lpName = new StringBuilder();
uint lpcchName = 256;
You are lying about the StringBuilder's capacity. It is 0, not 256. This will cause the pinvoke call to corrupt the GC heap. This eventually causes a hard crash, typically when a garbage collection takes place. Fix:
uint lpcchName = 256;
StringBuilder lpName = new StringBuilder(lpcchName);
Using the .NET RegistryKey.GetSubKeyNames() method instead would probably be wise.
Use the same way as. net4.0
static void Main(string[] args)
{
string displayName;
List<string> gInstalledSoftware = new List<string>();
using (var localMachine = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
{
var key = localMachine.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", false);
foreach (String keyName in key.GetSubKeyNames())
{
RegistryKey subkey = key.OpenSubKey(keyName);
displayName = subkey.GetValue("DisplayName") as string;
if (string.IsNullOrEmpty(displayName))
continue;
gInstalledSoftware.Add(displayName);
}
}
}
You can try using. net source code to solve this problem. etc.
public class RegistryKey:
IDisposable
{
internal static readonly IntPtr HKEY_CLASSES_ROOT = new IntPtr(unchecked((int)0x80000000));
internal static readonly IntPtr HKEY_CURRENT_USER = new IntPtr(unchecked((int)0x80000001));
internal static readonly IntPtr HKEY_LOCAL_MACHINE = new IntPtr(unchecked((int)0x80000002));
internal static readonly IntPtr HKEY_USERS = new IntPtr(unchecked((int)0x80000003));
internal static readonly IntPtr HKEY_PERFORMANCE_DATA = new IntPtr(unchecked((int)0x80000004));
internal static readonly IntPtr HKEY_CURRENT_CONFIG = new IntPtr(unchecked((int)0x80000005));
private static readonly String[] hkeyNames = new String[] {
"HKEY_CLASSES_ROOT",
"HKEY_CURRENT_USER",
"HKEY_LOCAL_MACHINE",
"HKEY_USERS",
"HKEY_PERFORMANCE_DATA",
"HKEY_CURRENT_CONFIG",
};
public Object GetValue(String name)
{
return InternalGetValue(name, null, false, true);
}
internal Object InternalGetValue(String name, Object defaultValue, bool doNotExpand, bool checkSecurity)
{
Object data = defaultValue;
int type = 0;
int datasize = 0;
int ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, (byte[])null, ref datasize);
if (ret != 0)
{
if (IsPerfDataKey())
{
int size = 65000;
int sizeInput = size;
int r;
byte[] blob = new byte[size];
while (Win32Native.ERROR_MORE_DATA == (r = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref sizeInput)))
{
if (size == Int32.MaxValue)
{
// ERROR_MORE_DATA was returned however we cannot increase the buffer size beyond Int32.MaxValue
//Win32Error(r, name);
Console.WriteLine(string.Format("[{0}] [{1}]", r,name));
}
else if (size > (Int32.MaxValue / 2))
{
// at this point in the loop "size * 2" would cause an overflow
size = Int32.MaxValue;
}
else
{
size *= 2;
}
sizeInput = size;
blob = new byte[size];
}
if (r != 0)
Console.WriteLine(string.Format("[{0}] [{1}]", r, name));
return blob;
}
else
{
// For stuff like ERROR_FILE_NOT_FOUND, we want to return null (data).
// Some OS's returned ERROR_MORE_DATA even in success cases, so we
// want to continue on through the function.
if (ret != Win32Native.ERROR_MORE_DATA)
return data;
}
}
if (datasize < 0)
{
// unexpected code path
//BCLDebug.Assert(false, "[InternalGetValue] RegQueryValue returned ERROR_SUCCESS but gave a negative datasize");
datasize = 0;
}
switch (type)
{
case Win32Native.REG_NONE:
case Win32Native.REG_DWORD_BIG_ENDIAN:
case Win32Native.REG_BINARY:
{
byte[] blob = new byte[datasize];
ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref datasize);
data = blob;
}
break;
case Win32Native.REG_QWORD:
{ // also REG_QWORD_LITTLE_ENDIAN
if (datasize > 8)
{
// prevent an AV in the edge case that datasize is larger than sizeof(long)
goto case Win32Native.REG_BINARY;
}
long blob = 0;
//BCLDebug.Assert(datasize==8, "datasize==8");
// Here, datasize must be 8 when calling this
ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, ref blob, ref datasize);
data = blob;
}
break;
case Win32Native.REG_DWORD:
{ // also REG_DWORD_LITTLE_ENDIAN
if (datasize > 4)
{
// prevent an AV in the edge case that datasize is larger than sizeof(int)
goto case Win32Native.REG_QWORD;
}
int blob = 0;
// Here, datasize must be four when calling this
ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, ref blob, ref datasize);
data = blob;
}
break;
case Win32Native.REG_SZ:
{
if (datasize % 2 == 1)
{
// handle the case where the registry contains an odd-byte length (corrupt data?)
try
{
datasize = checked(datasize + 1);
}
catch (OverflowException e)
{
throw new IOException(("Arg_RegGetOverflowBug"), e);
}
}
char[] blob = new char[datasize / 2];
ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref datasize);
if (blob.Length > 0 && blob[blob.Length - 1] == (char)0)
{
data = new String(blob, 0, blob.Length - 1);
}
else
{
// in the very unlikely case the data is missing null termination,
// pass in the whole char[] to prevent truncating a character
data = new String(blob);
}
}
break;
case Win32Native.REG_EXPAND_SZ:
{
if (datasize % 2 == 1)
{
// handle the case where the registry contains an odd-byte length (corrupt data?)
try
{
datasize = checked(datasize + 1);
}
catch (OverflowException e)
{
throw new IOException(("Arg_RegGetOverflowBug"), e);
}
}
char[] blob = new char[datasize / 2];
ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref datasize);
if (blob.Length > 0 && blob[blob.Length - 1] == (char)0)
{
data = new String(blob, 0, blob.Length - 1);
}
else
{
// in the very unlikely case the data is missing null termination,
// pass in the whole char[] to prevent truncating a character
data = new String(blob);
}
if (!doNotExpand)
data = Environment.ExpandEnvironmentVariables((String)data);
}
break;
case Win32Native.REG_MULTI_SZ:
{
if (datasize % 2 == 1)
{
// handle the case where the registry contains an odd-byte length (corrupt data?)
try
{
datasize = checked(datasize + 1);
}
catch (OverflowException e)
{
throw new IOException(("Arg_RegGetOverflowBug"), e);
}
}
char[] blob = new char[datasize / 2];
ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref datasize);
// make sure the string is null terminated before processing the data
if (blob.Length > 0 && blob[blob.Length - 1] != (char)0)
{
try
{
char[] newBlob = new char[checked(blob.Length + 1)];
for (int i = 0; i < blob.Length; i++)
{
newBlob[i] = blob[i];
}
newBlob[newBlob.Length - 1] = (char)0;
blob = newBlob;
}
catch (OverflowException e)
{
throw new IOException(("Arg_RegGetOverflowBug"), e);
}
blob[blob.Length - 1] = (char)0;
}
IList<String> strings = new List<String>();
int cur = 0;
int len = blob.Length;
while (ret == 0 && cur < len)
{
int nextNull = cur;
while (nextNull < len && blob[nextNull] != (char)0)
{
nextNull++;
}
if (nextNull < len)
{
//BCLDebug.Assert(blob[nextNull] == (char)0, "blob[nextNull] should be 0");
if (nextNull - cur > 0)
{
strings.Add(new String(blob, cur, nextNull - cur));
}
else
{
// we found an empty string. But if we're at the end of the data,
// it's just the extra null terminator.
if (nextNull != len - 1)
strings.Add(String.Empty);
}
}
else
{
strings.Add(new String(blob, cur, len - cur));
}
cur = nextNull + 1;
}
data = new String[strings.Count];
strings.CopyTo((String[])data, 0);
}
break;
case Win32Native.REG_LINK:
default:
break;
}
return data;
}
public String[] GetSubKeyNames()
{
return InternalGetSubKeyNames();
}
public RegistryKey OpenSubKey(string name, bool writable=false)
{
name = FixupName(name); // Fixup multiple slashes to a single slash
SafeRegistryHandle result = null;
int ret = Win32Native.RegOpenKeyEx(hkey,
name,
0,
GetRegistryKeyAccess(writable) | (int)regView,
out result);
if (ret == 0 && !result.IsInvalid)
{
RegistryKey key = new RegistryKey(result, writable, false, remoteKey, false, regView);
key.checkMode = GetSubKeyPermissonCheck(writable);
key.keyName = keyName + "\\" + name;
return key;
}
// Return null if we didn't find the key.
if (ret == Win32Native.ERROR_ACCESS_DENIED || ret == Win32Native.ERROR_BAD_IMPERSONATION_LEVEL)
{
// We need to throw SecurityException here for compatibility reasons,
// although UnauthorizedAccessException will make more sense.
//ThrowHelper.ThrowSecurityException(ExceptionResource.Security_RegistryPermission);
}
return null;
}
private const int MaxKeyLength = 255;
internal unsafe String[] InternalGetSubKeyNames()
{
int subkeys = InternalSubKeyCount();
String[] names = new String[subkeys]; // Returns 0-length array if empty.
if (subkeys > 0)
{
char[] name = new char[MaxKeyLength + 1];
int namelen;
fixed (char* namePtr = &name[0])
{
for (int i = 0; i < subkeys; i++)
{
namelen = name.Length; // Don't remove this. The API's doesn't work if this is not properly initialised.
int ret = Win32Native.RegEnumKeyEx(hkey,
i,
namePtr,
ref namelen,
null,
null,
null,
null);
if (ret != 0)
//Win32Error(ret, null);
Console.WriteLine(ret);
names[i] = new String(namePtr);
}
}
}
return names;
}
internal int InternalSubKeyCount()
{
int subkeys = 0;
int junk = 0;
int ret = Win32Native.RegQueryInfoKey(hkey,
null,
null,
IntPtr.Zero,
ref subkeys, // subkeys
null,
null,
ref junk, // values
null,
null,
null,
null);
if (ret != 0)
//Win32Error(ret, null);
Console.WriteLine(ret);
return subkeys;
}
public static RegistryKey OpenBaseKey(RegistryHive hKey, RegistryView view)
{
return GetBaseKey((IntPtr)((int)hKey), view);
}
internal static RegistryKey GetBaseKey(IntPtr hKey, RegistryView view)
{
int index = ((int)hKey) & 0x0FFFFFFF;
//BCLDebug.Assert(index >= 0 && index < hkeyNames.Length, "index is out of range!");
//BCLDebug.Assert((((int)hKey) & 0xFFFFFFF0) == 0x80000000, "Invalid hkey value!");
bool isPerf = hKey == HKEY_PERFORMANCE_DATA;
// only mark the SafeHandle as ownsHandle if the key is HKEY_PERFORMANCE_DATA.
SafeRegistryHandle srh = new SafeRegistryHandle(hKey, isPerf);
RegistryKey key = new RegistryKey(srh, true, true, false, isPerf, view);
key.checkMode = RegistryKeyPermissionCheck.Default;
key.keyName = hkeyNames[index];
return key;
}
private volatile SafeRegistryHandle hkey = null;
private volatile int state = 0;
private volatile String keyName;
private volatile bool remoteKey = false;
private volatile RegistryKeyPermissionCheck checkMode;
private volatile RegistryView regView = RegistryView.Default;
private const int STATE_DIRTY = 0x0001;
// SystemKey indicates that this is a "SYSTEMKEY" and shouldn't be "opened"
// or "closed".
//
private const int STATE_SYSTEMKEY = 0x0002;
// Access
//
private const int STATE_WRITEACCESS = 0x0004;
// Indicates if this key is for HKEY_PERFORMANCE_DATA
private const int STATE_PERF_DATA = 0x0008;
private RegistryKey(SafeRegistryHandle hkey, bool writable, bool systemkey, bool remoteKey, bool isPerfData, RegistryView view)
{
this.hkey = hkey;
this.keyName = "";
this.remoteKey = remoteKey;
this.regView = view;
if (systemkey)
{
this.state |= STATE_SYSTEMKEY;
}
if (writable)
{
this.state |= STATE_WRITEACCESS;
}
if (isPerfData)
this.state |= STATE_PERF_DATA;
}
private RegistryKeyPermissionCheck GetSubKeyPermissonCheck(bool subkeyWritable)
{
if (checkMode == RegistryKeyPermissionCheck.Default)
{
return checkMode;
}
if (subkeyWritable)
{
return RegistryKeyPermissionCheck.ReadWriteSubTree;
}
else
{
return RegistryKeyPermissionCheck.ReadSubTree;
}
}
static int GetRegistryKeyAccess(bool isWritable)
{
int winAccess;
if (!isWritable)
{
winAccess = Win32Native.KEY_READ;
}
else
{
winAccess = Win32Native.KEY_READ | Win32Native.KEY_WRITE;
}
return winAccess;
}
internal static String FixupName(String name)
{
//BCLDebug.Assert(name!=null,"[FixupName]name!=null");
if (name.IndexOf('\\') == -1)
return name;
StringBuilder sb = new StringBuilder(name);
FixupPath(sb);
int temp = sb.Length - 1;
if (temp >= 0 && sb[temp] == '\\') // Remove trailing slash
sb.Length = temp;
return sb.ToString();
}
private static void FixupPath(StringBuilder path)
{
//Contract.Requires(path != null);
int length = path.Length;
bool fixup = false;
char markerChar = (char)0xFFFF;
int i = 1;
while (i < length - 1)
{
if (path[i] == '\\')
{
i++;
while (i < length)
{
if (path[i] == '\\')
{
path[i] = markerChar;
i++;
fixup = true;
}
else
break;
}
}
i++;
}
if (fixup)
{
i = 0;
int j = 0;
while (i < length)
{
if (path[i] == markerChar)
{
i++;
continue;
}
path[j] = path[i];
i++;
j++;
}
path.Length += j - i;
}
}
#region IDisposable Support
private bool disposedValue = false; // 要检测冗余调用
public void Close()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (hkey != null)
{
if (!IsSystemKey())
{
try
{
hkey.Dispose();
}
catch (IOException)
{
// we don't really care if the handle is invalid at this point
}
finally
{
hkey = null;
}
}
else if (disposing && IsPerfDataKey())
{
// System keys should never be closed. However, we want to call RegCloseKey
// on HKEY_PERFORMANCE_DATA when called from PerformanceCounter.CloseSharedResources
// (i.e. when disposing is true) so that we release the PERFLIB cache and cause it
// to be refreshed (by re-reading the registry) when accessed subsequently.
// This is the only way we can see the just installed perf counter.
// NOTE: since HKEY_PERFORMANCE_DATA is process wide, there is inherent race condition in closing
// the key asynchronously. While Vista is smart enough to rebuild the PERFLIB resources
// in this situation the down level OSes are not. We have a small window between
// the dispose below and usage elsewhere (other threads). This is By Design.
// This is less of an issue when OS > NT5 (i.e Vista & higher), we can close the perfkey
// (to release & refresh PERFLIB resources) and the OS will rebuild PERFLIB as necessary.
SafeRegistryHandle.RegCloseKey(RegistryKey.HKEY_PERFORMANCE_DATA);
}
}
}
private bool IsPerfDataKey()
{
return (this.state & STATE_PERF_DATA) != 0;
}
private bool IsSystemKey()
{
return (this.state & STATE_SYSTEMKEY) != 0;
}
public void Dispose()
{
Dispose(true);
}
#endregion
}
[System.Security.SecurityCritical]
public sealed class SafeRegistryHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[System.Security.SecurityCritical]
internal SafeRegistryHandle() : base(true) { }
[System.Security.SecurityCritical]
public SafeRegistryHandle(IntPtr preexistingHandle, bool ownsHandle) : base(ownsHandle)
{
SetHandle(preexistingHandle);
}
[System.Security.SecurityCritical]
override protected bool ReleaseHandle()
{
return (RegCloseKey(handle) == Win32Native.ERROR_SUCCESS);
}
[DllImport(Win32Native.ADVAPI32)]
internal static extern int RegCloseKey(IntPtr hKey);
}
[Serializable]
[System.Runtime.InteropServices.ComVisible(true)]
public enum RegistryHive
{
ClassesRoot = unchecked((int)0x80000000),
CurrentUser = unchecked((int)0x80000001),
LocalMachine = unchecked((int)0x80000002),
Users = unchecked((int)0x80000003),
PerformanceData = unchecked((int)0x80000004),
CurrentConfig = unchecked((int)0x80000005),
}
public enum RegistryView
{
Default = 0, // 0x0000 operate on the default registry view
Registry64 = Win32Native.KEY_WOW64_64KEY, // 0x0100 operate on the 64-bit registry view
Registry32 = Win32Native.KEY_WOW64_32KEY, // 0x0200 operate on the 32-bit registry view
};
public enum RegistryKeyPermissionCheck
{
Default = 0,
ReadSubTree = 1,
ReadWriteSubTree = 2
}
public static class Win32Native
{
internal const String ADVAPI32 = "advapi32.dll";
internal const int KEY_WOW64_64KEY = 0x0100; //
internal const int KEY_WOW64_32KEY = 0x0200; //
internal const int ERROR_SUCCESS = 0x0;
internal const int READ_CONTROL = 0x00020000;
internal const int SYNCHRONIZE = 0x00100000;
internal const int STANDARD_RIGHTS_READ = READ_CONTROL;
internal const int STANDARD_RIGHTS_WRITE = READ_CONTROL;
internal const int KEY_QUERY_VALUE = 0x0001;
internal const int KEY_SET_VALUE = 0x0002;
internal const int KEY_CREATE_SUB_KEY = 0x0004;
internal const int KEY_ENUMERATE_SUB_KEYS = 0x0008;
internal const int KEY_NOTIFY = 0x0010;
internal const int KEY_CREATE_LINK = 0x0020;
internal const int KEY_READ = ((STANDARD_RIGHTS_READ |
KEY_QUERY_VALUE |
KEY_ENUMERATE_SUB_KEYS |
KEY_NOTIFY)
&
(~SYNCHRONIZE));
internal const int KEY_WRITE = ((STANDARD_RIGHTS_WRITE |
KEY_SET_VALUE |
KEY_CREATE_SUB_KEY)
&
(~SYNCHRONIZE));
internal const int ERROR_ACCESS_DENIED = 0x5;
internal const int ERROR_BAD_IMPERSONATION_LEVEL = 0x542;
[DllImport(ADVAPI32, CharSet = CharSet.Auto, BestFitMapping = false)]
internal static extern int RegOpenKeyEx(SafeRegistryHandle hKey, String lpSubKey,
int ulOptions, int samDesired, out SafeRegistryHandle hkResult);
[DllImport(ADVAPI32, CharSet = CharSet.Auto, BestFitMapping = false)]
internal static extern int RegQueryInfoKey(SafeRegistryHandle hKey, [Out]StringBuilder lpClass,
int[] lpcbClass, IntPtr lpReserved_MustBeZero, ref int lpcSubKeys,
int[] lpcbMaxSubKeyLen, int[] lpcbMaxClassLen,
ref int lpcValues, int[] lpcbMaxValueNameLen,
int[] lpcbMaxValueLen, int[] lpcbSecurityDescriptor,
int[] lpftLastWriteTime);
[DllImport(ADVAPI32, CharSet = CharSet.Auto, BestFitMapping = false)]
internal unsafe static extern int RegEnumKeyEx(SafeRegistryHandle hKey, int dwIndex,
char* lpName, ref int lpcbName, int[] lpReserved,
[Out]StringBuilder lpClass, int[] lpcbClass,
long[] lpftLastWriteTime);
internal const int ERROR_MORE_DATA = 0xEA;
internal const int REG_NONE = 0; // No value type
internal const int REG_DWORD_BIG_ENDIAN = 5; // 32-bit number
internal const int REG_BINARY = 3; // Free form binary
internal const int REG_QWORD = 11; // 64-bit number
internal const int REG_DWORD = 4; // 32-bit number
internal const int REG_SZ = 1; // Unicode nul terminated string
internal const int REG_EXPAND_SZ = 2; // Unicode nul terminated string
internal const int REG_MULTI_SZ = 7; // Multiple Unicode strings
internal const int REG_LINK = 6; // Symbolic Link (unicode)
[DllImport(ADVAPI32, CharSet = CharSet.Auto, BestFitMapping = false)]
internal static extern int RegQueryValueEx(SafeRegistryHandle hKey, String lpValueName,
int[] lpReserved, ref int lpType, [Out] byte[] lpData,
ref int lpcbData);
[DllImport(ADVAPI32, CharSet = CharSet.Auto, BestFitMapping = false)]
internal static extern int RegQueryValueEx(SafeRegistryHandle hKey, String lpValueName,
int[] lpReserved, ref int lpType, ref int lpData,
ref int lpcbData);
[DllImport(ADVAPI32, CharSet = CharSet.Auto, BestFitMapping = false)]
internal static extern int RegQueryValueEx(SafeRegistryHandle hKey, String lpValueName,
int[] lpReserved, ref int lpType, ref long lpData,
ref int lpcbData);
[DllImport(ADVAPI32, CharSet = CharSet.Auto, BestFitMapping = false)]
internal static extern int RegQueryValueEx(SafeRegistryHandle hKey, String lpValueName,
int[] lpReserved, ref int lpType, [Out] char[] lpData,
ref int lpcbData);
[DllImport(ADVAPI32, CharSet = CharSet.Auto, BestFitMapping = false)]
internal static extern int RegQueryValueEx(SafeRegistryHandle hKey, String lpValueName,
int[] lpReserved, ref int lpType, [Out]StringBuilder lpData,
ref int lpcbData);
}

Using Username and Password with WinInet

I am applying a proxy for my application's WebBrowser control using WinInet and the good thing about that method is that it doesn't affect the system's proxy and only applies the proxy to my application. However it only works with proxies that have no username and password.
Here is the code
private const int INTERNET_OPEN_TYPE_DIRECT = 1;
private const int INTERNET_OPEN_TYPE_PRECONFIG = 0;
public static string ApplicationName;
[DllImport("user32.dll", SetLastError = true)]
public static IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static bool ShowWindow(IntPtr hwnd, User32.WindowShowStyle nCmdShow);
[DllImport("user32.dll")]
public static bool EnableWindow(IntPtr hwnd, bool enabled);
[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static IntPtr InternetOpen(string lpszAgent, int dwAccessType, string lpszProxyName, string lpszProxyBypass, int dwFlags);
[DllImport("wininet.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static bool InternetCloseHandle(IntPtr hInternet);
[DllImport("wininet.dll", CharSet = CharSet.Ansi, SetLastError = true)]
private static bool InternetSetOption(IntPtr hInternet, User32.InternetOption dwOption, IntPtr lpBuffer, int lpdwBufferLength);
[DllImport("wininet.dll", EntryPoint = "InternetQueryOption", CharSet = CharSet.Ansi, SetLastError = true)]
private static bool InternetQueryOptionList(IntPtr handle, User32.InternetOption optionFlag, ref User32.InternetPerConnOptionList optionList, ref int size);
public static bool SetConnectionProxy(string proxyServer)
{
IntPtr hInternet = User32.InternetOpen(User32.ApplicationName, 1, (string) null, (string) null, 0);
User32.InternetPerConnOption[] internetPerConnOptionArray = new User32.InternetPerConnOption[2]
{
new User32.InternetPerConnOption()
{
dwOption = 1,
Value = {
dwValue = 2
}
},
new User32.InternetPerConnOption()
{
dwOption = 2,
Value = {
pszValue = Marshal.StringToHGlobalAnsi(proxyServer)
}
}
};
IntPtr ptr1 = Marshal.AllocCoTaskMem(Marshal.SizeOf((object) internetPerConnOptionArray[0]) + Marshal.SizeOf((object) internetPerConnOptionArray[1]));
IntPtr ptr2 = ptr1;
foreach (User32.InternetPerConnOption internetPerConnOption in internetPerConnOptionArray)
{
Marshal.StructureToPtr((object) internetPerConnOption, ptr2, false);
ptr2 = (IntPtr) ((int) ptr2 + Marshal.SizeOf((object) internetPerConnOption));
}
User32.InternetPerConnOptionList perConnOptionList = new User32.InternetPerConnOptionList()
{
pOptions = ptr1
};
perConnOptionList.Size = Marshal.SizeOf((object) perConnOptionList);
perConnOptionList.Connection = IntPtr.Zero;
perConnOptionList.OptionCount = internetPerConnOptionArray.Length;
perConnOptionList.OptionError = 0;
int num1 = Marshal.SizeOf((object) perConnOptionList);
IntPtr num2 = Marshal.AllocCoTaskMem(num1);
Marshal.StructureToPtr((object) perConnOptionList, num2, true);
bool flag = User32.InternetSetOption(hInternet, User32.InternetOption.InternetOptionPerConnectionOption, num2, num1);
Marshal.FreeCoTaskMem(ptr1);
Marshal.FreeCoTaskMem(num2);
User32.InternetCloseHandle(hInternet);
if (!flag)
throw new ApplicationException(" Set Internet Option Failed!");
else
return true;
}
private static User32.InternetPerConnOptionList GetSystemProxy()
{
User32.InternetPerConnOption[] internetPerConnOptionArray = new User32.InternetPerConnOption[3]
{
new User32.InternetPerConnOption()
{
dwOption = 1
},
new User32.InternetPerConnOption()
{
dwOption = 2
},
new User32.InternetPerConnOption()
{
dwOption = 3
}
};
IntPtr num = Marshal.AllocCoTaskMem(Marshal.SizeOf((object) internetPerConnOptionArray[0]) + Marshal.SizeOf((object) internetPerConnOptionArray[1]) + Marshal.SizeOf((object) internetPerConnOptionArray[2]));
IntPtr ptr = num;
foreach (User32.InternetPerConnOption internetPerConnOption in internetPerConnOptionArray)
{
Marshal.StructureToPtr((object) internetPerConnOption, ptr, false);
ptr = (IntPtr) ((int) ptr + Marshal.SizeOf((object) internetPerConnOption));
}
User32.InternetPerConnOptionList optionList = new User32.InternetPerConnOptionList()
{
pOptions = num
};
optionList.Size = Marshal.SizeOf((object) optionList);
optionList.Connection = IntPtr.Zero;
optionList.OptionCount = internetPerConnOptionArray.Length;
optionList.OptionError = 0;
int size = Marshal.SizeOf((object) optionList);
if (!User32.InternetQueryOptionList(IntPtr.Zero, User32.InternetOption.InternetOptionPerConnectionOption, ref optionList, ref size))
throw new ApplicationException(" Set Internet Option Failed! ");
else
return optionList;
}
public static bool RestoreSystemProxy()
{
IntPtr hInternet = User32.InternetOpen(User32.ApplicationName, 1, (string) null, (string) null, 0);
User32.InternetPerConnOptionList systemProxy = User32.GetSystemProxy();
int num1 = Marshal.SizeOf((object) systemProxy);
IntPtr num2 = Marshal.AllocCoTaskMem(num1);
Marshal.StructureToPtr((object) systemProxy, num2, true);
bool flag = User32.InternetSetOption(hInternet, User32.InternetOption.InternetOptionPerConnectionOption, num2, num1);
Marshal.FreeCoTaskMem(systemProxy.pOptions);
Marshal.FreeCoTaskMem(num2);
if (!flag)
throw new ApplicationException(" Set Internet Option Failed! ");
User32.InternetSetOption(hInternet, User32.InternetOption.InternetOptionSettingsChanged, IntPtr.Zero, 0);
User32.InternetSetOption(hInternet, User32.InternetOption.InternetOptionRefresh, IntPtr.Zero, 0);
User32.InternetCloseHandle(hInternet);
return true;
}
public enum WindowShowStyle : uint
{
Hide = 0U,
ShowNormal = 1U,
ShowMinimized = 2U,
Maximize = 3U,
ShowMaximized = 3U,
ShowNormalNoActivate = 4U,
Show = 5U,
Minimize = 6U,
ShowMinNoActivate = 7U,
ShowNoActivate = 8U,
Restore = 9U,
ShowDefault = 10U,
ForceMinimized = 11U,
}
private enum InternetOption
{
InternetOptionRefresh = 37,
InternetOptionSettingsChanged = 39,
InternetOptionPerConnectionOption = 75,
}
private enum InternetOptionPerConnFlags
{
ProxyTypeDirect = 1,
ProxyTypeProxy = 2,
ProxyTypeAutoProxyUrl = 4,
ProxyTypeAutoDetect = 8,
}
private struct InternetPerConnOption
{
public int dwOption;
public User32.InternetPerConnOptionOptionUnion Value;
}
private struct InternetPerConnOptionList
{
public int Size;
public IntPtr Connection;
public int OptionCount;
public int OptionError;
public IntPtr pOptions;
}
[StructLayout(LayoutKind.Explicit)]
private struct InternetPerConnOptionOptionUnion
{
[FieldOffset(0)]
public int dwValue;
[FieldOffset(0)]
public IntPtr pszValue;
[FieldOffset(0)]
private readonly System.Runtime.InteropServices.ComTypes.FILETIME ftValue;
}
private enum InternetPerConnOptionEnum
{
InternetPerConnFlags = 1,
InternetPerConnProxyServer = 2,
InternetPerConnProxyBypass = 3,
InternetPerConnAutoconfigUrl = 4,
InternetPerConnAutodiscoveryFlags = 5,
InternetPerConnAutoconfigSecondaryUrl = 6,
InternetPerConnAutoconfigReloadDelayMins = 7,
InternetPerConnAutoconfigLastDetectTime = 8,
InternetPerConnAutoconfigLastDetectUrl = 9,
InternetPerConnFlagsUi = 10,
}
Question: How could i use that code to apply a proxy with username and password ?
Notes:
I know that there is a couple of similar questions here but i cannot
get a good answer out of them.
I heard that this can be achieved using InternetSetOption however
i can't find how to implement that cause i don't have enough
knowledge about windoes functions etc...
The proper options to set the proxy credentials in WinINET are INTERNET_OPTION_PROXY_USERNAME and INTERNET_OPTION_PROXY_PASSWORD as shown at http://msdn.microsoft.com/en-us/library/windows/desktop/aa385328(v=vs.85).aspx
You may need to implement IAuthenticate instead, since the other calls are really intended for WinINET handles, which aren't exposed when you're using the Web Browser control.

Eject USB device via C#

I was looking for a short way to eject USB-devices via C#-code, so I coded a little class myself, yet it simply doesn't work. Since there's no popup that says "Lock success!" I assume that the problem relies within the "LockVolume"-function, but I don't know where.
Does anybody see the mistake I made?
class USBEject
{
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr SecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile
);
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool DeviceIoControl(
IntPtr hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
uint nInBufferSize,
IntPtr lpOutBuffer,
uint nOutBufferSize,
out uint lpBytesReturned,
IntPtr lpOverlapped
);
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool DeviceIoControl(
IntPtr hDevice,
uint dwIoControlCode,
byte[] lpInBuffer,
uint nInBufferSize,
IntPtr lpOutBuffer,
uint nOutBufferSize,
out uint lpBytesReturned,
IntPtr lpOverlapped
);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
private IntPtr handle = IntPtr.Zero;
const int GENERIC_READ = 0x80000000;
const int GENERIC_WRITE = 0x40000000;
const int FILE_SHARE_READ = 0x1;
const int FILE_SHARE_WRITE = 0x2;
const int FSCTL_LOCK_VOLUME = 0x00090018;
const int FSCTL_DISMOUNT_VOLUME = 0x00090020;
const int IOCTL_STORAGE_EJECT_MEDIA = 0x2D4808;
const int IOCTL_STORAGE_MEDIA_REMOVAL = 0x002D4804;
/// <summary>
/// Constructor for the USBEject class
/// </summary>
/// <param name="driveLetter">This should be the drive letter. Format: F:/, C:/..</param>
public USBEject(string driveLetter)
{
string filename = #"\\.\" + driveLetter[0] + ":";
handle = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, 0x3, 0, IntPtr.Zero);
}
public bool Eject()
{
if (LockVolume(handle) && DismountVolume(handle))
{
PreventRemovalOfVolume(handle, false);
return AutoEjectVolume(handle);
}
return false;
}
private bool LockVolume(IntPtr handle)
{
uint byteReturned;
for (int i = 0; i < 10; i++)
{
if (DeviceIoControl(handle, FSCTL_LOCK_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero))
{
System.Windows.Forms.MessageBox.Show("Lock success!");
return true;
}
Thread.Sleep(500);
}
return false;
}
private bool PreventRemovalOfVolume(IntPtr handle, bool prevent)
{
byte[] buf = new byte[1];
uint retVal;
buf[0] = (prevent) ? (byte)1 : (byte)0;
return DeviceIoControl(handle, IOCTL_STORAGE_MEDIA_REMOVAL, buf, 1, IntPtr.Zero, 0, out retVal, IntPtr.Zero);
}
private bool DismountVolume(IntPtr handle)
{
uint byteReturned;
return DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero);
}
private bool AutoEjectVolume(IntPtr handle)
{
uint byteReturned;
return DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, IntPtr.Zero, 0, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero);
}
private bool CloseVolume(IntPtr handle)
{
return CloseHandle(handle);
}
}
Changed just a little bit your code and it goes as follows:
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr SecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile
);
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool DeviceIoControl(
IntPtr hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
uint nInBufferSize,
IntPtr lpOutBuffer,
uint nOutBufferSize,
out uint lpBytesReturned,
IntPtr lpOverlapped
);
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool DeviceIoControl(
IntPtr hDevice,
uint dwIoControlCode,
byte[] lpInBuffer,
uint nInBufferSize,
IntPtr lpOutBuffer,
uint nOutBufferSize,
out uint lpBytesReturned,
IntPtr lpOverlapped
);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
private IntPtr handle = IntPtr.Zero;
const uint GENERIC_READ = 0x80000000;
const uint GENERIC_WRITE = 0x40000000;
const int FILE_SHARE_READ = 0x1;
const int FILE_SHARE_WRITE = 0x2;
const int FSCTL_LOCK_VOLUME = 0x00090018;
const int FSCTL_DISMOUNT_VOLUME = 0x00090020;
const int IOCTL_STORAGE_EJECT_MEDIA = 0x2D4808;
const int IOCTL_STORAGE_MEDIA_REMOVAL = 0x002D4804;
/// <summary>
/// Constructor for the USBEject class
/// </summary>
/// <param name="driveLetter">This should be the drive letter. Format: F:/, C:/..</param>
public IntPtr USBEject(string driveLetter)
{
string filename = #"\\.\" + driveLetter[0] + ":";
return CreateFile(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, 0x3, 0, IntPtr.Zero);
}
public bool Eject(IntPtr handle)
{
bool result = false;
if (LockVolume(handle) && DismountVolume(handle))
{
PreventRemovalOfVolume(handle, false);
result = AutoEjectVolume(handle);
}
CloseHandle(handle);
return result;
}
private bool LockVolume(IntPtr handle)
{
uint byteReturned;
for (int i = 0; i < 10; i++)
{
if (DeviceIoControl(handle, FSCTL_LOCK_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero))
{
System.Windows.Forms.MessageBox.Show("Lock success!");
return true;
}
Thread.Sleep(500);
}
return false;
}
private bool PreventRemovalOfVolume(IntPtr handle, bool prevent)
{
byte[] buf = new byte[1];
uint retVal;
buf[0] = (prevent) ? (byte)1 : (byte)0;
return DeviceIoControl(handle, IOCTL_STORAGE_MEDIA_REMOVAL, buf, 1, IntPtr.Zero, 0, out retVal, IntPtr.Zero);
}
private bool DismountVolume(IntPtr handle)
{
uint byteReturned;
return DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero);
}
private bool AutoEjectVolume(IntPtr handle)
{
uint byteReturned;
return DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, IntPtr.Zero, 0, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero);
}
private bool CloseVolume(IntPtr handle)
{
return CloseHandle(handle);
}
So you can use it in two ways:
handle = USBEject("D:");
Eject(handle);
or directly:
Eject(USBEject("D:"));
It works for me on my Windows 10 machine (preview 14291)
Found the answer for my issue by using some of Roger Deep's code for the CreateFile call.
My code to remove a USB drive inside WPF Window:
private void Button_Click_1(object sender, RoutedEventArgs e)
{
EjectDrive('K');
}
void EjectDrive(char driveLetter)
{
string path = #"\\.\" + driveLetter + #":";
IntPtr handle = CreateFile(path, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, 0x3, 0, IntPtr.Zero);
if ((long)handle == -1)
{
MessageBox.Show("Unable to open drive " + driveLetter);
return;
}
int dummy = 0;
DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, IntPtr.Zero, 0,
IntPtr.Zero, 0, ref dummy, IntPtr.Zero);
CloseHandle(handle);
MessageBox.Show("OK to remove drive.");
}
[DllImport("kernel32", SetLastError = true)]
private static extern IntPtr CreateFile
(string filename, uint desiredAccess,
uint shareMode, IntPtr securityAttributes,
int creationDisposition, int flagsAndAttributes,
IntPtr templateFile);
[DllImport("kernel32")]
private static extern int DeviceIoControl
(IntPtr deviceHandle, uint ioControlCode,
IntPtr inBuffer, int inBufferSize,
IntPtr outBuffer, int outBufferSize,
ref int bytesReturned, IntPtr overlapped);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
Here's some code that I converted from a powershell script. You need to run with admin privileges and It works to "unmount" the USB drive. However, when you try to unplug the USB drive and plug it in again, it doesn't show up as a drive letter. (To get around that you need to type "WindowsKey-X" and select Disk-Manager to reassign the drive less to the USB device. (If anybody knows how to fix that problem please post to commits.) Here's the Code:
// Right click Project and Add Reference to System.Management.dll
using System.Management;
string mq = "SELECT * FROM Win32_Volume Where Name = 'E:\\'";
ManagementObjectSearcher ms = new ManagementObjectSearcher(mq);
foreach (ManagementObject mo in ms.Get())
{
mo["DriveLetter"] = null;
mo.Put();
ManagementBaseObject inParams = mo.GetMethodParameters("Dismount");
inParams["Force"] = false;
inParams["Permanent"] = false;
mo.InvokeMethod("Dismount", inParams, null);
}
Note that the powershell script also has the same problem of reattaching the USB device after ejecting. Here's the powershell script for your reference:
$vol = get-wmiobject -Class Win32_Volume |
where{$_.Name -eq 'E:\'}
$vol.DriveLetter = $null
$vol.Put()
$vol.Dismount($false, $false)
Here's a class that I just wrote to Manage Mounting and Unmounting of Removable USB Drives using WMI:
using System;
using System.IO;
using System.Text;
using System.Windows;
using System.Management; //<-- right-click on project and add reference
using System.Collections.Generic;
using System.Text.RegularExpressions;
// This Class implements Mount/Unmount for USB Removable Drives
// in a way similar to "Disk Manager" in the Control Panel.
//
// Currently, It doesn't implement "Eject" like when you right
// right-click on the USB icon on lower right of screen.
// The "Unmount" is similar to "Eject" except it dosn't
// cleanup the registry so that the USB drive can be automatically
// recognized again without manually mounting it from "Disk Manager"
// If somebody knows how to fix this class to gain this function...
// please post it to their thread. Thanks.
namespace WPM {
public struct UsbDriveItem_t {
public int Index;
public string DeviceId;
public char DriveLetter;
public string Label;
public override string ToString() {
if (Index < 0)
return "<none>";
else
return String.Format("{0}: {1}", DriveLetter, Label);
}
};
delegate void UsbEvent();
class UsbDriveRemovable {
public static int Unmount(char DriveLetter) {
bool success = ValidateAdmin("UsbDriveRemovable.Unmount()");
if (!success) return -1;
string Name = "'" + DriveLetter + ":\\\\'";
string mq = "SELECT * FROM Win32_Volume Where Name = " + Name;
ManagementObjectSearcher ms = new ManagementObjectSearcher(mq);
ManagementObjectCollection mc = ms.Get();
foreach (ManagementObject mo in mc) {
var DriveLetterI = mo["DriveLetter"].ToString();
mo["DriveLetter"] = null;
mo.Put();
ManagementBaseObject inParams = mo.GetMethodParameters("Dismount");
inParams["Force"] = false;
inParams["Permanent"] = false;
ManagementBaseObject outParams = mo.InvokeMethod("Dismount", inParams, null);
string rc = outParams["ReturnValue"].ToString();
mo.Dispose();
}
mc.Dispose();
ms.Dispose();
return 0;
}
public static int Mount(string DeviceId, char Letter = '?') {
bool success = ValidateAdmin("UsbDriveRemovable.Mount()");
if (!success) return -1;
if (Letter == '?' || Letter == '#') {
GetFirstUnsedLetter(out Letter);
}
string FixDeviceId = Regex.Replace(DeviceId, #"\\", #"\\");
string mq = "SELECT * FROM Win32_Volume WHERE DeviceId = '"
+ FixDeviceId
+ "'";
ManagementObjectSearcher ms = new ManagementObjectSearcher(mq);
ManagementObjectCollection mc = ms.Get();
foreach (ManagementObject mo in mc) {
ManagementBaseObject inParams = mo.GetMethodParameters("AddMountPoint");
inParams["Directory"] = Letter + ":\\";
ManagementBaseObject outParams = mo.InvokeMethod("AddMountPoint", inParams, null);
string rc = outParams["ReturnValue"].ToString();
mo.Dispose();
}
mc.Dispose();
ms.Dispose();
return 0;
}
/*List<UsbDriveItem_t>*/
public static int ListDrives(ref List<UsbDriveItem_t> DriveList) {
DriveList.Clear();
string mq = "SELECT * FROM Win32_Volume Where DriveType = '2'";
ManagementObjectSearcher ms = new ManagementObjectSearcher(mq);
ManagementObjectCollection mc = ms.Get();
int count = 0;
foreach (ManagementObject mo in mc) {
UsbDriveItem_t item = new UsbDriveItem_t();
item.Index = count;
item.Label = (mo["Label"] == null) ? "<none>" : mo["Label"].ToString();
item.DriveLetter = (mo["DriveLetter"] == null) ? '#' : mo["DriveLetter"].ToString()[0];
item.DeviceId = (mo["DeviceId"] == null) ? "<none>" : mo["DeviceId"].ToString();
DriveList.Add(item);
mo.Dispose();
}
count++;
mc.Dispose();
ms.Dispose();
return 0;
}
public static void MountItem(UsbDriveItem_t DriveItem) {
char DriveLetter = DriveItem.DriveLetter;
string DriveLabel = DriveItem.Label;
string DeviceId = DriveItem.DeviceId;
// Mount Drive if its not already Mounted
if (DriveLetter == '#') {
UsbDriveRemovable.GetFirstUnsedLetter(out DriveLetter);
UsbDriveRemovable.Mount(DeviceId, DriveLetter);
}
return;
}
public static void UnmountItem(UsbDriveItem_t DriveItem) {
char DriveLetter = DriveItem.DriveLetter;
UsbDriveRemovable.Unmount(DriveLetter);
return;
}
public static int GetFirstUnsedLetter(out char Letter) {
bool[] alphabet = new bool[26];
for (int i=0; i < 26; i++) {
alphabet[i] = false;
}
string mq = "SELECT * FROM Win32_Volume";
ManagementObjectSearcher ms = new ManagementObjectSearcher(mq);
ManagementObjectCollection mc = ms.Get();
foreach (ManagementObject mo in mc) {
if (mo["DriveLetter"] != null) {
char cc = mo["DriveLetter"].ToString()[0];
int ci = char.ToUpper(cc) - 65;
alphabet[ci] = true;
}
mo.Dispose();
}
mc.Dispose();
ms.Dispose();
int found = -1;
for (int i=3; i < 26; i++) {
if (alphabet[i] == false) {
found = i;
break;
}
}
if (found >= 0) {
Letter = (char)(found + 65);
return 0;
}
else {
Letter = '?';
return -1;
}
}
public static object
RegisterInsertEvent(UsbEvent InsertEvent) {
var insertQuery = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2");
var insertWatcher = new ManagementEventWatcher(insertQuery);
insertWatcher.EventArrived += delegate(object sender, EventArrivedEventArgs e) {
// string driveName = e.NewEvent.Properties["DriveName"].Value.ToString();
Action action = delegate {
InsertEvent();
};
Application.Current.Dispatcher.BeginInvoke(action);
};
insertWatcher.Start();
return (object)insertWatcher;
}
public static object RegisterRemoveEvent(UsbEvent RemoveEvent) {
var removeQuery = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 3");
var removeWatcher = new ManagementEventWatcher(removeQuery);
removeWatcher.EventArrived += delegate(object sender, EventArrivedEventArgs e) {
// string driveName = e.NewEvent.Properties["DriveName"].Value.ToString();
Action action = delegate {
RemoveEvent();
};
Application.Current.Dispatcher.BeginInvoke(action);
};
removeWatcher.Start();
return (object)removeWatcher;
}
// Mount all UsbRemovable Drives that are not currently mounted
public static int MountAll() {
List<UsbDriveItem_t> DriveList = new List<UsbDriveItem_t>();
ListDrives(ref DriveList);
foreach (UsbDriveItem_t item in DriveList) {
if (item.DriveLetter == '?') {
Mount(item.DeviceId);
}
}
return 0;
}
// Unmount all UsbRemovable Drives
public static int UnmountAll() {
List<UsbDriveItem_t> DriveList = new List<UsbDriveItem_t>();
ListDrives(ref DriveList);
foreach (UsbDriveItem_t item in DriveList) {
if (item.DriveLetter != '?') {
Unmount(item.DriveLetter);
}
}
return 0;
}
public static bool IsAdministrator()
{
var id = System.Security.Principal.WindowsIdentity.GetCurrent();
var prin = new System.Security.Principal.WindowsPrincipal(id);
return prin.IsInRole(
System.Security.Principal.WindowsBuiltInRole.Administrator);
}
public static bool ValidateAdmin(string CalledFrom = null) {
if (CalledFrom == null) {
CalledFrom = "";
}
if (!IsAdministrator()) {
string msg = "Please rerun this application with admin privileges.\r\n\r\n"
+ "Access denied to call " + CalledFrom + "\r\n\r\n";
MessageBox.Show(msg, "ERROR");
return false;
}
return true;
}
public static void StartExplorer(char DriveLetter)
{
var proc1 = new System.Diagnostics.Process();
proc1.StartInfo.FileName = #"C:\\Windows\\System32\\explorer.exe";
proc1.StartInfo.Arguments = DriveLetter.ToString();
proc1.StartInfo.CreateNoWindow = true;
proc1.StartInfo.UseShellExecute = false;
proc1.StartInfo.RedirectStandardOutput = true;
proc1.StartInfo.RedirectStandardError = true;
proc1.Start();
proc1.WaitForExit();
string proc1out = proc1.StandardOutput.ReadToEnd();
string proc1err = proc1.StandardError.ReadToEnd();
//if (proc1.ExitCode != 0) {
// string msg = proc1out + "\r\n\r\n" + proc1err;
// MessageBox.Show(msg, "Error: Mountvol /R");
//}
proc1.Close();
}
} //class
} //namespace
/* DOESN'T WORK WELL...
// Kludge to get USB Drive to be recognized again
void UsbCleanup() {
var proc1 = new System.Diagnostics.Process();
proc1.StartInfo.FileName = #"C:\\Windows\\System32\\mountvol.exe";
proc1.StartInfo.Arguments = #"/R";
proc1.StartInfo.CreateNoWindow = true;
proc1.StartInfo.UseShellExecute = false;
proc1.StartInfo.RedirectStandardOutput = true;
proc1.StartInfo.RedirectStandardError = true;
proc1.Start();
proc1.WaitForExit();
string proc1out = proc1.StandardOutput.ReadToEnd();
string proc1err = proc1.StandardError.ReadToEnd();
if (proc1.ExitCode != 0) {
string msg = proc1out + "\r\n\r\n" + proc1err;
MessageBox.Show(msg, "Error: Mountvol /R");
}
proc1.Close();
var proc2 = new System.Diagnostics.Process();
proc2.StartInfo.FileName = #"C:\\Windows\\System32\\mountvol.exe";
proc2.StartInfo.Arguments = #"/E";
proc2.StartInfo.CreateNoWindow = true;
proc2.StartInfo.UseShellExecute = false;
proc2.StartInfo.RedirectStandardOutput = true;
proc2.StartInfo.RedirectStandardError = true;
proc2.Start();
proc2.WaitForExit();
string proc2out = proc2.StandardOutput.ReadToEnd();
string proc2err = proc2.StandardError.ReadToEnd();
if (proc2.ExitCode != 0) {
string msg = proc1out + "\r\n\r\n" + proc1err;
MessageBox.Show(msg, "Error: Mountvol /E");
}
proc2.Close();
return;
}
*/
You could convert the following PowerShell into C#:
$Eject = New-Object -comObject Shell.Application
$Eject.NameSpace(17).ParseName($usbDrvLetter+“:”).InvokeVerb(“Eject”)

Categories

Resources