Related
I've been trying to disable/enable Windows 10 devices immidiately with WPF. There is an answer here but it give me an exception as mentioned in the answer's comments. There also an suggestion for fixing also in the answer's comments but I don't know how to make it work because I'm new to WPF.
Here is the code:
public static class DisableHardware
{
const uint DIF_PROPERTYCHANGE = 0x12;
const uint DICS_ENABLE = 1;
const uint DICS_DISABLE = 2; // disable device
const uint DICS_FLAG_GLOBAL = 1; // not profile-specific
const uint DIGCF_ALLCLASSES = 4;
const uint DIGCF_PRESENT = 2;
const uint ERROR_INVALID_DATA = 13;
const uint ERROR_NO_MORE_ITEMS = 259;
const uint ERROR_ELEMENT_NOT_FOUND = 1168;
static DEVPROPKEY DEVPKEY_Device_DeviceDesc;
static DEVPROPKEY DEVPKEY_Device_HardwareIds;
[StructLayout(LayoutKind.Sequential)]
struct SP_CLASSINSTALL_HEADER
{
public UInt32 cbSize;
public UInt32 InstallFunction;
}
[StructLayout(LayoutKind.Sequential)]
struct SP_PROPCHANGE_PARAMS
{
public SP_CLASSINSTALL_HEADER ClassInstallHeader;
public UInt32 StateChange;
public UInt32 Scope;
public UInt32 HwProfile;
}
[StructLayout(LayoutKind.Sequential)]
struct SP_DEVINFO_DATA
{
public UInt32 cbSize;
public Guid classGuid;
public UInt32 devInst;
public IntPtr reserved; // CHANGE #1 - was UInt32
}
[StructLayout(LayoutKind.Sequential)]
struct DEVPROPKEY
{
public Guid fmtid;
public UInt32 pid;
}
[DllImport("setupapi.dll", SetLastError = true)]
static extern IntPtr SetupDiGetClassDevsW(
[In] ref Guid ClassGuid,
[MarshalAs(UnmanagedType.LPWStr)]
string Enumerator,
IntPtr parent,
UInt32 flags);
[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiDestroyDeviceInfoList(IntPtr handle);
[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiEnumDeviceInfo(IntPtr deviceInfoSet,
UInt32 memberIndex,
[Out] out SP_DEVINFO_DATA deviceInfoData);
[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiSetClassInstallParams(
IntPtr deviceInfoSet,
[In] ref SP_DEVINFO_DATA deviceInfoData,
[In] ref SP_PROPCHANGE_PARAMS classInstallParams,
UInt32 ClassInstallParamsSize);
[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiChangeState(
IntPtr deviceInfoSet,
[In] ref SP_DEVINFO_DATA deviceInfoData);
[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiGetDevicePropertyW(
IntPtr deviceInfoSet,
[In] ref SP_DEVINFO_DATA DeviceInfoData,
[In] ref DEVPROPKEY propertyKey,
[Out] out UInt32 propertyType,
IntPtr propertyBuffer,
UInt32 propertyBufferSize,
out UInt32 requiredSize,
UInt32 flags);
[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiGetDeviceRegistryPropertyW(
IntPtr DeviceInfoSet,
[In] ref SP_DEVINFO_DATA DeviceInfoData,
UInt32 Property,
[Out] out UInt32 PropertyRegDataType,
IntPtr PropertyBuffer,
UInt32 PropertyBufferSize,
[In,Out] ref UInt32 RequiredSize
);
static DisableHardware()
{
DisableHardware.DEVPKEY_Device_DeviceDesc = new DEVPROPKEY();
DEVPKEY_Device_DeviceDesc.fmtid = new Guid(
0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67,
0xd1, 0x46, 0xa8, 0x50, 0xe0);
DEVPKEY_Device_DeviceDesc.pid = 2;
DEVPKEY_Device_HardwareIds = new DEVPROPKEY();
DEVPKEY_Device_HardwareIds.fmtid = new Guid(
0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67,
0xd1, 0x46, 0xa8, 0x50, 0xe0);
DEVPKEY_Device_HardwareIds.pid = 3;
}
public static void DisableDevice(Func<string, bool> filter, bool disable = true)
{
IntPtr info = IntPtr.Zero;
Guid NullGuid = Guid.Empty;
try
{
info = SetupDiGetClassDevsW(
ref NullGuid,
null,
IntPtr.Zero,
DIGCF_ALLCLASSES);
CheckError("SetupDiGetClassDevs");
SP_DEVINFO_DATA devdata = new SP_DEVINFO_DATA();
devdata.cbSize = (UInt32)Marshal.SizeOf(devdata);
// Get first device matching device criterion.
for (uint i = 0; ; i++)
{
SetupDiEnumDeviceInfo(info,
i,
out devdata);
// if no items match filter, throw
if (Marshal.GetLastWin32Error() == ERROR_NO_MORE_ITEMS)
CheckError("No device found matching filter.", 0xcffff);
CheckError("SetupDiEnumDeviceInfo");
string devicepath = GetStringPropertyForDevice(info,
devdata, 1); // SPDRP_HARDWAREID
// Uncomment to print name/path
//Console.WriteLine(GetStringPropertyForDevice(info,
// devdata, DEVPKEY_Device_DeviceDesc));
//Console.WriteLine(" {0}", devicepath);
if (devicepath != null && filter(devicepath)) break;
}
SP_CLASSINSTALL_HEADER header = new SP_CLASSINSTALL_HEADER();
header.cbSize = (UInt32)Marshal.SizeOf(header);
header.InstallFunction = DIF_PROPERTYCHANGE;
SP_PROPCHANGE_PARAMS propchangeparams = new SP_PROPCHANGE_PARAMS();
propchangeparams.ClassInstallHeader = header;
propchangeparams.StateChange = disable ? DICS_DISABLE : DICS_ENABLE;
propchangeparams.Scope = DICS_FLAG_GLOBAL;
propchangeparams.HwProfile = 0;
SetupDiSetClassInstallParams(info,
ref devdata,
ref propchangeparams,
(UInt32)Marshal.SizeOf(propchangeparams));
CheckError("SetupDiSetClassInstallParams");
SetupDiChangeState(
info,
ref devdata);
CheckError("SetupDiChangeState");
}
finally
{
if (info != IntPtr.Zero)
SetupDiDestroyDeviceInfoList(info);
}
}
private static void CheckError(string message, int lasterror = -1)
{
int code = lasterror == -1 ? Marshal.GetLastWin32Error() : lasterror;
if (code != 0)
throw new ApplicationException(
String.Format("Error disabling hardware device (Code {0}): {1}",
code, message));
}
private static string GetStringPropertyForDevice(IntPtr info, SP_DEVINFO_DATA devdata,
uint propId)
{
uint proptype, outsize;
IntPtr buffer = IntPtr.Zero;
try
{
uint buflen = 512;
buffer = Marshal.AllocHGlobal((int)buflen);
outsize=0;
// CHANGE #2 - Use this instead of SetupDiGetDeviceProperty
SetupDiGetDeviceRegistryPropertyW(
info,
ref devdata,
propId,
out proptype,
buffer,
buflen,
ref outsize);
byte[] lbuffer = new byte[outsize];
Marshal.Copy(buffer, lbuffer, 0, (int)outsize);
int errcode = Marshal.GetLastWin32Error();
if (errcode == ERROR_INVALID_DATA) return null;
CheckError("SetupDiGetDeviceProperty", errcode);
return Encoding.Unicode.GetString(lbuffer);
}
finally
{
if (buffer != IntPtr.Zero)
Marshal.FreeHGlobal(buffer);
}
}
}
The suggested fix is:
The problem is that, the function uses a fixed buffer length of 512, which a lot of times is not enough to hold the data. What needs to be done is to first get the size of buffer as follows: if(!SetupDiGetDeviceRegistryPropertyW(info, ref devdata, propId, out proptype, IntPtr.Zero, 0, ref outsize)). Once the size is got, allocate the buffer and call SetupDiGetDeviceRegistryPropertyW again
I haven't messed with this stuff before, but just reading the comment on that other question, it looks like the suggestion is to do this:
private static string GetStringPropertyForDevice(IntPtr info, SP_DEVINFO_DATA devdata, uint propId)
{
uint proptype, outsize;
IntPtr buffer = IntPtr.Zero;
try
{
SetupDiGetDeviceRegistryPropertyW(
info,
ref devdata,
propId,
out proptype,
IntPtr.Zero,
0,
ref outsize);
uint buflen = outsize;
buffer = Marshal.AllocHGlobal((int)buflen);
outsize = 0;
SetupDiGetDeviceRegistryPropertyW(
info,
ref devdata,
propId,
out proptype,
buffer,
buflen,
ref outsize);
byte[] lbuffer = new byte[outsize];
Marshal.Copy(buffer, lbuffer, 0, (int)outsize);
int errcode = Marshal.GetLastWin32Error();
if (errcode == ERROR_INVALID_DATA) return null;
CheckError("SetupDiGetDeviceProperty", errcode);
return Encoding.Unicode.GetString(lbuffer);
}
finally
{
if (buffer != IntPtr.Zero)
Marshal.FreeHGlobal(buffer);
}
}
Call SetupDiGetDeviceRegistryPropertyW with no buffer first, just to get the outsize value, then use that as your buffer size. This avoids creating a buffer of arbitrary length which could be exceeded, at the cost of making the call twice.
Again- I haven't tested this or anything, it just seems like what the person writing that comment was intending. Hopefully it works!
I want to launch an external program from C# to be completely detached.
I use CreateProcess through pinvoke because Process.Start doesn't allow me to use DETACHED_PROCESS. Also I want this application to redirect it's output to some file.
Here's the sample code:
var processInformation = new ProcessUtility.PROCESS_INFORMATION();
var securityInfo = new ProcessUtility.STARTUPINFO();
var sa = new ProcessUtility.SECURITY_ATTRIBUTES();
sa.Length = Marshal.SizeOf(sa);
// Create process with no window and totally detached
var result = ProcessUtility.CreateProcess(Path.Combine(Environment.SystemDirectory, "cmd.exe"), commandLineArguments, ref sa, ref sa, false,
ProcessUtility.DETACHED_PROCESS, IntPtr.Zero, null, ref securityInfo, out processInformation);
CommandLineArguments are something like this : "/c Foo.bat > Foo.log 2>&1"
Everything works fine and Foo.log is populated by Foo.bat. No additional console window is visible. PERFECT.
CommandLineArguments are something like this : "/c Foo.exe > Foo.log 2>&1"
Foo.exe is .NET Console Application.
Foo.log is not populated and Foo.exe is launched in visible console window. STRANGE. Why behavior is different from 1. ?
Just for your information. CommandLineArguments are something like this : "/c Foo.exe > Foo.log 2>&1"
Foo.exe is .NET Windows Application.
Everything works fine but when I launch this application simply from command prompt I see no output because no console is allocated.
I want 2. work the same as 1. Why there's a difference ?
UPDATE: I don't want to write the Foo.log for myself because launching application will be killed.
UPDATE: Ok, I wrote some code to specify that only one handle is inherited but CreateProcess gives me error 87 when calling with EXTENDED_STARTUPINFO_PRESENT ( even if it's present and empty ).
Can you please help me why?
public class ProcessUtility
{
// Process creation flags
const uint ZERO_FLAG = 0x00000000;
const uint CREATE_BREAKAWAY_FROM_JOB = 0x01000000;
const uint CREATE_DEFAULT_ERROR_MODE = 0x04000000;
const uint CREATE_NEW_CONSOLE = 0x00000010;
const uint CREATE_NEW_PROCESS_GROUP = 0x00000200;
const uint CREATE_NO_WINDOW = 0x08000000;
const uint CREATE_PROTECTED_PROCESS = 0x00040000;
const uint CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000;
const uint CREATE_SEPARATE_WOW_VDM = 0x00001000;
const uint CREATE_SHARED_WOW_VDM = 0x00001000;
const uint CREATE_SUSPENDED = 0x00000004;
const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;
const uint DEBUG_ONLY_THIS_PROCESS = 0x00000002;
const uint DEBUG_PROCESS = 0x00000001;
const uint DETACHED_PROCESS = 0x00000008;
const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000;
const uint INHERIT_PARENT_AFFINITY = 0x00010000;
// Thread attributes flags
const uint PROC_THREAD_ATTRIBUTE_HANDLE_LIST = 0x00020002;
const uint PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000;
// File creation flags
const uint FILE_ACCESS_WRITE = 0x40000000;
// StartupInfo flags
const int STARTF_USESTDHANDLES = 0x00000100;
[StructLayout(LayoutKind.Sequential)]
struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
struct STARTUPINFOEX
{
public STARTUPINFO StartupInfo;
public IntPtr lpAttributeList;
};
[StructLayout(LayoutKind.Sequential)]
struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public Int32 dwProcessID;
public Int32 dwThreadID;
}
[StructLayout(LayoutKind.Sequential)]
struct SECURITY_ATTRIBUTES
{
public Int32 Length;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CreateProcess(
string lpApplicationName,
string lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
[In] ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CreateProcess(
string lpApplicationName,
string lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
[In] ref STARTUPINFOEX lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UpdateProcThreadAttribute(
IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, IntPtr lpValue,
IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool InitializeProcThreadAttributeList(
IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
static extern SafeFileHandle CreateFile(
string lpFileName,
uint fileAccess,
[MarshalAs(UnmanagedType.U4)] FileShare fileShare,
SECURITY_ATTRIBUTES securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile);
public static bool CreateProcessWithStdHandlesRedirect(string lpApplicationName, string lpCommandLine, string logFilename)
{
var startupInfo = new STARTUPINFOEX();
startupInfo.StartupInfo.cb = Marshal.SizeOf(startupInfo);
try
{
var lpSize = IntPtr.Zero;
if (InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref lpSize) || lpSize == IntPtr.Zero)
return false;
startupInfo.lpAttributeList = Marshal.AllocHGlobal(lpSize);
// Here startupInfo.lpAttributeList is initialized to hold 1 value
if (!InitializeProcThreadAttributeList(startupInfo.lpAttributeList, 1, 0, ref lpSize))
return false;
var fileSecurityAttributes = new SECURITY_ATTRIBUTES();
fileSecurityAttributes.Length = Marshal.SizeOf(fileSecurityAttributes);
// Create inheritable file handle
fileSecurityAttributes.bInheritHandle = true;
// Open log file for writing
using (var handle = CreateFile(logFilename, FILE_ACCESS_WRITE, FileShare.ReadWrite,
fileSecurityAttributes, FileMode.Create, 0, IntPtr.Zero))
{
var fileHandle = handle.DangerousGetHandle();
// Add filehandle to proc thread attribute list
if (!UpdateProcThreadAttribute(startupInfo.lpAttributeList, 0, (IntPtr)PROC_THREAD_ATTRIBUTE_HANDLE_LIST, fileHandle,
(IntPtr)IntPtr.Size, IntPtr.Zero, IntPtr.Zero))
return false;
startupInfo.StartupInfo.hStdError = fileHandle;
startupInfo.StartupInfo.hStdOutput = fileHandle;
// startupInfo.StartupInfo.hStdInput = ?;
startupInfo.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
var processInformation = new PROCESS_INFORMATION();
var securityAttributes = new SECURITY_ATTRIBUTES();
securityAttributes.Length = Marshal.SizeOf(securityAttributes);
securityAttributes.bInheritHandle = true;
// Create process with no window and totally detached
return ProcessUtility.CreateProcess(lpApplicationName, lpCommandLine, ref securityAttributes, ref securityAttributes, true,
DETACHED_PROCESS | EXTENDED_STARTUPINFO_PRESENT, IntPtr.Zero, null, ref startupInfo, out processInformation);
}
}
finally
{
if (startupInfo.lpAttributeList != IntPtr.Zero)
{
DeleteProcThreadAttributeList(startupInfo.lpAttributeList);
Marshal.FreeHGlobal(startupInfo.lpAttributeList);
}
}
}
}
In case 1, the instance of cmd.exe that you're launching can run the batch file itself. No child process is created.
In case 2, the instance of cmd.exe that you're launching has to run the console application as a child process. It has no way of knowing that you wanted the application to not be given a console window, so when it calls CreateProcess it does not use the DETACHED_PROCESS flag, and Windows creates a new console as normal.
In case 3, the child process is not a console application, so Windows does not create a console for it even though DETACHED_PROCESS was not specified.
The usual solution is to open the foo.log file yourself, launch the console application directly (rather than via cmd.exe) and use the STARTUP_INFO structure to pass the log file handle as the standard output and standard error for the new process. Once CreateProcess has returned you can close the file handle. The duplicated handle in the child process will not be affected when your process closes.
However, I'm not sure how you would properly go about that in .NET. It's a bit tricky at the best of times, because you have to make the child process inherit the log file handle without having it inherit other handles inappropriately - that's probably the reason Process.Start is causing you problems. The recommended practice is to use a process/thread attribute list (InitializeProcThreadAttributeList) with a PROC_THREAD_ATTRIBUTE_HANDLE_LIST entry. (But the log handle still needs to be inheritable.)
Create a vb script, NoWindow.vbs, perhaps programmatically on the fly, as follows
CreateObject("Wscript.Shell").Run WScript.Arguments(0), 0, False
From you main app, call cscript simply with Process.Start
Process.Start("cscript", "NoWindow.vbs \"cmd /c Foo.exe > Foo.log 2>&1 \" ");
The vbs script itself will detach the process. No window is visible.
My testing was limited to using Procexp to confirm that the proceses Foo.exe was detached - Win7.
Chekout this answer to hide console:
How to Run a C# console application with the console hidden
And this answer to launch detached process :
Process Tree
I have created website project, and am using IIS version 6 as the web server when developing. When I try to debug this method (createfile) bellow in order to read from USB .
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
const uint OPEN_EXISTING = 3;
const uint GENERIC_WRITE = (0x40000000);
const uint FSCTL_LOCK_VOLUME = 0x00090018;
const uint FSCTL_UNLOCK_VOLUME = 0x0009001c;
const uint FSCTL_DISMOUNT_VOLUME = 0x00090020;
bool success = false;
int intOut;
string deviceId = #"\\.\H:";
long DiskSize = 2056320000;
SafeFileHandle diskHandle = CreateFile(deviceId, GENERIC_WRITE, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (diskHandle.IsInvalid)
{
//Console.WriteLine(deviceId + " open error.");
Debug.WriteLine(Marshal.GetLastWin32Error());
return 0;
}
I get this error num (5=access denied), but it works fine in other types of projects like (windows forms , console application , asp project ).
How can this be solved?
thank you in advance
the solution has been found, we just need to give permissions to the application pool by changing the identity property to (Local system)
I am trying to port the following C++ code:
BOOL SyskeyGetClassBytes(HKEY hKeyReg,LPSTR keyName,LPSTR valueName,LPBYTE classBytes) {
HKEY hKey,hSubKey;
DWORD dwDisposition=0,classSize;
BYTE classStr[16];
LONG ret;
BOOL isSuccess = FALSE;
ret = RegCreateKeyEx(hKeyReg,keyName,0,NULL,REG_OPTION_NON_VOLATILE,KEY_QUERY_VALUE,NULL,&hKey,&dwDisposition);
if(ret!=ERROR_SUCCESS)
return FALSE;
else if(dwDisposition!=REG_OPENED_EXISTING_KEY) {
RegCloseKey(hKey);
return FALSE;
}
else {
if(RegOpenKeyEx(hKey,valueName,0,KEY_READ,&hSubKey)==ERROR_SUCCESS) {
classSize = 8+1;
ret = RegQueryInfoKey(hSubKey,(LPTSTR)classStr,&classSize,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
if((ret==ERROR_SUCCESS)&&(classSize==8)) {
classBytes[0]= (HexDigitToByte(classStr[0]) << 4) | HexDigitToByte(classStr[1]);
classBytes[1]= (HexDigitToByte(classStr[2]) << 4) | HexDigitToByte(classStr[3]);
classBytes[2]= (HexDigitToByte(classStr[4]) << 4) | HexDigitToByte(classStr[5]);
classBytes[3]= (HexDigitToByte(classStr[6]) << 4) | HexDigitToByte(classStr[7]);
isSuccess = TRUE;
}
RegCloseKey(hSubKey);
}
RegCloseKey(hKey);
}
return isSuccess;
}
I spent like 5 hours trying to figure out my problem, with no success. I know for a fact that I am properly calling this method. My C# code is
unsafe static bool SyskeyGetClassBytes(RegistryHive hKeyReg, string keyName, string valueName, byte* classBytes)
{
UIntPtr hSubKey;
UIntPtr hKey;
RegResult tmp; ;
uint classSize;
StringBuilder classStr = new StringBuilder();
int ret;
bool isSuccess = false;
ret = RegCreateKeyEx(hKeyReg, keyName, 0, null, RegOption.NonVolatile, RegSAM.QueryValue, UIntPtr.Zero, out hKey, out tmp);
if (ret != 0)
{
return false;
}
else if (tmp != RegResult.OpenedExistingKey)
{
return false;
}
else
{
int res = RegOpenKeyEx(hKey, valueName, 0, (int)RegSAM.Read, out hSubKey);
if (res == 0)
{
classSize = 8 + 1;
ret = RegQueryInfoKey(hSubKey, out classStr, ref classSize, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
if ((classSize == 8))
{
classBytes[0] = (byte)((byte)(HexDigitToByte(classStr[0]) << (byte)4) | HexDigitToByte(classStr[1]));
classBytes[1] = (byte)((byte)(HexDigitToByte(classStr[2]) << (byte)4) | HexDigitToByte(classStr[3]));
classBytes[2] = (byte)((byte)(HexDigitToByte(classStr[4]) << (byte)4) | HexDigitToByte(classStr[5]));
classBytes[3] = (byte)((byte)(HexDigitToByte(classStr[6]) << (byte)4) | HexDigitToByte(classStr[7]));
isSuccess = true;
}
RegCloseKey(hSubKey);
}
else
{
return false;
}
RegCloseKey(hKey);
}
return isSuccess;
}
Its a little bit hard for me to debug, but eventually I determined that the problem is occurring at this line. Execution seems to halt afterwards.
ret = RegQueryInfoKey(hSubKey, out classStr, ref classSize, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
I know this is not a problem with permissions, as this C# program is running with admin perms AND as the local system account. The method that I need that the .Net APIs don't offer is RegQueryInfoKey. My P/Invoke signatures and types used are:
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public unsafe byte* lpSecurityDescriptor;
public int bInheritHandle;
}
[Flags]
public enum RegOption
{
NonVolatile = 0x0,
Volatile = 0x1,
CreateLink = 0x2,
BackupRestore = 0x4,
OpenLink = 0x8
}
[Flags]
public enum RegSAM
{
QueryValue = 0x0001,
SetValue = 0x0002,
CreateSubKey = 0x0004,
EnumerateSubKeys = 0x0008,
Notify = 0x0010,
CreateLink = 0x0020,
WOW64_32Key = 0x0200,
WOW64_64Key = 0x0100,
WOW64_Res = 0x0300,
Read = 0x00020019,
Write = 0x00020006,
Execute = 0x00020019,
AllAccess = 0x000f003f
}
public enum RegResult
{
CreatedNewKey = 0x00000001,
OpenedExistingKey = 0x00000002
}
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int RegOpenKeyEx(
UIntPtr hKey,
string subKey,
int ulOptions,
int samDesired,
out UIntPtr hkResult);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern int RegCloseKey(
UIntPtr hKey);
[DllImport("advapi32.dll", SetLastError = true)]
static extern int RegCreateKeyEx(
RegistryHive hKey,
string lpSubKey,
int Reserved,
string lpClass,
RegOption dwOptions,
RegSAM samDesired,
UIntPtr lpSecurityAttributes,
out UIntPtr phkResult,
out RegResult lpdwDisposition);
[DllImport("advapi32.dll", EntryPoint = "RegQueryInfoKey", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
extern private static int RegQueryInfoKey(
UIntPtr hkey,
out StringBuilder lpClass,
ref uint lpcbClass,
IntPtr lpReserved,
IntPtr lpcSubKeys,
IntPtr lpcbMaxSubKeyLen,
IntPtr lpcbMaxClassLen,
IntPtr lpcValues,
IntPtr lpcbMaxValueNameLen,
IntPtr lpcbMaxValueLen,
IntPtr lpcbSecurityDescriptor,
IntPtr lpftLastWriteTime);
The lpClass parameter is declared incorrectly. Pass the StringBuilder by value.
[DllImport("advapi32.dll")]
extern private static int RegQueryInfoKey(
UIntPtr hkey,
StringBuilder lpClass,
ref uint lpcbClass,
IntPtr lpReserved,
IntPtr lpcSubKeys,
IntPtr lpcbMaxSubKeyLen,
IntPtr lpcbMaxClassLen,
IntPtr lpcValues,
IntPtr lpcbMaxValueNameLen,
IntPtr lpcbMaxValueLen,
IntPtr lpcbSecurityDescriptor,
IntPtr lpftLastWriteTime
);
You also need to allocate the StringBuilder instance to have the desired capacity. So, allocate the StringBuilder like this:
StringBuilder classStr = new StringBuilder(255);//or whatever length you like
And then set classSize like this:
classSize = classStr.Capacity+1;
I removed the parameters to DllImport. Most are not necessary, and the SetLastError is incorrect.
There may be other issues with your code, but with these changes at least the call to RegQueryInfoKey will match your C++ code.
Try this signature for RegQueryInfoKey:
[DllImport("advapi32.dll", EntryPoint="RegQueryInfoKey", CallingConvention=CallingConvention.Winapi, SetLastError=true)]
extern private static int RegQueryInfoKey(
UIntPtr hkey,
out StringBuilder lpClass,
ref uint lpcbClass,
IntPtr lpReserved,
out uint lpcSubKeys,
out uint lpcbMaxSubKeyLen,
out uint lpcbMaxClassLen,
out uint lpcValues,
out uint lpcbMaxValueNameLen,
out uint lpcbMaxValueLen,
out uint lpcbSecurityDescriptor,
IntPtr lpftLastWriteTime);
You are not declaring them as out params and in the RegQueryInfoKey Win32 call they are _Out_opt_.
You need to initialize your StringBuilder with enough capacity to store a classSize number of characters.
classSize = 8 + 1;
classStr.Capacity = classSize;
ret = RegQueryInfoKey(hSubKey, out classStr, ref classSize, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
The marshaller will use the capacity set on the StringBuilder to send a buffer of the capacity size to the RegQueryInfoKey function. Without that you are probably corrupted memory.
See http://msdn.microsoft.com/en-us/library/s9ts558h.aspx#cpcondefaultmarshalingforstringsanchor3
I need to obtain disk geometry info, but something wrong and DeviceIoControl returning false. Any ideas how to fix it? Or Other examples using C# and kernel32 appreciated.
[DllImport("kernel32.dll")]
public static extern IntPtr CreateFile(
string lpFileName, int dwDesiredAccess, int dwShareMode,
IntPtr lpSecurityAttributes, int dwCreationDisposition,
int dwFlagsAndAttributes, IntPtr hTemplateFile);
private const int FILE_SHARE_READ = 1;
private const int OPEN_ALWAYS = 4;
private const int INVALID_HANDLE_VALUE = -1;
[DllImport("kernel32.dll", ExactSpelling = true)]
internal static extern bool DeviceIoControl(
IntPtr hDevice, int dwIoControlCode, IntPtr lpInBuffer, int nInBufferSize,
IntPtr lpOutBuffer, int nOutBufferSize, ref int lpBytesReturned, IntPtr lpOverlapped);
private const int IOCTL_DISK_GET_MEDIA_TYPES = 0x00070c00;
static void Main(string[] args)
{
IntPtr hflp = CreateFile(#""\\.\C:", 0, FILE_SHARE_READ, IntPtr.Zero, OPEN_ALWAYS, 0, IntPtr.Zero);
if ((int)hflp == INVALID_HANDLE_VALUE)
{ Console.WriteLine("CreateFile failed"); return; }
Type ts = typeof(DISK_GEOMETRY);
int ss = Marshal.SizeOf(ts);
int ssa = ss * 20;
IntPtr mptr = Marshal.AllocHGlobal(ssa);
int byret = 0;
bool ok = DeviceIoControl(hflp, IOCTL_DISK_GET_MEDIA_TYPES, IntPtr.Zero, 0,
mptr, ssa, ref byret, IntPtr.Zero);
if (!ok)
{ Console.WriteLine("DeviceIoControl failed"); return; }
int count = byret / ss;
int run = (int)mptr;
DISK_GEOMETRY gem;
for (int i = 0; i < count; i++)
{
gem = (DISK_GEOMETRY)Marshal.PtrToStructure((IntPtr)run, ts);
Console.WriteLine("MediaType={0} SectorsPerTrack={1}", gem.MediaType, gem.SectorsPerTrack);
run += ss;
}
Marshal.FreeHGlobal(mptr);
}
P.S I've already read msdn help on this.
IOCTL_DISK_GET_MEDIA_TYPES appears to be legacy and no longer supported. At least that's the case on my OS (Win7 x64). Attempting to call DeviceIoControl with IOCTL_DISK_GET_MEDIA_TYPES results in error code 1, ERROR_INVALID_FUNCTION.
I believe that you will need to use IOCTL_STORAGE_GET_MEDIA_TYPES_EX instead.
My advice in this situation is to attempt to call the API functions from C++ first. That way you don't have to struggle with p/invoke and you know that all the structures and function prototypes are correct. Once you have worked out how to call the particular API function then translate into p/invoke.
As an aside, you should be a little more careful about your p/invokes. Take care to use uint to match DWORD, and make sure you use SetLastError=true so that you can query the error code with Marshal.GetLastWin32Error().
Something like this:
[DllImport("kernel32.dll", SetLastError=true)]
public static extern IntPtr CreateFile(
string lpFileName, uint dwDesiredAccess, uint dwShareMode,
IntPtr lpSecurityAttributes, uint dwCreationDisposition,
uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError=true)]
internal static extern bool DeviceIoControl(
IntPtr hDevice, uint dwIoControlCode, IntPtr lpInBuffer,
uint nInBufferSize, IntPtr lpOutBuffer, uint nOutBufferSize,
ref uint lpBytesReturned, IntPtr lpOverlapped);