delphi - rastro - programa para eliminar virus de acceso directo
¿Cómo puedo eliminar un disco flash USB programáticamente usando Delphi? (3)
Esto no expulsa la unidad, pero vacía los búferes de la unidad y hace que sea seguro eliminarla. Requiere derechos administrativos bajo Vista y superior (y XP si se ejecuta como un usuario con derechos limitados, IIRC). Probablemente debería intentarlo ... para asegurarse de que se llama a CloseHandle
; Lo dejo como un ejercicio para el lector, ya que el formato de código es ajustado aquí sin desplazamiento horizontal. :-)
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;
Uso de muestra:
if FlushUSBDrive(''G:'') then
ShowMessage(''Safe to remove USB drive G:'')
else
ShowMessage(''Flush of drive G: failed!'' +
SysErrorMessage(GetLastError()));
¿Cómo puedo detectar y eliminar un disco flash USB programáticamente usando Delphi?
He visto algunos de los ejemplos en este sitio web, pero carecen de una explicación clara sobre cómo hacerlo.
¡Por favor los ejemplos realmente ayudarán!
La clave para eliminar una unidad USB es utilizar la función CM_Request_Device_Eject ,
Consulte esta aplicación de delphi de muestra, basada en este artículo How to Prepare a USB Drive for Safe Removal
y que utiliza la 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.
Esta es una traducción rápida y sucia de este código de muestra para eliminar una unidad, de support.microsoft.com. Sin embargo, funciona solo para usuarios con permisos de administrador en mi sistema.
Para obtener más información sobre cómo trabajar con dispositivos USB, en general, siga el enlace en esta respuesta por 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;