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 :)
Related
I am developing an automation interface program and I looking to enhance capability with a machine software which uses a COPYDATA API aimed at C++. The goal is to control and report status of the machine through my own software.
The method uses pointers and memory allocation which I have not had any experience with thus far.
I have looked at a number of other sources, such as this with no luck at the moment. I have tried the following code to try and run a program on the machine software.
class Program
{
[DllImport("User32.dll", SetLastError = true, EntryPoint = "FindWindow")]
public static extern IntPtr FindWindow(String lpClassName, String lpWindowName);
[DllImport("User32.dll", SetLastError = true, EntryPoint = "SendMessage")]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, ref COPYDATASTRUCT lParam);
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
public IntPtr dwData; // Any value the sender chooses. Perhaps its main window handle?
public int cbData; // The count of bytes in the message.
public IntPtr lpData; // The address of the message.
}
const int WM_COPYDATA = 0x004A;
const int EXTERNAL_CD_COMMAND_RUN_ASYNC = 0x8001;
static void Main(string[] args)
{
Console.WriteLine("{0} bit process.", (IntPtr.Size == 4) ? "32" : "64");
Console.Write("Press ENTER to run test.");
Console.ReadLine();
IntPtr hwnd = FindWindow(null, "InSpecAppFrame");
Console.WriteLine("hwnd = {0:X}", hwnd.ToInt64());
var cds = new COPYDATASTRUCT();
byte[] buff = Encoding.ASCII.GetBytes("C:\\Users\\Desktop\\COPYDATATEST.iwp");
cds.dwData = (IntPtr)EXTERNAL_CD_COMMAND_RUN_ASYNC;
cds.lpData = Marshal.AllocHGlobal(buff.Length);
Marshal.Copy(buff, 0, cds.lpData, buff.Length);
cds.cbData = buff.Length;
var ret = SendMessage(hwnd, WM_COPYDATA, 0, ref cds);
Console.WriteLine("Return value is {0}", ret);
Marshal.FreeHGlobal(cds.lpData);
Console.ReadLine();
}
}
Running this code returns 0 for both hwnd and ret and the machine software does not react.
Sending a command is the first step, the next will be to try and get a response so I can monitor machine statuses etc.
As a sidenote to what Alejandro wrote (and that I think is correct), you can simplify a little the code, removing a copy of the data. You can directly "pin" your byte[]. It is important that you remember to "unpin" it (for this reason the try/finally block)
There is another potential problem in your code (a problem that I saw only on a second pass of the code): C strings must be \0 terminated (so "Foo" must be "Foo\0"). Your Encoding.ASCII doesn't guarantee a \0 termination. The classical way to do it is to make the byte[] "a little larger" than necessary. I've done the changes necessary.
[DllImport("User32.dll", SetLastError = true)]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, ref COPYDATASTRUCT lParam);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
public IntPtr dwData; // Any value the sender chooses. Perhaps its main window handle?
public int cbData; // The count of bytes in the message.
public IntPtr lpData; // The address of the message.
}
[StructLayout(LayoutKind.Sequential)]
public struct ExternalGetPositionType
{
public double X;
public double Y;
public double Z;
public double W;
}
const int WM_COPYDATA = 0x004A;
const int EXTERNAL_CD_COMMAND_RUN_ASYNC = 0x8001;
const int EXTERNAL_CD_GET_POSITION_PCS = 0x8011;
const int EXTERNAL_CD_GET_POSITION_MCS = 0x8012;
static void Main(string[] args)
{
Console.WriteLine("{0} bit process.", (IntPtr.Size == 4) ? "32" : "64");
Console.Write("Press ENTER to run test.");
Console.ReadLine();
IntPtr hwnd = FindWindow(null, "Form1");
Console.WriteLine("hwnd = {0:X}", hwnd.ToInt64());
if (hwnd == IntPtr.Zero)
{
throw new Exception("hwnd not found");
}
IntPtr ret = RunAsync(hwnd, #"C:\Users\Desktop\COPYDATATEST.iwp");
Console.WriteLine($"Return value for EXTERNAL_CD_COMMAND_RUN_ASYNC is {ret}");
ret = GetPosition(hwnd, true, new ExternalGetPositionType { X = 1, Y = 2, Z = 3, W = 4 });
Console.WriteLine($"Return value for EXTERNAL_CD_GET_POSITION_PCS is {ret}");
ret = GetPosition(hwnd, false, new ExternalGetPositionType { X = 10, Y = 20, Z = 30, W = 40 });
Console.WriteLine($"Return value for EXTERNAL_CD_GET_POSITION_MCS is {ret}");
Console.ReadLine();
}
public static IntPtr RunAsync(IntPtr hwnd, string str)
{
// We have to add a \0 terminator, so len + 1 / len + 2 for Unicode
int len = Encoding.Default.GetByteCount(str);
var buff = new byte[len + 1]; // len + 2 for Unicode
Encoding.Default.GetBytes(str, 0, str.Length, buff, 0);
IntPtr ret;
GCHandle h = default(GCHandle);
try
{
h = GCHandle.Alloc(buff, GCHandleType.Pinned);
var cds = new COPYDATASTRUCT();
cds.dwData = (IntPtr)EXTERNAL_CD_COMMAND_RUN_ASYNC;
cds.lpData = h.AddrOfPinnedObject();
cds.cbData = buff.Length;
ret = SendMessage(hwnd, WM_COPYDATA, 0, ref cds);
}
finally
{
if (h.IsAllocated)
{
h.Free();
}
}
return ret;
}
public static IntPtr GetPosition(IntPtr hwnd, bool pcs, ExternalGetPositionType position)
{
// We cheat here... It is much easier to pin an array than to copy around a struct
var positions = new[]
{
position
};
IntPtr ret;
GCHandle h = default(GCHandle);
try
{
h = GCHandle.Alloc(positions, GCHandleType.Pinned);
var cds = new COPYDATASTRUCT();
cds.dwData = pcs ? (IntPtr)EXTERNAL_CD_GET_POSITION_PCS : (IntPtr)EXTERNAL_CD_GET_POSITION_MCS;
cds.lpData = h.AddrOfPinnedObject();
cds.cbData = Marshal.SizeOf<ExternalGetPositionType>();
ret = SendMessage(hwnd, WM_COPYDATA, 0, ref cds);
}
finally
{
if (h.IsAllocated)
{
h.Free();
}
}
return ret;
}
Note even that instead of ASCII you can use the Default encoding, that is a little better.
If you want to receive the messages, in your Winforms do:
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_COPYDATA)
{
COPYDATASTRUCT cds = Marshal.PtrToStructure<COPYDATASTRUCT>(m.LParam);
if (cds.dwData == (IntPtr)EXTERNAL_CD_COMMAND_RUN_ASYNC)
{
string str = Marshal.PtrToStringAnsi(cds.lpData);
Debug.WriteLine($"EXTERNAL_CD_COMMAND_RUN_ASYNC: {str}");
m.Result = (IntPtr)100; // If you want to return a value
}
else if (cds.dwData == (IntPtr)EXTERNAL_CD_GET_POSITION_PCS)
{
if (cds.cbData >= Marshal.SizeOf<ExternalGetPositionType>())
{
var position = Marshal.PtrToStructure<ExternalGetPositionType>(cds.lpData);
Debug.WriteLine($"EXTERNAL_CD_GET_POSITION_PCS: X = {position.X}, Y = {position.Y}, Z = {position.Z}, W = {position.W}");
m.Result = (IntPtr)200;
}
else
{
m.Result = (IntPtr)0;
}
}
else if (cds.dwData == (IntPtr)EXTERNAL_CD_GET_POSITION_MCS)
{
if (cds.cbData >= Marshal.SizeOf<ExternalGetPositionType>())
{
var position = Marshal.PtrToStructure<ExternalGetPositionType>(cds.lpData);
Debug.WriteLine($"EXTERNAL_CD_GET_POSITION_MCS: X = {position.X}, Y = {position.Y}, Z = {position.Z}, W = {position.W}");
m.Result = (IntPtr)300;
}
else
{
m.Result = (IntPtr)0;
}
}
return;
}
base.WndProc(ref m);
}
Note that if you control both the sender AND the receiver, it is better much better to use Unicode for the string parameter. You'll have to modify both the sender and the receiver: Encoding.Unicode.GetByteCount/Encoding.Unicode.GetBytes, the +2 instead of +1 and Marshal.PtrToStringUni.
I'm reading other process memory with other memory scan tools and then I use given address in this simple console app:
const int PROCESS_WM_READ = 0x0010;
[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);
static void Main(string[] args)
{
Process process = Process.GetProcessesByName("myProcess")[0];
IntPtr processHandle = OpenProcess(PROCESS_WM_READ, false, process.Id);
var ptr = int.Parse(Console.ReadLine(), NumberStyles.HexNumber);
Console.WriteLine($"ptr: {ptr}");
for (int i = 1; i < 129; i++)
{
int bytesRead = 0;
byte[] buffer = new byte[i];
try
{
ReadProcessMemory((int)processHandle, ptr, buffer, buffer.Length, ref bytesRead);
if (BitConverter.ToInt32(buffer, 0) == 0)
{
Console.WriteLine("error occured");
continue;
}
Console.WriteLine(bytesRead.ToString());
Console.WriteLine(Encoding.Unicode.GetString(buffer));
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Console.ReadLine();
}
The problem is that outcome is always some ? ?? ? instead of int that I'm trying to reach
I tried different encodings
Console.WriteLine(Encoding.ASCII.GetString(buffer) + " (" + bytesRead.ToString() + "bytes)");
Console.WriteLine(Encoding.UTF8.GetString(buffer) + " (" + bytesRead.ToString() + "bytes)");
Console.WriteLine(Encoding.Default.GetString(buffer) + " (" + bytesRead.ToString() + "bytes)");
and buffer length - thats why there's Loop
What may be wrong with this?
You can attach the target process to Visual Studio to see values in the address you want to read. If they are valid data you can print out like this: Console.WriteLine(buffer[0]);
The following is example of reading process memory of BingDict (32bit) you can refer to.
class Program
{
[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);
const int PROCESS_WM_READ = 0x0010;
static void Main(string[] args)
{
Process process = Process.GetProcessById(13568);
IntPtr processHandle = OpenProcess(PROCESS_WM_READ, false, process.Id);
// Get the process start information
ProcessStartInfo myProcessStartInfo = new ProcessStartInfo("BingDict");
// Assign 'StartInfo' of notepad to 'StartInfo' of 'process' object.
process.StartInfo = myProcessStartInfo;
//process.Start();
System.Threading.Thread.Sleep(1000);
ProcessModule myProcessModule;
// Get all the modules associated with the process
ProcessModuleCollection myProcessModuleCollection = process.Modules;
Console.WriteLine("Base addresses of the modules associated are:");
// Display the 'BaseAddress' of each of the modules.
for (int i = 0; i < myProcessModuleCollection.Count; i++)
{
myProcessModule = myProcessModuleCollection[i];
Console.WriteLine(myProcessModule.ModuleName + " : "
+ myProcessModule.BaseAddress);
}
// Get the main module associated with the process
myProcessModule = process.MainModule;
// Display the 'BaseAddress' of the main module.
Console.WriteLine("The process's main module's base address is: {0:X4}",
(int)myProcessModule.BaseAddress);
var ptr = (int)myProcessModule.BaseAddress;
for (int i = 1; i < 129; i++)
{
int bytesRead = 0;
byte[] buffer = new byte[1];
try
{
if (ReadProcessMemory((int)processHandle, ptr, buffer, buffer.Length, ref bytesRead))
{
Console.WriteLine(buffer[0]);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Console.ReadLine();
}
}
I'm struggling a bit on this part...
I want to do this in CE!(that is read the value 20 in my c# app)
However my code is not working...
[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int dwSize, ref IntPtr lpNumberOfBytesRead);
public int ReadInt32(IntPtr address, int[] pointers)
{
/* FOR REFERENCE ONLY! PSEUDO-CODE
ReadProcessMemory(..., ModuleBaseAddress + 0x010F418, Temporary, ..., ...); // -> 0x02A917F8
ReadProcessMemory(..., 0x02A917F8+0x48, Temporary, .....,.); // -> 0x02A9A488
[02A9A488] = 20
*/
IntPtr bytesRead = IntPtr.Zero;
byte[] _buff = new byte[sizeof(int)];
int offIndex = 0;
IntPtr finalval = address;
Console.WriteLine("[BASE] {0:x}", (int)address);
foreach(int PointerOffs in pointers)
{
ReadProcessMemory(hProcess, address, _buff, _buff.Length, ref bytesRead);
finalval += pointers[offIndex];
Console.WriteLine("[Curr ADDRESS] {0:x}", finalval);
offIndex++;
}
return BitConverter.ToInt32(_buff, 0);
}
And this is how I access the method:
int currAmmo = (int) pReader.ReadInt32((IntPtr)LocalPlayer.BaseAddress, LocalPlayer.oMGAmmo);
Console.Write("[AMMO] {0}\n", currAmmo);
Your function has enough problems to warrant a replacement, I tried to fix it but it was easier just to start fresh. By utilizing a pre increment instead of a post increment you will de-reference the first pointer before adding an offset which is ideal.
public static int ReadInt32(IntPtr hProc, IntPtr ptr, int[] offsets)
{
IntPtr addr = ptr;
var buffer = new byte[4];
for (int i = 0; i < offsets.Length; ++i)
{
ReadProcessMemory(hProc, addr, buffer, buffer.Length, out var read1);
addr = IntPtr.Add(new IntPtr(BitConverter.ToInt32(buffer, 0)), offsets[i]);
}
ReadProcessMemory(hProc, addr, buffer, 4, out var read);
return BitConverter.ToInt32(buffer, 0);
}
I learned C# today just to answer this question :)
I've a problem trying to call SetupDiGetDeviceInterfaceDetail from C#. It always returns 1784 error code ("The supplied user buffer is not valid for the requested operation"). This is my C# code:
Guid GUID_DEVINTERFACE_DFU = new Guid(0x3fe809ab, 0xfb91, 0x4cb5, 0xa6, 0x43, 0x69, 0x67, 0x0d, 0x52,0x36,0x6e);
Guid classGuid = GUID_DEVINTERFACE_DFU;
IntPtr hDevInfo = Win32.SetupDiGetClassDevs(ref classGuid, IntPtr.Zero, IntPtr.Zero, Win32.DIGCF_DEVICEINTERFACE | Win32.DIGCF_PRESENT);
if (hDevInfo.ToInt32() == Win32.INVALID_HANDLE_VALUE)
{
Console.WriteLine("read hardware information error");
}
else
{
SP_DEVINFO_DATA devInfoData = new SP_DEVINFO_DATA();
devInfoData.cbSize = (uint)Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
devInfoData.classGuid = Guid.Empty;
devInfoData.devInst = 0;
devInfoData.reserved = IntPtr.Zero;
bool result = Win32.SetupDiEnumDeviceInfo(hDevInfo, i, devInfoData);
if (false == result)
{
int error = Marshal.GetLastWin32Error();
if (error != Win32.ERROR_NO_MORE_ITEMS)
throw new Win32Exception(error);
}
SP_DEVICE_INTERFACE_DATA ifData = new SP_DEVICE_INTERFACE_DATA();
ifData.cbSize = (uint)Marshal.SizeOf(ifData);
ifData.Flags = 0;
ifData.InterfaceClassGuid = Guid.Empty;
ifData.Reserved = IntPtr.Zero;
bool result2 = Win32.SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref classGuid, i, ifData);
if(result2 == false)
{
int error = Marshal.GetLastWin32Error();
if (error != Win32.ERROR_NO_MORE_ITEMS)
throw new Win32Exception(error);
}
uint needed;
// This returns: needed=160, result3=false and error=122 ("The data area passed to a system call is too small")
bool result3 = Win32.SetupDiGetDeviceInterfaceDetail(hDevInfo, ifData, null, 0, out needed, null);
if(result3 == false)
{
int error = Marshal.GetLastWin32Error();
}
IntPtr detailDataBuffer = IntPtr.Zero;
SP_DEVICE_INTERFACE_DETAIL_DATA ifDetailsData = new SP_DEVICE_INTERFACE_DETAIL_DATA();
ifDetailsData.devicePath = new byte[needed - 4];
ifDetailsData.cbSize = (uint)Marshal.SizeOf(ifDetailsData);
uint nBytes = needed;
// This returns always: error = 1784
bool result4 = Win32.SetupDiGetDeviceInterfaceDetail(hDevInfo, ifData, ifDetailsData, nBytes, out needed, null);
if (result4 == false)
{
int error = Marshal.GetLastWin32Error();
if (error != Win32.ERROR_NO_MORE_ITEMS)
throw new Win32Exception(error);
}
}
Classe Win32:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace USB_test
{
[StructLayout(LayoutKind.Sequential)]
public class SP_DEVINFO_DATA
{
public uint cbSize;
public Guid classGuid;
public uint devInst;
public IntPtr reserved;
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class SP_DEVICE_INTERFACE_DETAIL_DATA
{
public uint cbSize;
public byte[] devicePath;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public class SP_DEVICE_INTERFACE_DATA
{
public uint cbSize;
public Guid InterfaceClassGuid;
public uint Flags;
public IntPtr Reserved;
}
public class Win32
{
public static uint ANYSIZE_ARRAY = 1000;
[DllImport("setupapi.dll", SetLastError = true)]
public static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, IntPtr Enumerator, IntPtr hwndParent, uint Flags);
[DllImport("setupapi.dll", SetLastError = true)]
public static extern Boolean SetupDiEnumDeviceInfo(IntPtr lpInfoSet, UInt32 dwIndex, SP_DEVINFO_DATA devInfoData);
[DllImport(#"setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern Boolean SetupDiEnumDeviceInterfaces(IntPtr hDevInfo, IntPtr devInfo, ref Guid interfaceClassGuid, uint memberIndex, SP_DEVICE_INTERFACE_DATA deviceInterfaceData);
[DllImport(#"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr hDevInfo, SP_DEVICE_INTERFACE_DATA deviceInterfaceData, SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData, uint deviceInterfaceDetailDataSize, out uint requiredSize, SP_DEVINFO_DATA deviceInfoData);
public const int DIGCF_PRESENT = 0x02;
public const int DIGCF_DEVICEINTERFACE = 0x10;
public const int SPDRP_DEVICEDESC = (0x00000000);
public const long ERROR_NO_MORE_ITEMS = 259L;
}
}
If it can help someone, this is the solution:
IntPtr detailDataBuffer = Marshal.AllocHGlobal((int)needed);
Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8);
uint nBytes = needed;
bool result4 = Win32.SetupDiGetDeviceInterfaceDetail(hDevInfo, ifData, detailDataBuffer, nBytes, out needed, null);
if (result4 == false)
{
int error = Marshal.GetLastWin32Error();
if (error != Win32.ERROR_NO_MORE_ITEMS)
throw new Win32Exception(error);
}
IntPtr pDevicePathName = new IntPtr(detailDataBuffer.ToInt32() + 4);
String devicePathName = Marshal.PtrToStringAuto(pDevicePathName);
Additional Note: If running on a 64-bit machine, or forced 64-bit mode, the above line for the pointer to pDevicePathName would reference a 64-bit pointer, not 32
IntPtr pDevicePathName = new IntPtr(detailDataBuffer.ToInt64() + 8);
The struct is a variable sized structure which cannot be marshalled automatically. You'll need to do so yourself.
You'll need to remove the SP_DEVICE_INTERFACE_DETAIL_DATA type. It's no use to you. Change the declaration of SetupDiGetDeviceInterfaceDetail to:
[DllImport(#"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean SetupDiGetDeviceInterfaceDetail(
IntPtr hDevInfo,
SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
IntPtr deviceInterfaceDetailData,
uint deviceInterfaceDetailDataSize,
out uint requiredSize,
SP_DEVINFO_DATA deviceInfoData
);
Pass IntPtr.Zero in the first call to SetupDiGetDeviceInterfaceDetail. Then allocate a buffer of the required size by calling Marshal.AllocHGlobal. Then write the size into the first 4 bytes of that buffer. Then call SetupDiGetDeviceInterfaceDetail again.
Something along these lines:
bool result3 = Win32.SetupDiGetDeviceInterfaceDetail(hDevInfo, ifData, IntPtr.Zero, 0,
out needed, null);
if(!result3)
{
int error = Marshal.GetLastWin32Error();
}
// expect that result3 is false and that error is ERROR_INSUFFICIENT_BUFFER = 122,
// and needed is the required size
IntPtr DeviceInterfaceDetailData = Marshal.AllocHGlobal((int)needed);
try
{
uint size = needed;
Marshal.WriteInt32(DeviceInterfaceDetailData, (int)size);
bool result4 = Win32.SetupDiGetDeviceInterfaceDetail(hDevInfo, ifData,
DeviceInterfaceDetailData, size, out needed, null);
if(!result4)
{
int error = Marshal.GetLastWin32Error();
}
// do whatever you need with DeviceInterfaceDetailData
}
finally
{
Marshal.FreeHGlobal(DeviceInterfaceDetailData);
}
For me, the answer of David Hoffmann doesn't work. But he inspired me to this solution:
IntPtr buffer = Marshal.AllocHGlobal((int)requiredSize);
int cbSize = sizeof(DWORD) + 2 * sizeof(CHAR); // cbSize + empty DevicePath
Marshal.WriteInt32(buffer, cbSize);
deviceInterfaceInfoData.cbSize = (DWORD)Marshal.SizeOf(deviceInterfaceInfoData);
if (!SetupDiGetDeviceInterfaceDetail(
deviceInfoSet,
ref deviceInterfaceData,
buffer,
requiredSize,
out requiredSize,
ref deviceInterfaceInfoData))
throw new Win32Exception(error);
int devicePathSize = (int)requiredSize - sizeof(DWORD); // cbSize
char[] devicePathChars = new char[devicePathSize / sizeof(char)];
int offset = sizeof(DWORD); // cbSize
Marshal.Copy(IntPtr.Add(buffer, offset), devicePathChars, 0, devicePathChars.Length);
string devicePath = new(devicePathChars, 0, devicePathChars.Length - 1); // Remove NULL terminator
Marshal.FreeHGlobal(buffer);
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”)