使用Delphi查找特定驱动器

4

我正在尝试在Delphi 2007中编写一个小程序,以便在Windows 7计算机插入可移动USB驱动器时可以访问其中的文件。但是,该驱动器不会显示为标准驱动器字母。它出现在Windows资源管理器中的“便携式设备”下。我已经编写了以下代码来枚举“计算机”下的所有项目:

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;

(注意:状态过程只是向TMemo输出消息。)
每当Windows shell子系统通知我的应用程序发生更改时,就会调用此过程。 它枚举所有本地驱动器和网络驱动器,但仅限于这些(iCloud照片驱动器也不包括在内)。
有人知道如何访问这些虚拟驱动器上的文件吗?

1
http://msdn.microsoft.com/en-us/library/dd388998%28v=vs.85%29.aspx - Sertac Akyuz
啊...太好了。又要学习一个完整的API,而不是只使用现有的shell函数。好吧,一旦我了解了Windows便携式设备API,我找到了一个人编写了一个完整的示例程序,正好做我想做的事情。不幸的是,它在Experts Exchange上,你需要一个账户才能看到它。对于那些有账户的人,请在这里查看:www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_28303837.html - Caynadian
@SertacAkyuz:您能够将您的评论发布为答案,以便我可以标记此问题为已关闭吗? - Caynadian
“可移动设备”绝对可以通过 shell 命名空间访问。我们使用 Plasmatech 控件,它们不使用 PortableDevice API,但仍然能够看到这些设备。 - Zoë Peterson
@CraigPeterson:我不确定这对我是否有效。A)它是商业软件包,B)它看起来只是复制Windows资源管理器。它是否允许您完全无需任何GUI操作就可以访问文件? - Caynadian
显示剩余5条评论
2个回答

4
你很可能没有正确初始化COM。如果你不调用CoInitializeEx或者以错误的值调用它,你的代码将会正常工作,但是便携设备驱动程序需要公寓线程才能工作。
根据你的代码,这里有一个可以正常工作并显示可移动设备的示例应用程序。如果你注释掉CoInitializeEx/CoUninitialize调用或者传入COINIT_MULTITHREADED,它仍然可以工作,但只会显示驱动器。
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.

@Ken:糟糕!已修复。我的原始版本泄露了所有内容,当我在整理发布时,尝试将其加入其中。 - Zoë Peterson
没问题。我已经点赞了,因为通过小修复它可以工作。我只是想提一下。 :-) - Ken White
@CraigPeterson:太酷了!谢谢。我确实觉得正常的 shell 控件无法看到便携设备很奇怪。我从未考虑过初始化的问题,因为一切都可以正常工作。 - Caynadian

1

感谢 @SertacAkyuz 指出需要使用Windows Portable Device API,这使我想到了 这个Experts Exchange问题,讨论了同样的事情。Sinisa Vuk 提供了一个很棒的代码示例来回答那个问题,我已经获得了链接的许可并在此处进行了链接(它太长了无法嵌入):http://pastebin.com/0hSWv5pE


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接