Moin Leute,
bei Codeproject fand ich den Artikel
Dabei waren die Quellen eines entsprechenden C-Projekts.
In Ermangelung anderer Aufgaben habe ich mich rangesetzt und diesen Code nach C# überttragen.
Mit freundlicher Zustimmung des Autors
möchte ich Euch an dem Projekt teilhaben lassen.
Ich habe es unter Win10 mit x86 und x64 getestet, es dürfte also unter AnyCPU laufen.
Ich habe die Kommentare etwas präzisiert, fragt aber bitte nicht, wie das ganze funktioniert, das weiß ich auch nicht so richtig.
In der Vorlage wurden einige Rückgabewerte ignoriert, die teste ich ab, was zum Übertragen nach C# erforderlich war.
Aus einigen der Strukturen habe ich Klassen gemacht. Dies war erforderlich, weil es Aufrufe mit
Das ganze ist hier als Console-Anwendung geschrieben, die eigentliche(n) Prozedur(en) ist aber separiert, so dass sie in jedes Projekt eingefügt werden können.
Program.cs
NativeMethods.cs
bei Codeproject fand ich den Artikel
How to Prepare a USB Drive for Safe Removal
.Dabei waren die Quellen eines entsprechenden C-Projekts.
In Ermangelung anderer Aufgaben habe ich mich rangesetzt und diesen Code nach C# überttragen.
Mit freundlicher Zustimmung des Autors
Uwe Sieber
uwe-sieber.demöchte ich Euch an dem Projekt teilhaben lassen.
Ich habe es unter Win10 mit x86 und x64 getestet, es dürfte also unter AnyCPU laufen.
Ich habe die Kommentare etwas präzisiert, fragt aber bitte nicht, wie das ganze funktioniert, das weiß ich auch nicht so richtig.
In der Vorlage wurden einige Rückgabewerte ignoriert, die teste ich ab, was zum Übertragen nach C# erforderlich war.
Aus einigen der Strukturen habe ich Klassen gemacht. Dies war erforderlich, weil es Aufrufe mit
null
als Parameter gab.Das ganze ist hier als Console-Anwendung geschrieben, die eigentliche(n) Prozedur(en) ist aber separiert, so dass sie in jedes Projekt eingefügt werden können.
C#-Quellcode
- //#define TEST_FIXED_LETTER // Defaultwert zum einfachen Testen
- // Ursprüngliche C-Vorlage
- // https://www.codeproject.com/Articles/13839/How-to-Prepare-a-USB-Drive-for-Safe-Removal
- // System-Error-Codes vom Betriebssystem
- // https://docs.microsoft.com/en-us/windows/desktop/debug/system-error-codes--0-499-
- using System;
- using System.IO;
- using System.Runtime.InteropServices;
- using System.Runtime.Serialization;
- using System.Runtime.Serialization.Formatters.Binary;
- using System.Text;
- namespace RemoveDriveByLetter
- {
- using HANDLE = IntPtr;
- internal class Program
- {
- private static void Main(string[] args)
- {
- char driveLetter;
- #if TEST_FIXED_LETTER
- Console.WriteLine("################################################");
- driveLetter = 'F';
- Console.WriteLine("Fixed Letter '{0}'", driveLetter);
- Console.WriteLine("################################################");
- #else
- if (args.Length == 0)
- {
- Console.WriteLine("RemoveDriveByLetter X");
- Console.WriteLine("Press any key to exit");
- Console.ReadKey();
- return;
- }
- driveLetter = args[0][0];
- #endif
- Console.WriteLine("System: {0}", Environment.Is64BitProcess ? "x64" : "x86");
- Console.WriteLine();
- Program.Eject(driveLetter);
- Console.WriteLine("Press any key to exit");
- Console.ReadKey();
- }
- /// <summary>
- /// Stand Alone-Methode zum Auswerfen eines USB-Sticks mit dem Laufwerksbuchstaben
- /// </summary>
- /// <param name="driveLetter">der Laufwerksbuchstabe</param>
- /// <returns>Success</returns>
- private static bool Eject(char driveLetter)
- {
- driveLetter = char.ToUpper(driveLetter);
- if (driveLetter < 'A' || driveLetter > 'Z')
- {
- Console.WriteLine("Argument is not a Drive Letter");
- return false;
- }
- // "\\.\X:" -> to open the volume
- string szVolumeAccessPath = string.Format(@"\\.\{0}:", driveLetter);
- int deviceNumber = -1;
- // open the storage volume
- HANDLE hVolume = NativeMethods.CreateFile(szVolumeAccessPath, 0, System.IO.FileShare.Read | System.IO.FileShare.Write, IntPtr.Zero, System.IO.FileMode.Open, 0, IntPtr.Zero);
- if (hVolume == NativeMethods.INVALID_HANDLE_VALUE)
- {
- Console.WriteLine("Device not available");
- return false;
- }
- // get the volume's device number
- NativeMethods.STORAGE_DEVICE_NUMBER sdn = default(NativeMethods.STORAGE_DEVICE_NUMBER);
- uint dwBytesReturned = 0;
- bool res = NativeMethods.DeviceIoControl(hVolume, NativeMethods.IOCTL_STORAGE_GET_DEVICE_NUMBER, IntPtr.Zero, 0, ref sdn, Marshal.SizeOf(sdn), ref dwBytesReturned, IntPtr.Zero);
- if (res)
- {
- deviceNumber = sdn.DeviceNumber;
- }
- NativeMethods.CloseHandle(hVolume);
- if (deviceNumber == -1)
- {
- Console.WriteLine("Invalid Device Number");
- return false;
- }
- string szDevicePath = driveLetter + ":";
- System.IO.DriveInfo di = new System.IO.DriveInfo(szDevicePath);
- StringBuilder lpTargetPath = new StringBuilder(NativeMethods.MAX_PATH);
- // get the dos device name (like \device\floppy0) to decide if it's a floppy or not - who knows a better way?
- res = NativeMethods.QueryDosDevice(szDevicePath, lpTargetPath, NativeMethods.MAX_PATH);
- if (!res)
- {
- Console.WriteLine("Invalid Device");
- return false;
- }
- // get the device instance handle of the storage volume by means of a SetupDi enum and matching the device number
- int devInst = Program.GetDrivesDevInstByDeviceNumber(deviceNumber, di.DriveType, lpTargetPath.ToString());
- if (devInst == 0)
- {
- return false;
- }
- NativeMethods.PNP_VETO_TYPE VetoType = NativeMethods.PNP_VETO_TYPE.PNP_VetoTypeUnknown;
- char[] VetoNameW = new char[NativeMethods.MAX_PATH];
- VetoNameW[0] = '\0';
- bool bSuccess = false;
- // get drives's parent, e.g. the USB bridge, the SATA port, an IDE channel with two drives!
- uint DevInstParent = 0;
- uint res2 = NativeMethods.CM_Get_Parent(out DevInstParent, devInst, 0);
- // res2 = 0 !
- for (int tries = 1; tries <= 3; tries++)
- {
- // sometimes we need some tries...
- VetoNameW[0] = '\0';
- res2 = NativeMethods.CM_Request_Device_Eject(DevInstParent, out VetoType, VetoNameW, NativeMethods.MAX_PATH, 0);
- bSuccess = (res2 == 0 && VetoType == NativeMethods.PNP_VETO_TYPE.PNP_VetoTypeUnknown);
- if (bSuccess)
- {
- break;
- }
- System.Threading.Thread.Sleep(500); // required to give the next tries a chance!
- }
- if (bSuccess)
- {
- Console.WriteLine("Success\n\n");
- }
- else
- {
- Console.WriteLine("failed\n");
- Console.WriteLine(string.Format("Result = {0}\n", res));
- if (VetoNameW[0] != '\0')
- {
- Console.WriteLine("VetoName = {0]", VetoNameW);
- }
- }
- return true;
- }
- private static int GetDrivesDevInstByDeviceNumber(int DeviceNumber, System.IO.DriveType driveType, string szDosDeviceName)
- {
- bool isFloppy = szDosDeviceName.Contains("\\Floppy"); // who knows a better way?
- Guid guid;
- // ReSharper disable once SwitchStatementMissingSomeCases
- switch (driveType)
- {
- case System.IO.DriveType.Removable:
- if (isFloppy)
- {
- guid = new Guid(0x53f56311, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
- }
- else
- {
- guid = new Guid(0x53f56307, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
- }
- break;
- case System.IO.DriveType.Fixed:
- guid = new Guid(0x53f56307, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
- break;
- case System.IO.DriveType.CDRom:
- guid = new Guid(0x53f56308, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
- break;
- default:
- return 0;
- }
- // Get device interface info set handle for all devices attached to system
- HANDLE hDevInfo = NativeMethods.SetupDiGetClassDevs(ref guid, IntPtr.Zero, IntPtr.Zero, NativeMethods.DIGCF_PRESENT | NativeMethods.DIGCF_DEVICEINTERFACE);
- if (hDevInfo == NativeMethods.INVALID_HANDLE_VALUE)
- {
- return 0;
- }
- // Retrieve a context structure for a device interface of a device information set
- uint dwIndex = 0;
- bool res;
- // class
- NativeMethods.SP_DEVICE_INTERFACE_DETAIL_DATA pspdidd = new NativeMethods.SP_DEVICE_INTERFACE_DETAIL_DATA();
- // struct
- NativeMethods.SP_DEVICE_INTERFACE_DATA spdid = default(NativeMethods.SP_DEVICE_INTERFACE_DATA);
- // class
- NativeMethods.SP_DEVINFO_DATA spdd = new NativeMethods.SP_DEVINFO_DATA();
- spdid.cbSize = Marshal.SizeOf(spdid); // 28
- uint dwSize;
- while (true)
- {
- res = NativeMethods.SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref guid, dwIndex, ref spdid);
- if (!res)
- {
- break;
- }
- // check the buffer size
- dwSize = 0;
- res = NativeMethods.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref spdid, IntPtr.Zero, 0, out dwSize, null);
- // dieser Rückgabewert wurde in der C-Vorlage einfach ignoriert.
- if (!res)
- {
- int error = Marshal.GetLastWin32Error();
- int error2 = 122; // steht im www
- if (Environment.Is64BitProcess)
- {
- // x64 error = 1008, in der C-Vorlage ausprobiert
- error2 = 1008;
- }
- // das muss so sein!
- if (error != error2)
- {
- Console.WriteLine("Error = {0}", error);
- }
- }
- if (dwSize != 0 && dwSize <= pspdidd.DevicePath.Length)
- {
- spdd.Clear();
- spdd.cbSize = Marshal.SizeOf(spdd);
- // Bestimmung der realen Größe der pspdidd-Instanz
- int objectSize = Program.GetObjectSize(pspdidd);
- // unmanaged Speicher bereitstellen
- IntPtr p1 = Marshal.AllocHGlobal(objectSize);
- // Länge der nativen Struktur reinschreiben, Werte aus der C-Vorlage übernommen
- if (Environment.Is64BitProcess)
- {
- Marshal.WriteInt32(p1, 8);
- }
- else
- {
- Marshal.WriteInt32(p1, 5);
- }
- res = NativeMethods.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref spdid, p1, dwSize, out dwSize, spdd);
- if (!res)
- {
- int error = Marshal.GetLastWin32Error();
- Console.WriteLine("Error = {0}", error);
- // Speicher wieder freigeben
- Marshal.FreeHGlobal(p1);
- }
- else
- {
- // Index 4 geht irgendwie nicht richtig
- Marshal.Copy(p1, pspdidd.DevicePath, 0, pspdidd.DevicePath.Length - 4);
- // Speicher wieder freigeben
- Marshal.FreeHGlobal(p1);
- string fileName = pspdidd.GetFileName((int)dwSize);
- // Ausgabe des System-Pfades zur Kontrolle
- Console.WriteLine(fileName);
- // open the disk or cdrom or floppy
- HANDLE hDrive = NativeMethods.CreateFile(fileName, 0, System.IO.FileShare.Read | System.IO.FileShare.Write, IntPtr.Zero, System.IO.FileMode.Open, 0, IntPtr.Zero);
- if (hDrive != NativeMethods.INVALID_HANDLE_VALUE)
- {
- // get its device number
- NativeMethods.STORAGE_DEVICE_NUMBER sdn = default(NativeMethods.STORAGE_DEVICE_NUMBER);
- uint dwBytesReturned = 0;
- res = NativeMethods.DeviceIoControl(hDrive, NativeMethods.IOCTL_STORAGE_GET_DEVICE_NUMBER, IntPtr.Zero, 0, ref sdn, Marshal.SizeOf(sdn), ref dwBytesReturned, IntPtr.Zero);
- if (res)
- {
- if (DeviceNumber == sdn.DeviceNumber)
- {
- // match the given device number with the one of the current device
- NativeMethods.CloseHandle(hDrive);
- NativeMethods.SetupDiDestroyDeviceInfoList(hDevInfo);
- return spdd.DevInst;
- }
- }
- NativeMethods.CloseHandle(hDrive);
- }
- }
- }
- dwIndex++;
- }
- NativeMethods.SetupDiDestroyDeviceInfoList(hDevInfo);
- return 0;
- }
- /// <summary>
- /// Bestimmen der Größe der SP_DEVICE_INTERFACE_DETAIL_DATA-Instanz
- /// </summary>
- /// <param name="pspdidd">die Instant</param>
- /// <returns>deren Größe</returns>
- private static int GetObjectSize(NativeMethods.SP_DEVICE_INTERFACE_DETAIL_DATA pspdidd)
- {
- int objectSize;
- using (MemoryStream stream = new MemoryStream())
- {
- IFormatter formatter = new BinaryFormatter();
- formatter.Serialize(stream, pspdidd);
- objectSize = (int)stream.Position;
- }
- return objectSize;
- }
- }
- }
C#-Quellcode
- using System;
- using System.Runtime.InteropServices;
- using System.Text;
- namespace RemoveDriveByLetter
- {
- internal static class NativeMethods
- {
- #region Enum
- /// <summary>
- /// Enum für CM_Request_Device_Eject
- /// </summary>
- internal enum PNP_VETO_TYPE
- {
- PNP_VetoTypeUnknown, // Name is unspecified
- PNP_VetoLegacyDevice, // Name is an Instance Path
- PNP_VetoPendingClose, // Name is an Instance Path
- PNP_VetoWindowsApp, // Name is a Module
- PNP_VetoWindowsService, // Name is a Service
- PNP_VetoOutstandingOpen, // Name is an Instance Path
- PNP_VetoDevice, // Name is an Instance Path
- PNP_VetoDriver, // Name is a Driver Service Name
- PNP_VetoIllegalDeviceRequest, // Name is an Instance Path
- PNP_VetoInsufficientPower, // Name is unspecified
- PNP_VetoNonDisableable, // Name is an Instance Path
- PNP_VetoLegacyDriver, // Name is a Service
- PNP_VetoInsufficientRights // Name is unspecified
- }
- #endregion Enum
- #region Classes
- [Serializable]
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
- internal class SP_DEVICE_INTERFACE_DETAIL_DATA
- {
- /// <summary>Größe der Struktur</summary>
- public int cbSize;
- /// <summary>Speicher für den Pfad</summary>
- public byte[] DevicePath;
- public SP_DEVICE_INTERFACE_DETAIL_DATA()
- {
- this.DevicePath = new byte[1024];
- }
- /// <summary>
- /// Auslesen des File-Namens
- /// </summary>
- /// <param name="size">C-String-Länge inklusive '\0'</param>
- /// <returns>.NET-File-Name</returns>
- public string GetFileName(int size)
- {
- string fileName = System.Text.Encoding.UTF8.GetString(this.DevicePath, 4, size);
- // String am 0-Terminierer abschneiden
- fileName = fileName.Split(new[] { '\0' }, 2)[0];
- return fileName;
- }
- }
- [StructLayout(LayoutKind.Sequential)]
- internal class SP_DEVINFO_DATA
- {
- public int cbSize;
- public Guid ClassGuid;
- public int DevInst; // DEVINST handle
- public IntPtr Reserved;
- public void Clear()
- {
- this.cbSize = 0;
- this.ClassGuid = Guid.Empty;
- this.DevInst = 0;
- this.Reserved = IntPtr.Zero;
- }
- }
- #endregion Classes
- #region Structs
- [StructLayout(LayoutKind.Sequential)]
- internal struct SP_DEVICE_INTERFACE_DATA
- {
- public int cbSize;
- public Guid InterfaceClassGuid;
- public uint Flags;
- public IntPtr Reserved;
- }
- [StructLayout(LayoutKind.Sequential)]
- internal struct STORAGE_DEVICE_NUMBER
- {
- /// <summary>The FILE_DEVICE_XXX type for this device.</summary>
- public int DeviceType;
- /// <summary>The number of this device</summary>
- public int DeviceNumber;
- /// <summary>
- /// If the device is partitionable, the partition number of the device.
- /// Otherwise -1
- /// </summary>
- public int PartitionNumber;
- }
- #endregion Structs
- #region Statics, Consts
- internal static IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1);
- internal const int MAX_PATH = 260;
- internal const int DIGCF_PRESENT = 0x02;
- internal const int DIGCF_DEVICEINTERFACE = 0x10;
- internal const uint METHOD_BUFFERED = 0;
- //internal const uint METHOD_IN_DIRECT = 1;
- //internal const uint METHOD_OUT_DIRECT = 2;
- //internal const uint METHOD_NEITHER = 3;
- private const int FILE_ANY_ACCESS = 0;
- //private const int FILE_READ_ACCESS = 1;
- //private const int FILE_WRITE_ACCESS = 2;
- //private const int OPEN_EXISTING = 3;
- // Device type in the "User Defined" range.
- private const uint IOCTL_STORAGE_BASE = 0x0000002d;
- #endregion Statics, Consts
- #region Kernel32.dll
- [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
- internal static extern IntPtr CreateFile(
- string fileName,
- uint fileAccess,
- [MarshalAs(UnmanagedType.U4)] System.IO.FileShare fileShare,
- IntPtr securityAttributes,
- [MarshalAs(UnmanagedType.U4)] System.IO.FileMode creationDisposition,
- int flags,
- IntPtr template);
- [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
- [return: MarshalAs(UnmanagedType.Bool)]
- internal static extern bool DeviceIoControl(
- IntPtr hDevice, // Microsoft.Win32.SafeHandles.SafeFileHandle hDevice,
- uint dwIoControlCode, // EIOControlCode IoControlCode,
- IntPtr InBuffer, // [MarshalAs(UnmanagedType.AsAny)] [In] object InBuffer,
- int nInBufferSize, // Size in Byte of Data in InBuffer
- //IntPtr OutBuffer, // [MarshalAs(UnmanagedType.AsAny)] [Out] object OutBuffer,
- [MarshalAs(UnmanagedType.Struct)] ref STORAGE_DEVICE_NUMBER lpSystemInfo,
- int nOutBufferSize, // Size in Byte of Data in OutBuffer
- ref uint nBytesReturned, // Number of Bytes pushed in OutBuffer (<= nOutBufferSize)
- IntPtr lpOverlapped); // [In] ref System.Threading.NativeOverlapped Overlapped
- [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
- [return: MarshalAs(UnmanagedType.Bool)]
- internal static extern bool QueryDosDevice(string lpDeviceName, StringBuilder lpTargetPath, int ucchMax);
- [DllImport("kernel32.dll", SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- internal static extern bool CloseHandle(IntPtr hObject);
- #endregion Kernel32.dll
- #region SetupApi.dll
- [DllImport("setupapi.dll", SetLastError = true)]
- internal static extern IntPtr SetupDiGetClassDevs(ref Guid classGuid, IntPtr enumerator, IntPtr hwndParent, UInt32 flags);
- [DllImport("setupapi.dll", SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- internal static extern bool SetupDiEnumDeviceInterfaces(IntPtr hDevInfo, IntPtr devInfo, ref Guid interfaceClassGuid, UInt32 memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData);
- [DllImport("setupapi.dll", SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- internal static extern bool SetupDiGetDeviceInterfaceDetail(
- IntPtr hDevInfo,
- ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
- //SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData,
- IntPtr deviceInterfaceDetailData,
- uint deviceInterfaceDetailDataSize,
- out uint requiredSize,
- SP_DEVINFO_DATA deviceInfoData);
- [DllImport("setupapi.dll", SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- internal static extern bool SetupDiDestroyDeviceInfoList(IntPtr hDevInfo);
- [DllImport("setupapi.dll")]
- internal static extern uint CM_Get_Parent(out UInt32 pdnDevInst, int dnDevInst, int ulFlags);
- [DllImport("setupapi.dll", CharSet = CharSet.Auto)]
- internal static extern uint CM_Request_Device_Eject(UInt32 devinst, out PNP_VETO_TYPE pVetoType, char[] pszVetoName, int ulNameLength, int ulFlags);
- #endregion SetupApi.dll
- #region Properties
- internal static uint IOCTL_STORAGE_GET_DEVICE_NUMBER
- {
- get { return NativeMethods.CTL_CODE(NativeMethods.IOCTL_STORAGE_BASE, 0x0420, NativeMethods.METHOD_BUFFERED, NativeMethods.FILE_ANY_ACCESS); }
- }
- #endregion Properties
- #region Private Stuff
- /// <summary>
- /// from WinIoCtl.h
- /// Macro definition for defining IOCTL and FSCTL function control codes.
- /// Note that function codes 0-2047 are reserved for Microsoft Corporation,
- /// and 2048-4095 are reserved for customers.
- /// </summary>
- /// <returns>Control Code</returns>
- private static uint CTL_CODE(uint deviceType, uint function, uint method, uint access)
- {
- return (((deviceType) << 16) | ((access) << 14) | ((function) << 2) | (method));
- }
- #endregion Private Stuff
- }
- }
Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch
Ein guter .NET-Snippetkonverter (der ist verfügbar).
Programmierfragen über PN / Konversation werden ignoriert!
Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch
Ein guter .NET-Snippetkonverter (der ist verfügbar).
Programmierfragen über PN / Konversation werden ignoriert!
Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „RodFromGermany“ () aus folgendem Grund: Ausgabe der Prozessbreite (x64 / x86)