procedure TfMain.copyFilesToClipboard(FileList: string); const C_UNABLE_TO_ALLOCATE_MEMORY = 'Unable to allocate memory.'; C_UNABLE_TO_ACCESS_MEMORY = 'Unable to access allocated memory.'; var DropFiles: PDropFiles; hGlobal: THandle; iLen: Integer; begin iLen := Length(FileList); hGlobal := GlobalAlloc(GMEM_SHARE or GMEM_MOVEABLE or GMEM_ZEROINIT,SizeOf(TDropFiles) + ((iLen + 2) * SizeOf(Char))); if (hGlobal = 0) then raise Exception.Create(C_UNABLE_TO_ALLOCATE_MEMORY); try DropFiles := GlobalLock(hGlobal); if (DropFiles = nil) then raise Exception.Create(C_UNABLE_TO_ACCESS_MEMORY); try DropFiles^.pFiles := SizeOf(TDropFiles); DropFiles^.fWide := True; if FileList <> '' then Move(FileList[1],(PByte(DropFiles) + SizeOf(TDropFiles))^,iLen * SizeOf(Char)); finally GlobalUnlock(hGlobal); end; Clipboard.SetAsHandle(CF_HDROP,hGlobal); except GlobalFree(hGlobal); end; end;
(这似乎是互联网上流行的一段代码)
使用我的应用程序,一旦文件被复制到剪贴板,我可以使用Windows资源管理器将它们粘贴到每个其他文件夹,除了文件最初来自的文件夹!我期待它的行为就像一个普通的Windows副本(即粘贴它应该创建一个后缀为’-copy’的文件),但这似乎不起作用.有线索吗?
解决方法
如果所有文件都来自同一源文件夹,则可以检索源文件夹的IShellFolder并查询其中各个文件的子PIDL,然后使用IShellFolder.GetUIObjectOf()获取表示文件的IDataObject.然后使用OleSetClipboard()将该对象放在剪贴板上.例如:
uses System.Classes,Winapi.Windows,Winapi.ActiveX,Winapi.Shlobj,Winapi.ShellAPI,System.Win.ComObj; procedure copyFilesToClipboard(const Folder: string; FileNames: TStrings); var SF: IShellFolder; PidlFolder: PItemIDList; PidlChildren: array of PItemIDList; Eaten: UINT; Attrs: DWORD; Obj: IDataObject; I: Integer; begin if (Folder = '') or (FileNames = nil) or (FileNames.Count = 0) then Exit; OleCheck(SHParsedisplayName(PChar(Folder),nil,PidlFolder,Attrs)); try OleCheck(SHBindToObject(nil,IShellFolder,Pointer(SF))); finally CoTaskMemFree(PidlFolder); end; SetLength(PidlChildren,FileNames.Count); for I := Low(PidlChildren) to High(PidlChildren) do PidlChildren[i] := nil; try for I := 0 to FileNames.Count-1 do OleCheck(SF.ParsedisplayName(0,PChar(FileNames[i]),Eaten,PidlChildren[i],Attrs)); OleCheck(SF.GetUIObjectOf(0,FileNames.Count,PIdlChildren[0],IDataObject,obj)); finally for I := Low(PidlChildren) to High(PidlChildren) do begin if PidlChildren[i] <> nil then CoTaskMemFree(PidlChildren[i]); end; end; OleCheck(OleSetClipboard(obj)); OleCheck(OleFlushClipboard); end;
更新:如果文件位于不同的源文件夹中,则可以使用CFSTR_SHELLIDLIST
格式:
uses System.Classes,System.SysUtils,System.Win.ComObj,Vcl.Clipbrd; {$POINTERMATH ON} function HIDA_GetPIDLFolder(pida: PIDA): LPITEMIDLIST; begin Result := LPITEMIDLIST(LPBYTE(pida) + pida.aoffset[0]); end; function HIDA_GetPIDLItem(pida: PIDA; idx: Integer): LPITEMIDLIST; begin Result := LPITEMIDLIST(LPBYTE(pida) + (PUINT(@pida.aoffset[0])+(1+idx))^); end; var CF_SHELLIDLIST: UINT = 0; type CidaPidlInfo = record Pidl: PItemIDList; PidlOffset: UINT; PidlSize: UINT; end; procedure copyFilesToClipboard(FileNames: TStrings); var PidlInfo: array of CidaPidlInfo; Attrs,AllocSize: DWORD; gmem: THandle; ida: PIDA; I: Integer; begin if (FileNames = nil) or (FileNames.Count = 0) or (CF_SHELLIDLIST = 0) then Exit; SetLength(PidlInfo,FileNames.Count); for I := Low(PidlInfo) to High(PidlInfo) do PidlInfo[I].Pidl := nil; try AllocSize := SizeOf(CIDA)+(SizeOf(UINT)*FileNames.Count)+SizeOf(Word); for I := 0 to FileNames.Count-1 do begin OleCheck(SHParsedisplayName(PChar(FileNames[I]),PidlInfo[I].Pidl,Attrs)); PidlInfo[I].PidlOffset := AllocSize; PidlInfo[I].PidlSize := ILGetSize(PidlInfo[I].Pidl); Inc(AllocSize,PidlInfo[I].PidlSize); end; gmem := GlobalAlloc(GMEM_MOVEABLE,AllocSize); if gmem = 0 then RaiseLastOSError; try ida := PIDA(GlobalLock(gmem)); if ida = nil then RaiseLastOSError; try ida.cidl := FileNames.Count; ida.aoffset[0] := SizeOf(CIDA)+(SizeOf(UINT)*FileNames.Count); HIDA_GetPIDLFolder(ida).mkid.cb := 0; for I := 0 to FileNames.Count-1 do begin ida.aoffset[1+I] := PidlInfo[I].PidlOffset; Move(PidlInfo[I].Pidl^,HIDA_GetPIDLItem(ida,I)^,PidlInfo[I].PidlSize); end; finally GlobalUnlock(gmem); end; Clipboard.SetAsHandle(CF_SHELLIDLIST,gmem); except GlobalFree(gmem); raise; end; finally for I := Low(PidlInfo) to High(PidlInfo) do CoTaskMemFree(PidlInfo[I].Pidl); end; end; initialization CF_SHELLIDLIST := RegisterClipboardFormat(CFSTR_SHELLIDLIST);
或者:
procedure copyFilesToClipboard(FileNames: TStrings); var Pidls: array of PItemIdList; Attrs: DWORD; I: Integer; obj: IDataObject; begin if (FileNames = nil) or (FileNames.Count = 0) then Exit; SetLength(Pidls,FileNames.Count); for I := Low(Pidls) to High(Pidls) do Pidls[I] := nil; try for I := 0 to FileNames.Count-1 do OleCheck(SHParsedisplayName(PChar(FileNames[I]),Pidls[I],Attrs)); OleCheck(CIDLData_CreateFromIDArray(nil,PItemIDList(Pidls),obj)); finally for I := Low(Pidls) to High(Pidls) do CoTaskMemFree(Pidls[I]); end; OleCheck(OleSetClipboard(obj)); OleCheck(OleFlushClipboard); end;
但是,我发现Windows资源管理器有时但不总是允许将CFSTR_SHELLIDLIST粘贴到引用文件的源文件夹中.我不知道阻止Windows资源管理器粘贴的标准是什么.也许是某种权限问题?
你应该听取微软的建议:
Handling Shell Data Transfer Scenarios
Include as many formats as you can support. You generally do not kNow where the data object will be dropped. This practice improves the odds that the data object will contain a format that the drop target can accept.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。