Reputation: 384
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
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
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