11
A
回答
8
這是一個快速而髒的翻譯this sample code從support.microsoft.com刪除驅動器。但它只適用於在我的系統上擁有管理權限的用戶。
有關使用USB設備的更多信息,請參見this answer的concept03。
function OpenVolume(ADrive: char): THandle;
var
RootName, VolumeName: string;
AccessFlags: DWORD;
begin
RootName := ADrive + ':\'; (* '\'' // keep SO syntax highlighting working *)
case GetDriveType(PChar(RootName)) of
DRIVE_REMOVABLE:
AccessFlags := GENERIC_READ or GENERIC_WRITE;
DRIVE_CDROM:
AccessFlags := GENERIC_READ;
else
Result := INVALID_HANDLE_VALUE;
exit;
end;
VolumeName := Format('\\.\%s:', [ADrive]);
Result := CreateFile(PChar(VolumeName), AccessFlags,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
if Result = INVALID_HANDLE_VALUE then
RaiseLastWin32Error;
end;
function LockVolume(AVolumeHandle: THandle): boolean;
const
LOCK_TIMEOUT = 10 * 1000; // 10 Seconds
LOCK_RETRIES = 20;
LOCK_SLEEP = LOCK_TIMEOUT div LOCK_RETRIES;
// #define FSCTL_LOCK_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 6, METHOD_BUFFERED, FILE_ANY_ACCESS)
FSCTL_LOCK_VOLUME = (9 shl 16) or (0 shl 14) or (6 shl 2) or 0;
var
Retries: integer;
BytesReturned: Cardinal;
begin
for Retries := 1 to LOCK_RETRIES do begin
Result := DeviceIoControl(AVolumeHandle, FSCTL_LOCK_VOLUME, nil, 0,
nil, 0, BytesReturned, nil);
if Result then
break;
Sleep(LOCK_SLEEP);
end;
end;
function DismountVolume(AVolumeHandle: THandle): boolean;
const
// #define FSCTL_DISMOUNT_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 8, METHOD_BUFFERED, FILE_ANY_ACCESS)
FSCTL_DISMOUNT_VOLUME = (9 shl 16) or (0 shl 14) or (8 shl 2) or 0;
var
BytesReturned: Cardinal;
begin
Result := DeviceIoControl(AVolumeHandle, FSCTL_DISMOUNT_VOLUME, nil, 0,
nil, 0, BytesReturned, nil);
if not Result then
RaiseLastWin32Error;
end;
function PreventRemovalOfVolume(AVolumeHandle: THandle;
APreventRemoval: boolean): boolean;
const
// #define IOCTL_STORAGE_MEDIA_REMOVAL CTL_CODE(IOCTL_STORAGE_BASE, 0x0201, METHOD_BUFFERED, FILE_READ_ACCESS)
IOCTL_STORAGE_MEDIA_REMOVAL = ($2d shl 16) or (1 shl 14) or ($201 shl 2) or 0;
type
TPreventMediaRemoval = record
PreventMediaRemoval: BOOL;
end;
var
BytesReturned: Cardinal;
PMRBuffer: TPreventMediaRemoval;
begin
PMRBuffer.PreventMediaRemoval := APreventRemoval;
Result := DeviceIoControl(AVolumeHandle, IOCTL_STORAGE_MEDIA_REMOVAL,
@PMRBuffer, SizeOf(TPreventMediaRemoval), nil, 0, BytesReturned, nil);
if not Result then
RaiseLastWin32Error;
end;
function AutoEjectVolume(AVolumeHandle: THandle): boolean;
const
// #define IOCTL_STORAGE_EJECT_MEDIA CTL_CODE(IOCTL_STORAGE_BASE, 0x0202, METHOD_BUFFERED, FILE_READ_ACCESS)
IOCTL_STORAGE_EJECT_MEDIA = ($2d shl 16) or (1 shl 14) or ($202 shl 2) or 0;
var
BytesReturned: Cardinal;
begin
Result := DeviceIoControl(AVolumeHandle, IOCTL_STORAGE_EJECT_MEDIA, nil, 0,
nil, 0, BytesReturned, nil);
if not Result then
RaiseLastWin32Error;
end;
function EjectVolume(ADrive: char): boolean;
var
VolumeHandle: THandle;
begin
Result := FALSE;
// Open the volume
VolumeHandle := OpenVolume(ADrive);
if VolumeHandle = INVALID_HANDLE_VALUE then
exit;
try
// Lock and dismount the volume
if LockVolume(VolumeHandle) and DismountVolume(VolumeHandle) then begin
// Set prevent removal to false and eject the volume
if PreventRemovalOfVolume(VolumeHandle, FALSE) then
AutoEjectVolume(VolumeHandle);
end;
finally
// Close the volume so other processes can use the drive
CloseHandle(VolumeHandle);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
EjectVolume('E');
end;
2
這不會彈出驅動器,但會沖洗驅動器緩衝區並使其可以安全地移除。它需要Vista和更高版本的管理權限(如果作爲有限權限的用戶運行,則爲XP)。它可能應該有一個嘗試..最終確保CloseHandle
被調用;我把這個作爲一個練習留給讀者,因爲在沒有水平滾動的情況下,代碼格式是緊的。 :-)
unit USBDriveFlush;
interface
uses Windows;
type
// Taken from JEDI JwaWinIoctl
PSTORAGE_HOTPLUG_INFO = ^STORAGE_HOTPLUG_INFO;
{$EXTERNALSYM PSTORAGE_HOTPLUG_INFO}
_STORAGE_HOTPLUG_INFO = record
Size: DWORD; // version
MediaRemovable: BOOLEAN; // ie. zip, jaz, cdrom, mo, etc. vs hdd
MediaHotplug: BOOLEAN; // ie. does the device succeed a lock
// even though its not lockable media?
DeviceHotplug: BOOLEAN; // ie. 1394, USB, etc.
WriteCacheEnableOverride: BOOLEAN; // This field should not be
// relied upon because it is no longer used
end;
{$EXTERNALSYM _STORAGE_HOTPLUG_INFO}
STORAGE_HOTPLUG_INFO = _STORAGE_HOTPLUG_INFO;
{$EXTERNALSYM STORAGE_HOTPLUG_INFO}
TStorageHotplugInfo = STORAGE_HOTPLUG_INFO;
PStorageHotplugInfo = PSTORAGE_HOTPLUG_INFO;
function FlushUSBDrive(const Drive: string): Boolean;
implementation
function FlushUSBDrive(const Drive: string): Boolean;
var
shpi : TStorageHotplugInfo;
retlen : DWORD; //unneeded, but deviceiocontrol expects it
h : THandle;
begin
Result := False;
h := CreateFile(PChar('\\.\' + Drive),
0,
FILE_SHARE_READ or FILE_SHARE_WRITE,
nil,
OPEN_EXISTING,
0,
0);
if h <> INVALID_HANDLE_VALUE then
begin
shpi.Size := SizeOf(shpi);
if DeviceIoControl(h,
IOCTL_STORAGE_GET_HOTPLUG_INFO,
nil,
0,
@shpi,
SizeOf(shpi),
retlen,
nil) then
begin
//shpi now has the existing values, so you can check to
//see if the device is already hot-pluggable
if not shpi.DeviceHotplug then
begin
shpi.DeviceHotplug:= True;
//Need to use correct administrator security privilages here
//otherwise it'll just give 'access is denied' error
Result := DeviceIoControl(h,
IOCTL_STORAGE_SET_HOTPLUG_INFO,
@shpi,
SizeOf(shpi),
nil,
0,
retlen,
nil);
end;
end;
CloseHandle(h);
end;
end;
使用示例:
if FlushUSBDrive('G:') then
ShowMessage('Safe to remove USB drive G:')
else
ShowMessage('Flush of drive G: failed!' +
SysErrorMessage(GetLastError()));
4
用於移除USB驅動器是使用功能的關鍵,
檢查該樣本Delphi應用程序,基於本文How to Prepare a USB Drive for Safe Removal
和上使用JEDI API Library & Security Code Library
{$APPTYPE CONSOLE}
{$R *.res}
uses
JwaWinIoctl,
Cfg,
CfgMgr32,
SetupApi,
Windows,
SysUtils;
function GetDrivesDevInstByDeviceNumber(DeviceNumber : LONG; DriveType : UINT; szDosDeviceName: PChar) : DEVINST;
var
StorageGUID : TGUID;
IsFloppy : Boolean;
hDevInfo : SetupApi.HDEVINFO;
dwIndex : DWORD;
res : BOOL;
pspdidd : PSPDeviceInterfaceDetailData;
spdid : SP_DEVICE_INTERFACE_DATA;
spdd : SP_DEVINFO_DATA;
dwSize : DWORD;
hDrive : THandle;
sdn : STORAGE_DEVICE_NUMBER;
dwBytesReturned : DWORD;
begin
Result:=0;
IsFloppy := pos('\\Floppy', szDosDeviceName)>0; // who knows a better way?
case DriveType of
DRIVE_REMOVABLE:
if (IsFloppy) then
StorageGUID := GUID_DEVINTERFACE_FLOPPY
else
StorageGUID := GUID_DEVINTERFACE_DISK;
DRIVE_FIXED: StorageGUID := GUID_DEVINTERFACE_DISK;
DRIVE_CDROM: StorageGUID := GUID_DEVINTERFACE_CDROM;
else
exit
end;
// Get device interface info set handle for all devices attached to system
hDevInfo := SetupDiGetClassDevs(@StorageGUID, nil, 0, DIGCF_PRESENT OR DIGCF_DEVICEINTERFACE);
if (NativeUInt(hDevInfo) <> INVALID_HANDLE_VALUE) then
try
// Retrieve a context structure for a device interface of a device information set
dwIndex := 0;
//PSP_DEVICE_INTERFACE_DETAIL_DATA pspdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)Buf;
spdid.cbSize := SizeOf(spdid);
while true do
begin
res := SetupDiEnumDeviceInterfaces(hDevInfo, nil, StorageGUID, dwIndex, spdid);
if not res then
break;
dwSize := 0;
SetupDiGetDeviceInterfaceDetail(hDevInfo, @spdid, nil, 0, dwSize, nil); // check the buffer size
if (dwSize<>0) then
begin
pspdidd := AllocMem(dwSize);
try
pspdidd.cbSize := SizeOf(TSPDeviceInterfaceDetailData);
ZeroMemory(@spdd, sizeof(spdd));
spdd.cbSize := SizeOf(spdd);
res := SetupDiGetDeviceInterfaceDetail(hDevInfo, @spdid, pspdidd, dwSize, dwSize, @spdd);
if res then
begin
// open the disk or cdrom or floppy
hDrive := CreateFile(pspdidd.DevicePath, 0, FILE_SHARE_READ OR FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
if (hDrive <> INVALID_HANDLE_VALUE) then
try
// get its device number
dwBytesReturned := 0;
res := DeviceIoControl(hDrive, IOCTL_STORAGE_GET_DEVICE_NUMBER, nil, 0, @sdn, sizeof(sdn), dwBytesReturned, nil);
if res then
begin
if (DeviceNumber = sdn.DeviceNumber) then
begin // match the given device number with the one of the current device
Result:= spdd.DevInst;
exit;
end;
end;
finally
CloseHandle(hDrive);
end;
end;
finally
FreeMem(pspdidd);
end;
end;
Inc(dwIndex);
end;
finally
SetupDiDestroyDeviceInfoList(hDevInfo);
end;
end;
procedure EjectUSB(const DriveLetter:char);
var
szRootPath, szDevicePath : PChar;
szVolumeAccessPath : PChar;
hVolume : THandle;
DeviceNumber : LONG;
sdn : STORAGE_DEVICE_NUMBER;
dwBytesReturned : DWORD;
res : BOOL;
resCM : Cardinal;
DriveType : UINT;
szDosDeviceName : array [0..MAX_PATH-1] of Char;
DevInst : CfgMgr32.DEVINST;
VetoType : PNP_VETO_TYPE;
VetoName : array [0..MAX_PATH-1] of WCHAR;
bSuccess : Boolean;
DevInstParent : CfgMgr32.DEVINST;
tries : Integer;
begin
szRootPath := PChar(DriveLetter+':\');
szDevicePath := PChar(DriveLetter+':');
szVolumeAccessPath := PChar(Format('\\.\%s:',[DriveLetter]));
DeviceNumber:=-1;
// open the storage volume
hVolume := CreateFile(szVolumeAccessPath, 0, FILE_SHARE_READ OR FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
if (hVolume <> INVALID_HANDLE_VALUE) then
try
//get the volume's device number
dwBytesReturned := 0;
res := DeviceIoControl(hVolume, IOCTL_STORAGE_GET_DEVICE_NUMBER, nil, 0, @sdn, SizeOf(sdn), dwBytesReturned, nil);
if res then
DeviceNumber := sdn.DeviceNumber;
finally
CloseHandle(hVolume);
end;
if DeviceNumber=-1 then exit;
// get the drive type which is required to match the device numbers correctely
DriveType := GetDriveType(szRootPath);
// get the dos device name (like \device\floppy0) to decide if it's a floppy or not - who knows a better way?
QueryDosDevice(szDevicePath, szDosDeviceName, MAX_PATH);
// get the device instance handle of the storage volume by means of a SetupDi enum and matching the device number
DevInst := GetDrivesDevInstByDeviceNumber(DeviceNumber, DriveType, szDosDeviceName);
if (DevInst = 0) then
exit;
VetoType := PNP_VetoTypeUnknown;
bSuccess := false;
// get drives's parent, e.g. the USB bridge, the SATA port, an IDE channel with two drives!
DevInstParent := 0;
resCM := CM_Get_Parent(DevInstParent, DevInst, 0);
for tries:=0 to 3 do // sometimes we need some tries...
begin
FillChar(VetoName[0], SizeOf(VetoName), 0);
// CM_Query_And_Remove_SubTree doesn't work for restricted users
//resCM = CM_Query_And_Remove_SubTree(DevInstParent, &VetoType, VetoNameW, MAX_PATH, CM_REMOVE_NO_RESTART); // CM_Query_And_Remove_SubTreeA is not implemented under W2K!
//resCM = CM_Query_And_Remove_SubTree(DevInstParent, NULL, NULL, 0, CM_REMOVE_NO_RESTART); // with messagebox (W2K, Vista) or balloon (XP)
resCM := CM_Request_Device_Eject(DevInstParent, @VetoType, @VetoName[0], Length(VetoName), 0);
resCM := CM_Request_Device_Eject(DevInstParent,nil, nil, 0, 0); // optional -> shows messagebox (W2K, Vista) or balloon (XP)
bSuccess := (resCM=CR_SUCCESS) and (VetoType=PNP_VetoTypeUnknown);
if (bSuccess) then
break;
Sleep(500); // required to give the next tries a chance!
end;
if (bSuccess) then
Writeln('Success')
else
Writeln('Failed');
end;
begin
try
LoadSetupApi;
LoadConfigManagerApi;
EjectUSB('F');
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
相關問題
- 1. 如何以編程方式使用MBR和USB閃存扇區工作?
- 2. 以編程方式查詢USB閃存驅動器卷標
- 3. 如何以編程方式刪除AlertDialog
- 4. 如何以編程方式刪除plist?
- 5. 如何以編程方式刪除layout_marginStart
- 6. 在Linux中使用libudev以編程方式枚舉USB閃存驅動器
- 7. 以編程方式創建/刪除/擦除磁盤分區?
- 8. 以編程方式從TFS2012刪除/刪除TestSuite使用API
- 9. 如何檢測USB閃存驅動器的盤符USB閃存驅動器的
- 10. 如何以編程方式刪除緩存?
- 11. 如何以編程方式刪除Eclipse安全存儲設置
- 12. 如何以編程方式查找USB存儲路徑?
- 13. 以編程方式從通知托盤中刪除UILocalNotification
- 14. 如何以編程方式在Genymotion上閃存zip
- 15. 在Windows中編程區分USB軟盤驅動器和USB閃存驅動器
- 16. 如何以編程方式從Blackberry OS中刪除托盤圖標
- 17. 以編程方式刪除控件
- 18. Angular2以編程方式刪除組件。
- 19. data.table - 以編程方式刪除列
- 20. HTML5 appcache以編程方式刪除
- 21. NSArrayController - 添加:&刪除:以編程方式
- 22. 以編程方式刪除NavigationController
- 23. 以編程方式刪除Web.config設置
- 24. Drupal以編程方式刪除JavaScript
- 25. 以編程方式刪除UITableViewCell
- 26. 以編程方式刪除TFS分支
- 27. 以編程方式刪除Twitter帳戶
- 28. 以編程方式刪除IE6 cookies
- 29. 以編程方式刪除prestashop訂單
- 30. 以編程方式刪除datagrid列
這將是非常糟糕的,如果非用戶管理員cou ld刪除管理員正在使用的USB磁盤(或者甚至確定它是否在使用中)。所以限制似乎是合乎邏輯的 – MSalters 2009-01-12 11:55:12
不是。即使我可以通過通知區域中的「安全刪除硬件」圖標移除U盤,此代碼對我來說也不起作用(非管理員,「超級用戶」的成員)。它適用於「運行爲...」和管理員帳戶,雖然... – mghie 2009-01-12 12:08:35