问题
I am trying to write a small program in Delphi 2007 to access files off of a portable USB drive whenever it is plugged in to a Windows 7 machine. This drive does not show up as a standard drive letter though. It appears under Portable Devices in Windows Explorer. I have written the following code to enumerate all the items under 'Computer':
Procedure TfrmMain.ComputerChanged(Var Msg: TMessage);
Var
Enum: IEnumIDList;
Fetched: Longword;
Item: PItemIDList;
Path: String;
Computer: IShellFolder;
StrRet: TSTRRET;
Begin
Status('Computer changed... Checking folders.');
fDesktop.BindToObject(fCompPidl, Nil, IID_IShellFolder, Computer);
If Assigned(Computer) And
(Computer.EnumObjects(Self.Handle, SHCONTF_FOLDERS, Enum) = NOERROR) Then
Begin
While (Enum.Next(1, Item, Fetched) = NOERROR) Do
Begin
FillChar(StrRet, SizeOf(StrRet), #0);
Computer.GetDisplayNameOf(Item, SHGDN_FORADDRESSBAR or SHGDN_NORMAL, StrRet);
Path := StrRetToStr(StrRet, Item);
Status(Path);
End;
End;
End;
(note: the Status procedure just outputs a message to a TMemo.)
This is called whenever my application is notified of a change by the Windows shell subsystem. It enumerates all of the local drives and network drives but nothing else (iCloud Photos drive is missing as well).
Does anyone know how I can access the files on these virtual drives?
回答1:
You more than likely aren't initializing COM correctly. Your code will work as-is if you don't call CoInitializeEx or if you call it with a bad value, but the Portable Device drivers require apartment threading to work.
Based on your code, here's a sample app that works correctly and shows portable devices. If you comment out the CoInitializeEx
/CoUninitialize
calls or pass in COINIT_MULTITHREADED
instead it will still work, but it only shows the drives.
program ListMyComputer;
{$APPTYPE CONSOLE}
uses
ComObj, ShlObj, ShellApi, ShLwApi, ActiveX, Windows, SysUtils;
var
Enum: IEnumIDList;
Fetched: Longword;
CompPidl, Item: PItemIDList;
Path: PWideChar;
Desktop, Computer: IShellFolder;
StrRet: TSTRRET;
begin
CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
try
WriteLn('Computer changed... Checking folders.');
SHGetDesktopFolder(Desktop);
SHGetFolderLocation(0, CSIDL_DRIVES, 0, 0, CompPidl);
Desktop.BindToObject(CompPidl, Nil, IID_IShellFolder, Computer);
CoTaskMemFree(CompPidl);
If Assigned(Computer) And
(Computer.EnumObjects(0, SHCONTF_FOLDERS, Enum) = NOERROR) Then
Begin
While (Enum.Next(1, Item, Fetched) = NOERROR) Do
Begin
FillChar(StrRet, SizeOf(StrRet), #0);
Computer.GetDisplayNameOf(Item, SHGDN_FORADDRESSBAR or SHGDN_NORMAL, StrRet);
StrRetToStr(@StrRet, Item, Path);
WriteLn(Path);
CoTaskMemFree(Path);
End;
End;
WriteLn('Enumeration complete');
ReadLn;
finally
CoUninitialize
end;
end.
回答2:
Thanks to @SertacAkyuz for pointing out the need to use Windows Portable Device API which lead me to this Experts Exchange question discussing the same thing. Sinisa Vuk supplied an awesome code example to answer that question which I have linked (it's too long to embed) here with permission: http://pastebin.com/0hSWv5pE
来源:https://stackoverflow.com/questions/23962222/use-delphi-to-find-special-drives