如何将文件复制到dll中的另一个路径?

how to copy file to another path in dll?

我正在尝试将文件从特定路径复制到另一个路径。

我定位的路径是CSIDL_COMMON_APPDATA,也就是程序数据,所以我试了:

CopyFile(PChar(AppFolder +'\needed.dll'), PChar(anotherfolder+'\needed.dll'), False);

但它不会将任何内容复制到目标路径。

所以我决定测试 EXE 应用程序中的代码,它可以正常复制文件。可能是什么问题呢?为什么我无法从 DLL 复制文件?

这是我的 Appfolder 路径:

function GetSpecialFolder(const CSIDL : integer) : String;
var
  RecPath : PWideChar;
begin
  RecPath := StrAlloc(MAX_PATH);
  try
    FillChar(RecPath^,MAX_PATH,0);
    if SHGetSpecialFolderPath(0,RecPath,CSIDL,false) then begin
      Result := RecPath;
    end else Result := '';
  finally
    StrDispose(RecPath);
  end;
end;

function AnotherFolder: string;
begin
  if IsWindowsVistaOrGreater then
  begin
    Result := GetSpecialFolder(CSIDL_COMMON_APPDATA)+'\applocation';
  end;
end;

function AppFolder: string;
begin
  if IsWindowsVistaOrGreater then
  begin
    Result := GetSpecialFolder(CSIDL_INTERNET_CACHE)+'\Low\applocation';
  end else
    Result := GetSpecialFolder(CSIDL_COMMON_APPDATA)+'\application';
    //Result := ExtractFilePath(Application.ExeName);
end;

添加实际代码

// dll form

function GetSpecialFolder(const CSIDL: integer): String;
var
  RecPath: PWideChar;
begin
  RecPath := StrAlloc(MAX_PATH);
  try
    FillChar(RecPath^, MAX_PATH, 0);
    if SHGetSpecialFolderPath(0, RecPath, CSIDL, false) then
    begin
      Result := RecPath;
    end
    else
      Result := '';
  finally
    StrDispose(RecPath);
  end;
end;

function AnotherFolder: string;
begin
  // Vista check removed
  Result := GetSpecialFolder(CSIDL_COMMON_APPDATA) + '\applocation';
end;

function AppFolder: string;
begin
  if IsWindowsVistaOrGreater then
  begin
    Result := GetSpecialFolder(CSIDL_INTERNET_CACHE) + '\Low\applocation';
  end
  else
    Result := GetSpecialFolder(CSIDL_COMMON_APPDATA) + '\application';
  // Result := ExtractFilePath(Application.ExeName);
end;

procedure Tform1.FormShow(Sender: TObject);
begin
  if IsWindowsVistaOrGreater then
  begin
    CopyFile(PChar(AppFolder + '\needed.dll'), PChar(AnotherFolder + '\needed.dll'), false);
    memo1.Lines.Add('Value : ' + IntTostr(GetLastError()));
  end;
end;

我的 dll 只有一种形式,当它显示时它显示从一个位置复制文件到另一个。

清单文件

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity
    type="win32"
    name="DelphiApplication"
    version="1.0.0.0" 
    processorArchitecture="*"/>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity
        type="win32"
        name="Microsoft.Windows.Common-Controls"
        version="6.0.0.0"
        publicKeyToken="6595b64144ccf1df"
        language="*"
        processorArchitecture="*"/>
    </dependentAssembly>
  </dependency>
</assembly>

项目dll源码

{$R apploadform.res' apploadform.rc'}

您没有检查错误。 Win32 API 函数 CopyFile return 是一个 Boolean 表示成功或失败。在你的情况下,我预测它会 return False。然后根据文档(您仔细阅读了吗?)指示您调用 GetLastError 以查明调用失败的原因。我预测这将产生 5 的值,即 ERROR_ACCESS_DENIED。这样做的原因是 CSIDL_COMMON_APPDATA 是安全的,不会被标准用户写入。您需要 运行 提升您的进程才能在那里写入。

当然,我在这里的猜测可能是错误的。在这种情况下,您仍会按照此建议找到答案。如果函数调用失败,错误代码会告诉您原因。

在我看来,这里要学习的重要一课是在调用 Win32 API 函数时进行错误检查的重要性。您必须密切关注文档,并遵循它规定的错误检查程序。

另一个教训是调试时应该简化。也许您的代码会产生无效路径。也许 GetSpecialFolder 坏了。也许您没有正确添加目录分隔符。调试的第 1 步是检查您传递给 CopyFile 的路径是否正确。是你做的吗?一旦你对此感到满意,你就可以深入挖掘。当然,如果那部分代码有效,那么我们就不需要在这里看到它。为了我们的利益,您可以替换该代码并将常量传递给 CopyFile

另一方面,您传递给 CopyFile 的路径可能不是您期望的那样。您应该进行一些调试以检查这些路径。你知道你传递给 CopyFile 的路径是什么吗?如果不是,您真的应该检查一下它们是否符合您的预期。

尝试更像这样的东西:

uses
  ..., SysUtils, Windows, ShlObj;

function GetSpecialFolder(const iFolder: Integer; OwnerWnd: HWND = 0): String;
var
  RecPath: array[0..MAX_PATH] of Char;
begin
  if SHGetSpecialFolderPath(OwnerWnd, RecPath, iFolder, False) then
    Result := IncludeTrailingPathDelimiter(RecPath)
  else
    Result := '';
end;

function AnotherFolder(OwnerWnd: HWND = 0): string;
begin
  Result := GetSpecialFolder(CSIDL_COMMON_APPDATA, OwnerWnd) + 'applocation' + PathDelim;
end;

function AppFolder(OwnerWnd: HWND = 0): string;
begin
  Result := GetSpecialFolder(CSIDL_COMMON_APPDATA, OwnerWnd) + 'application' + PathDelim;
end;

procedure TForm1.FormShow(Sender: TObject);
var
  Source, Target: String;
  ErrCode: DWORD;
begin
  Source := AppFolder(Handle) + 'needed.dll';
  Target := AnotherFolder(Handle) + 'needed.dll';

  Memo1.Lines.Add('Source: ' + Source);
  Memo1.Lines.Add('Target: ' + Target);

  if CopyFile(PChar(Source), PChar(Target), false) then
  begin
    Memo1.Lines.Add('Copied OK');
  end else
  begin
    ErrCode := GetLastError;
    Memo1.Lines.Add('Unable to copy! Error ' + IntTostr(ErrCode) + '. ' + SysErrorMessage(ErrCode));
  end;
end;