DelphiStudent
DelphiStudent

Reputation: 384

how to copy file to another path in dll?

I am trying to copy a file from a specific path to another.

The path that I targeted is CSIDL_COMMON_APPDATA, which is Program Data, so I tried:

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

but it doesn't copy anything to the target path.

So I deiced to test the code inside an EXE application and it copies the file normally. What could be the problem? Why I am unable to copy files from a DLL?

Here is my Appfolder path:

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;

Adding Actual code

// 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;

my dll only have 1 form that when its shows it show copy file from location to another .

manifest file

<?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>

project dll source

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

Upvotes: 0

Views: 871

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 596592

Try something more like this instead:

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;

Upvotes: 1

David Heffernan
David Heffernan

Reputation: 613013

You aren't checking for errors. The Win32 API function CopyFile returns a Boolean indicating success or failure. In your case I predict it will return False. You are then directed by the documentation (did you read it closely?) to call GetLastError to find out why the call failed. I predict that will yield a value of 5 which is ERROR_ACCESS_DENIED. The reason for this is that CSIDL_COMMON_APPDATA is secured against writing from standard user. You'd need to run your process elevated to be able to write there.

Of course, my guesswork here could be wrong. In that case, you will still be led to the answer by following this advice. If the function calls fail, the error codes will give you the reason why.

The big lesson to learn here, in my opinion, is the importance of error checking when calling Win32 API functions. You must pay close attention to the documentation, and follow the error checking procedures that it prescribes.

The other lesson is that when debugging you should simplify. Perhaps your code produces invalid paths. Perhaps GetSpecialFolder is broken. Perhaps you fail to add directory separators correctly. Step 1 of debugging is to check that the paths you pass to CopyFile are correct. Did you do that? Once you are happy with that then you can dig deeper. Certainly if that part of the code works then we don't need to see it here. You could have replaced that code and passed constants to CopyFile, for our benefit.

On the other hand, perhaps the paths that you pass to CopyFile are not what you expect them to be. You should have done some debugging to check those paths. Do you know what paths you pass to CopyFile? If not, you really should check they are as you expect.

Upvotes: 2

Related Questions