在循环中调用CopyFile(),将其bFailIfExists 参数设置为TRUE,以便在CopyFile() 失败并出现ERROR_FILE_EXISTS 错误代码时使用新文件名重试。
例如:
procedure TsomeForm.UniFileUpload1Completed(Sender: TObject; AStream: TFileStream);
var
DestName : string;
DestFolder : string;
n : integer;
begin
DestFolder := UniServerModule.StartPath + 'files\' + UniMainModule.foldername + '\';
DestName := UniMainModule.FirstName + '_' + UniMainModule.LastName + '.pdf';
n := 0;
while not CopyFile(PChar(AStream.FileName), PChar(DestFolder + DestName), True) do
begin
if GetLastError() <> ERROR_FILE_EXISTS then
begin
// error handling...
Break;
end;
Inc(n);
DestName := UniMainModule.FirstName + '_' + UniMainModule.LastName + ' (' + IntToStr(n) + ').pdf';
end;
ModalResult := mrOk;
end;
但是,您应该让操作系统为您完成工作,而不是手动处理。特别是因为操作系统有自己的方式来重命名复制的文件,并且命名方案可以从一个操作系统版本更改(并且已经)到另一个版本。
不要使用CopyFile(),而是使用SHFileOperation(),它有一个FOF_RENAMEONCOLLISION 标志:
如果目标名称中已存在具有目标名称的文件,则在移动、复制或重命名操作中为正在操作的文件指定一个新名称。
例如:
uses
..., Winapi.ShellAPI;
procedure TsomeForm.UniFileUpload1Completed(Sender: TObject; AStream: TFileStream);
var
DestName : string;
DestFolder : string;
fo : TSHFileOpStruct;
begin
DestFolder := UniServerModule.StartPath + 'files\' + UniMainModule.foldername + '\';
DestName := DestFolder + UniMainModule.FirstName + '_' + UniMainModule.LastName + '.pdf';
ZeroMemory(@fo, SizeOf(fo));
fo.Wnd := Handle;
fo.wFunc := FO_COPY;
fo.pFrom := PChar(AStream.FileName+#0);
fo.pTo := PChar(DestName+#0);
fo.fFlags := FOF_SILENT or FOF_NOCONFIRMATION or FOF_NOERRORUI or FOF_NOCONFIRMMKDIR or FOF_RENAMEONCOLLISION;
if SHFileOperation(fo) <> 0 then
begin
// error handling...
end
else if fo.fAnyOperationsAborted then
begin
// abort handling ...
end;
ModalResult := mrOk;
end;
如果您需要知道操作系统为重命名的文件名选择了什么,还有一个FOF_WANTMAPPINGHANDLE 标志:
如果指定了 FOF_RENAMEONCOLLISION 并且重命名了任何文件,请将包含其旧名称和新名称的名称映射对象分配给 hNameMappings 成员。当不再需要此对象时,必须使用SHFreeNameMappings 释放它。
例如:
uses
..., Winapi.ShellAPI;
type
PHandleToMappings = ^THandleToMappings;
THandleToMappings = record
uNumberOfMappings: UINT; // Number of mappings in the array.
lpSHNameMappings: array[0..0] of PSHNAMEMAPPINGW; // array of pointers to mappings.
end;
procedure TsomeForm.UniFileUpload1Completed(Sender: TObject; AStream: TFileStream);
var
DestName : string;
DestFolder : string;
fo : TSHFileOpStruct;
pMappings : PHandleToMappings;
pMapping : PSHNAMEMAPPINGW;
begin
DestFolder := UniServerModule.StartPath + 'files\' + UniMainModule.foldername + '\';
DestName := DestFolder + UniMainModule.FirstName + '_' + UniMainModule.LastName + '.pdf';
ZeroMemory(@fo, SizeOf(fo));
fo.Wnd := Handle;
fo.wFunc := FO_COPY;
fo.pFrom := PChar(AStream.FileName+#0);
fo.pTo := PChar(DestName+#0);
fo.fFlags := FOF_SILENT or FOF_NOCONFIRMATION or FOF_NOERRORUI or FOF_NOCONFIRMMKDIR or FOF_RENAMEONCOLLISION or FOF_WANTMAPPINGHANDLE;
if SHFileOperation(fo) <> 0 then
begin
// error handling...
end else
begin
if fo.fAnyOperationsAborted then
begin
// abort handling...
end;
if fo.hNameMappings <> nil then
begin
try
pMappings := PHandleToMappings(fo.hNameMappings);
pMapping := pMappings^.lpSHNameMappings[0];
SetString(DestName, pMapping^.pszNewPath, pMapping^.cchNewPath);
finally
SHFreeNameMappings(THandle(fo.hNameMappings));
end;
// use DestName as needed...
end;
end;
ModalResult := mrOk;
end;
在 Vista 及更高版本上,您可以替代地使用 IFileOperation.CopyItem(),它还支持在碰撞时重命名项目。如果发生重命名冲突,可以使用IFileOperationProgressSink 回调来发现新文件名。
例如:
uses
..., Winapi.ActiveX, Winapi.ShlObj, System.Win.Comobj;
type
TMyCopyProgressSink = class(TInterfacedObject, IFileOperationProgressSink)
public
CopiedName: string;
function StartOperations: HResult; stdcall;
function FinishOperations(hrResult: HResult): HResult; stdcall;
function PreRenameItem(dwFlags: DWORD; const psiItem: IShellItem;
pszNewName: LPCWSTR): HResult; stdcall;
function PostRenameItem(dwFlags: DWORD; const psiItem: IShellItem;
pszNewName: LPCWSTR; hrRename: HResult; const psiNewlyCreated: IShellItem): HResult; stdcall;
function PreMoveItem(dwFlags: DWORD; const psiItem: IShellItem;
const psiDestinationFolder: IShellItem; pszNewName: LPCWSTR): HResult; stdcall;
function PostMoveItem(dwFlags: DWORD; const psiItem: IShellItem;
const psiDestinationFolder: IShellItem; pszNewName: LPCWSTR;
hrMove: HResult; const psiNewlyCreated: IShellItem): HResult; stdcall;
function PreCopyItem(dwFlags: DWORD; const psiItem: IShellItem;
const psiDestinationFolder: IShellItem; pszNewName: LPCWSTR): HResult; stdcall;
function PostCopyItem(dwFlags: DWORD; const psiItem: IShellItem;
const psiDestinationFolder: IShellItem; pszNewName: LPCWSTR;
hrCopy: HResult; const psiNewlyCreated: IShellItem): HResult; stdcall;
function PreDeleteItem(dwFlags: DWORD; const psiItem: IShellItem): HResult; stdcall;
function PostDeleteItem(dwFlags: DWORD; const psiItem: IShellItem; hrDelete: HResult;
const psiNewlyCreated: IShellItem): HResult; stdcall;
function PreNewItem(dwFlags: DWORD; const psiDestinationFolder: IShellItem;
pszNewName: LPCWSTR): HResult; stdcall;
function PostNewItem(dwFlags: DWORD; const psiDestinationFolder: IShellItem;
pszNewName: LPCWSTR; pszTemplateName: LPCWSTR; dwFileAttributes: DWORD;
hrNew: HResult; const psiNewItem: IShellItem): HResult; stdcall;
function UpdateProgress(iWorkTotal: UINT; iWorkSoFar: UINT): HResult; stdcall;
function ResetTimer: HResult; stdcall;
function PauseTimer: HResult; stdcall;
function ResumeTimer: HResult; stdcall;
end;
function TMyCopyProgressSink.StartOperations: HResult; stdcall;
begin
Result := S_OK;
end;
function TMyCopyProgressSink.FinishOperations(hrResult: HResult): HResult; stdcall;
begin
Result := S_OK;
end;
function TMyCopyProgressSink.PreRenameItem(dwFlags: DWORD; const psiItem: IShellItem;
pszNewName: LPCWSTR): HResult; stdcall;
begin
Result := S_OK;
end;
function TMyCopyProgressSink.PostRenameItem(dwFlags: DWORD; const psiItem: IShellItem;
pszNewName: LPCWSTR; hrRename: HResult; const psiNewlyCreated: IShellItem): HResult; stdcall;
begin
Result := S_OK;
end;
function TMyCopyProgressSink.PreMoveItem(dwFlags: DWORD; const psiItem: IShellItem;
const psiDestinationFolder: IShellItem; pszNewName: LPCWSTR): HResult; stdcall;
begin
Result := S_OK;
end;
function TMyCopyProgressSink.PostMoveItem(dwFlags: DWORD; const psiItem: IShellItem;
const psiDestinationFolder: IShellItem; pszNewName: LPCWSTR;
hrMove: HResult; const psiNewlyCreated: IShellItem): HResult; stdcall;
begin
Result := S_OK;
end;
function TMyCopyProgressSink.PreCopyItem(dwFlags: DWORD; const psiItem: IShellItem;
const psiDestinationFolder: IShellItem; pszNewName: LPCWSTR): HResult; stdcall;
begin
Result := S_OK;
end;
function TMyCopyProgressSink.PostCopyItem(dwFlags: DWORD; const psiItem: IShellItem;
const psiDestinationFolder: IShellItem; pszNewName: LPCWSTR;
hrCopy: HResult; const psiNewlyCreated: IShellItem): HResult; stdcall;
begin
CopiedName := pszNewName;
Result := S_OK;
end;
function TMyCopyProgressSink.PreDeleteItem(dwFlags: DWORD; const psiItem: IShellItem): HResult; stdcall;
begin
Result := S_OK;
end;
function TMyCopyProgressSink.PostDeleteItem(dwFlags: DWORD; const psiItem: IShellItem; hrDelete: HResult;
const psiNewlyCreated: IShellItem): HResult; stdcall;
begin
Result := S_OK;
end;
function TMyCopyProgressSink.PreNewItem(dwFlags: DWORD; const psiDestinationFolder: IShellItem;
pszNewName: LPCWSTR): HResult; stdcall;
begin
Result := S_OK;
end;
function TMyCopyProgressSink.PostNewItem(dwFlags: DWORD; const psiDestinationFolder: IShellItem;
pszNewName: LPCWSTR; pszTemplateName: LPCWSTR; dwFileAttributes: DWORD;
hrNew: HResult; const psiNewItem: IShellItem): HResult; stdcall;
begin
Result := S_OK;
end;
function TMyCopyProgressSink.UpdateProgress(iWorkTotal: UINT; iWorkSoFar: UINT): HResult; stdcall;
begin
Result := S_OK;
end;
function TMyCopyProgressSink.ResetTimer: HResult; stdcall;
begin
Result := S_OK;
end;
function TMyCopyProgressSink.PauseTimer: HResult; stdcall;
begin
Result := S_OK;
end;
function TMyCopyProgressSink.ResumeTimer: HResult; stdcall;
begin
Result := S_OK;
end;
procedure TsomeForm.UniFileUpload1Completed(Sender: TObject; AStream: TFileStream);
var
DestName : string;
DestFolder : string;
pfo : IFileOperation;
psiFrom : IShellItem;
psiTo : IShellItem;
Sink : IFileOperationProgressSink;
bAborted : BOOL;
begin
DestFolder := UniServerModule.StartPath + 'files\' + UniMainModule.foldername + '\';
DestName := UniMainModule.FirstName + '_' + UniMainModule.LastName + '.pdf';
try
OleCheck(SHCreateItemFromParsingName(PChar(AStream.FileName), nil, IShellItem, psiFrom));
OleCheck(SHCreateItemFromParsingName(PChar(DestFolder), nil, IShellItem, psiTo));
OleCheck(CoCreateInstance(CLSID_FileOperation, nil, CLSCTX_ALL, IFileOperation, pfo));
OleCheck(pfo.SetOperationFlags(FOF_SILENT or FOF_NOCONFIRMATION or FOF_NOCONFIRMMKDIR or FOF_NOERRORUI or FOF_RENAMEONCOLLISION or FOFX_PRESERVEFILEEXTENSIONS));
Sink := TMyCopyProgressSink.Create;
OleCheck(pfo.CopyItem(psiFrom, psiTo, PChar(DestName), Sink));
OleCheck(pfo.PerformOperations());
pfo.GetAnyOperationsAborted(bAborted);
if bAborted then
begin
// abort handling...
end;
DestName := TMyCopyProgressSink(Sink).CopiedName;
// use DestName as needed...
except
// error handling...
end;
end;