//new directory if the operation requires one to be created.
SHFileOperation有一个不错的功能:如果目标文件夹不存在,该函数将创建它!FOF_NOCONFIRMMKDIR标志确保Windows不会向用户显示任何对话框,询问是否应创建目标文件夹。
IFileOperation
这是使用IFileOperation接口复制文件的方法
uses ActiveX, ComObj, ShlObj;;
function CopyFileIFileOperation(const srcFile, destFile : string) : boolean;
//works on Windows >= Vista and 2008 server
var
r : HRESULT;
fileOp: IFileOperation;
siSrcFile: IShellItem;
siDestFolder: IShellItem;
destFileFolder, destFileName : string;
begin
result := false;
destFileFolder := ExtractFileDir(destFile);
destFileName := ExtractFileName(destFile);
//init com
r := CoInitializeEx(nil, COINIT_APARTMENTTHREADED or COINIT_DISABLE_OLE1DDE);
if Succeeded(r) then
begin
//create IFileOperation interface
r := CoCreateInstance(CLSID_FileOperation, nil, CLSCTX_ALL, IFileOperation, fileOp);
if Succeeded(r) then
begin
//set operations flags
r := fileOp.SetOperationFlags(FOF_NOCONFIRMATION OR FOFX_NOMINIMIZEBOX);
if Succeeded(r) then
begin
//get source shell item
r := SHCreateItemFromParsingName(PChar(srcFile), nil, IShellItem, siSrcFile);
if Succeeded(r) then
begin
//get destination folder shell item
r := SHCreateItemFromParsingName(PChar(destFileFolder), nil, IShellItem, siDestFolder);
//add copy operation
if Succeeded(r) then r := fileOp.CopyItem(siSrcFile, siDestFolder, PChar(destFileName), nil);
end;
//execute
if Succeeded(r) then r := fileOp.PerformOperations;
result := Succeeded(r);
OleCheck(r);
end;
end;
CoUninitialize;
end;
end;
请注意,PerformActions方法执行通过调用单个方法(如CopyItem(s),DeleteItem(s)等)添加的操作。
但是,使用IFIleOperation存在一个大问题:在复制操作中,如果目标文件夹不存在,该操作将失败!
即使使用SetOperationFlags方法设置了FOF_NOCONFIRMMKDIR,也是如此。
请注意第二次使用SHCreateItemFromParsingName的行:
//get destination folder shell item
r := SHCreateItemFromParsingName(PChar(destFileFolder), nil, IShellItem, siDestFolder);
它创建并初始化目标文件夹的外壳程序项,如果该文件夹不存在,则调用将失败。
IFileOperation.CopyItem +强制目标目录
该解决方案将在第二个参数中找到:const pbc:IBindCtx; 这是指向绑定上下文的指针,该绑定上下文用于将参数传递给解析函数(在本例中为SHCreateItemFromParsingName)。我们可以使用绑定上下文来强制SHCreateItemFromParsingName不查询文件系统-而是仅使用我们提供的内容。我们将提供WIN32_FIND_DATA结构(指定FILE_ATTRIBUTE_DIRECTORY)和实现IFileSystemBindData接口的对象实例的复杂混合。
这是完整的代码,以及所需接口的实现。
uses ActiveX, ComObj, ShlObj;
function CopyFileIFileOperationForceDirectories(const srcFile, destFile : string) : boolean;
//works on Windows >= Vista and 2008 server
var
r : HRESULT;
fileOp: IFileOperation;
siSrcFile: IShellItem;
siDestFolder: IShellItem;
destFileFolder, destFileName : string;
pbc : IBindCtx;
w32fd : TWin32FindData;
ifs : TFileSystemBindData;
begin
result := false;
destFileFolder := ExtractFileDir(destFile);
destFileName := ExtractFileName(destFile);
//init com
r := CoInitializeEx(nil, COINIT_APARTMENTTHREADED or COINIT_DISABLE_OLE1DDE);
if Succeeded(r) then
begin
//create IFileOperation interface
r := CoCreateInstance(CLSID_FileOperation, nil, CLSCTX_ALL, IFileOperation, fileOp);
if Succeeded(r) then
begin
//set operations flags
r := fileOp.SetOperationFlags(FOF_NOCONFIRMATION OR FOFX_NOMINIMIZEBOX);
if Succeeded(r) then
begin
//get source shell item
r := SHCreateItemFromParsingName(PChar(srcFile), nil, IShellItem, siSrcFile);
if Succeeded(r) then
begin
//create binding context to pretend there is a folder there
if NOT DirectoryExists(destFileFolder) then
begin
ZeroMemory(@w32fd, Sizeof(TWin32FindData));
w32fd.dwFileAttributes := FILE_ATTRIBUTE_DIRECTORY;
ifs := TFileSystemBindData.Create;
ifs.SetFindData(w32fd);
r := CreateBindCtx(0, pbc);
r := pbc.RegisterObjectParam(STR_FILE_SYS_BIND_DATA, ifs);
end
else
pbc := nil;
//get destination folder shell item
r := SHCreateItemFromParsingName(PChar(destFileFolder), pbc, IShellItem, siDestFolder);
//add copy operation
if Succeeded(r) then r := fileOp.CopyItem(siSrcFile, siDestFolder, PChar(destFileName), nil);
end;
//execute
if Succeeded(r) then r := fileOp.PerformOperations;
result := Succeeded(r);
OleCheck(r);
end;
end;
CoUninitialize;
end;
end;
这是TFileSystemBindData,IFileSystemBindData 接口的实现:
type
TFileSystemBindData = class (TInterfacedObject, IFileSystemBindData)
fw32fd: TWin32FindData;
function SetFindData(var w32fd: TWin32FindData): HRESULT; stdcall;
function GetFindData(var w32fd: TWin32FindData): HRESULT; stdcall;
end;
...
function TFileSystemBindData.GetFindData(var w32fd: TWin32FindData): HRESULT;
begin
w32fd:= fw32fd;
Result := S_OK;
end;
function TFileSystemBindData.SetFindData(var w32fd: TWin32FindData): HRESULT;
begin
fw32fd := w32fd;
Result := S_OK;
end;
最后,用法如下:
//works even if "d:\f1\f2\f3\" does not exist!
CopyFileIFileOperationForceDirectories('c:\somefile.png', 'd:\f1\f2\f3\copiedfile.png');
就是这样,现在您可以使用IFIleOperation而不是SHFileOperation。当然,您需要确保您的代码至少在Windows Vista或Windows Server 2008上运行。您可以使用TOSVersion来检查代码在其上运行的操作系统。