I'm trying to verify whether a resource exists in another Delphi application, the problem is that only know how to do it locally, I mean in the application itself.
Source:
function exists_resource(name: string): boolean;
begin
if (FindResource(HInstance, PChar(name), RT_RCDATA) <> 0) then
begin
Result := True;
end
else
begin
Result := False;
end;
end;
As I can verify this in a different application?
Use LoadLibraryEx to load the external file.
function exists_resource(const name, filename: string): boolean;
var
HInst: THandle;
begin
HInst:= LoadLibraryEx(PChar(Filename), 0, LOAD_LIBRARY_AS_DATAFILE or LOAD_LIBRARY_AS_IMAGE_RESOURCE);
//Win32Check(HInst); //Uncomment if you want to generate errors.
if HInst = 0 then exit(false);
try
Exit((FindResource(HInst, PChar(name), RT_RCDATA) <> 0));
finally
FreeLibrary(Hinst);
end; {try}
end;
Note that using LoadLibraryEx in this way will work for loading both dll's and exe's.
Related
In my application, all printers are listed using printer.printers command. This lists only printer names. Upon selection, it is saved in the Database.
Later on, this printer name is assigned using the AssignFilefunction to a text file.
And printing is done using Rewrite function.
If I save the selected printer as \\PCname\printer name in the database and then use it with Rewrite function then it works.
But if we save only printer name in the database then printing is not happening.
Is it necessary to save \\PCname\printer name path? or Is there any other solution.
Anotherway than printing using the Pascal file access functions is to use the Windows API for the spooler.
function PrintWithSpooler(const Name: string; const Data: AnsiString): integer;
var
hPrinter: THandle;
DocInfo: TDocInfo1;
bSuccess: boolean;
dwBytesWritten: DWORD;
begin
result := S_OK;
bSuccess := false;
DocInfo.pOutputFile := nil;
DocInfo.pDatatype := 'RAW';
DocInfo.pDocName := 'Label';
if OpenPrinter(PChar(Trim(Name)), hPrinter, nil) then
begin
try
if StartDocPrinter(hPrinter, 1, #DocInfo) > 0 then
begin
try
if StartPagePrinter(hPrinter) then
begin
try
bSuccess := WritePrinter(hPrinter, Pointer(Data), Length(Data), dwBytesWritten);
finally
EndPagePrinter(hPrinter);
end;
end;
finally
EndDocPrinter(hPrinter);
end;
end;
finally
ClosePrinter(hPrinter);
end;
end;
if not bSuccess then
begin
result := GetLastError;
// in case there was no error from GetLastError
if result = S_OK then
result := S_FALSE;
end;
end;
Obviously, you need PCname. You can save it in the database as you said but it will be a problem if the database is used from several PC, the only save printer name in the database and add PCname on the PC using the printer. You can get PCname using GetComputerName
I am using Kaspersky Internet Security 2018. But when I compile my Delphi application with these codes, my anti-virus application will remove the compiled exe:
function BrowseForFolder(var dpFolder: String; dpTitle: String): Boolean;
var
dpBrowseInfo: TBrowseInfo;
dpDisplayName: array[0..255] of Char;
dpItemIDList: PItemIDList;
begin
FillChar(dpBrowseInfo, sizeof(dpBrowseInfo), #0);
with dpBrowseInfo do begin
hwndOwner := Application.Handle;
pszDisplayName := #dpDisplayName[0];
lpszTitle := PChar(dpTitle);
dpItemIDList := SHBrowseForFolder(dpBrowseInfo);
ulFlags := BIF_RETURNONLYFSDIRS and BIF_VALIDATE;
end;
if Assigned(dpItemIDList) then begin
if SHGetPathFromIDList(dpItemIDList, dpDisplayName) then begin
dpFolder := dpDisplayName;
Result := True;
end else begin
Result := False;
end;
end;
end;
What should I do to pop up "Browse folder" menu other than adding my app to whitelist?
I have tried every "Browse Folder" dialog types and I have realized that my anti-virus software only allows Vcl.FileCtrl.TSelectDirExtOpt (also it hates other FileCtrl dialogs).
So, I fixed my issue myself like this:
procedure TForm1.gözatDüğmesiClick(Sender: TObject);
begin
if not (menü4CB = '1') then begin
if not (SelectDirectory('Kurulum programının yedekleneceği klasörü seçin',
GetSpecialFolderPathFromCSIDL($0011), adres, [sdNewFolder, sdNewUI], nil) = False) then begin
adresÇubuğu.Text := adres;
end else begin
end;
end;
end;
Thank Remy Lebeau anyway for caring about me.
Using the code below I try to set a value in the HKEY_LOCAL_MACHINE section of registry but I get an error 'Failed to set data for.....'
If I use HKEY_CURRENT_USER there is no problem.
What might I be missing here.
(The code is not complete, but I think it is the important parts of it)
type
TTypWinBits = (Bit32, Bit64);
function WinBits: TTypWinBits;
type
TIsWow64Process = function(Handle:THandle; var IsWow64 : BOOL) : BOOL; stdcall;
var
hKernel32 : Integer;
IsWow64Process : TIsWow64Process;
IsWow64 : BOOL;
begin
Result := Bit32;
hKernel32 := LoadLibrary('kernel32.dll');
if (hKernel32 = 0) then RaiseLastOSError;
#IsWow64Process := GetProcAddress(hkernel32, 'IsWow64Process');
if Assigned(IsWow64Process) then
begin
IsWow64 := False;
if (IsWow64Process(GetCurrentProcess, IsWow64)) then
Result := Bit64
else
RaiseLastOSError;
end;
FreeLibrary(hKernel32);
end;
function TFastRegistry.CreateConnection: TRegistry;
begin
Result := TRegistry.Create;
try
case WinBits of
Bit32: Result := TRegistry.Create;
Bit64: Result := TRegistry.Create(KEY_WRITE OR KEY_WOW64_64KEY);
end;
except
on E: exception do
Result := nil;
end;
end;
procedure TFastRegistry.RunAdd(aDesc, aName: string);
var
Reg: TRegistry;
sRegKey: String;
begin
sRegKey := 'Software\Microsoft\Windows\CurrentVersion\Run';
Reg := CreateConnection;
with Reg do
begin
try
RootKey := HKEY_LOCAL_MACHINE;
if not KeyExists(sRegKey) then
OpenKey(sRegKey, True)
else
OpenKey(sRegKey, False);
WriteString(aDesc, aName);
finally
CloseKey;
Free;
end;
end;
end;
A program requires elevated privileges to write to the local-machine key. Without that, functions will fail, as you've observed. If your program is supposed to be an administrative tool, then use a manifest file so the OS will prompt for permission. If you don't need that, then write to the current-user key instead so it doesn't affect all accounts on the system.
You just need to release the handle by the "Free" and for the next entry in the register to recreate it, and not keep it permanently set up and open and close them through OpenKey and CloseKey! It looks like a bug :-)
I'm working on something which dynamically loads specially formulated DLL's. I need to be able to check the DLL and make sure all the expected functions exist before I consider using this DLL. If it's missing some certain functions, I should not try to load it. I know I could attempt to call one of the functions and see if there's an exception, but I would see errors in debug mode.
How should I go about checking a DLL if a function exists? I'd like to check it before I load it (using LoadLibrary) but I guess it's OK if I have to load it to perform this check too.
UPDATE
I've accepted David's answer below, and thought I'd post my final code to show the whole process. I turned it into a function returning a Bool, whether it succeeded or not, cleaned the code a bit, and added another function at the bottom which uses this one to check each name one by one.
I decided to use this method instead of reading GetProcAddress because it will help me in the future with other things.
type
PIMAGE_NT_HEADERS = ^IMAGE_NT_HEADERS;
PIMAGE_EXPORT_DIRECTORY = ^IMAGE_EXPORT_DIRECTORY;
function ImageNtHeader(Base: Pointer): PIMAGE_NT_HEADERS; stdcall;
external 'dbghelp.dll';
function ImageRvaToVa(NtHeaders: Pointer; Base: Pointer; Rva: ULONG;
LastRvaSection: Pointer): Pointer; stdcall; external 'dbghelp.dll';
function ExportedFunctionNames(const ImageName: string; NamesList: TStrings): Bool;
var
i: Integer;
FileHandle: THandle;
ImageHandle: THandle;
ImagePointer: Pointer;
Header: PIMAGE_NT_HEADERS;
ExportTable: PIMAGE_EXPORT_DIRECTORY;
NamesPointer: Pointer;
Names: PAnsiChar;
NamesDataLeft: Integer;
begin
Result:= False;
NamesList.Clear;
FileHandle:= CreateFile(PChar(ImageName), GENERIC_READ, FILE_SHARE_READ,
nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if FileHandle = INVALID_HANDLE_VALUE then Exit;
try
ImageHandle:= CreateFileMapping(FileHandle, nil, PAGE_READONLY, 0, 0, nil);
if ImageHandle = 0 then Exit;
try
ImagePointer:= MapViewOfFile(ImageHandle, FILE_MAP_READ, 0, 0, 0);
if not Assigned(ImagePointer) then Exit;
try
Header:= ImageNtHeader(ImagePointer);
if not Assigned(Header) then Exit;
if Header.Signature <> $00004550 then Exit; // "PE\0\0" as a DWORD.
ExportTable:= ImageRvaToVa(Header, ImagePointer,
Header.OptionalHeader.DataDirectory[0].VirtualAddress, nil);
if not Assigned(ExportTable) then Exit;
NamesPointer:= ImageRvaToVa(Header, ImagePointer,
Cardinal(ExportTable.AddressOfNames), nil);
if not Assigned(NamesPointer) then Exit;
Names:= ImageRvaToVa(Header, ImagePointer, Cardinal(NamesPointer^), nil);
if not Assigned(Names) then Exit;
NamesDataLeft:= Header.OptionalHeader.DataDirectory[0].Size;
for i:= 0 to ExportTable.NumberOfNames - 1 do begin
NamesList.Add(Names);
while (Names^ <> chr(0)) and (NamesDataLeft > 0) do begin
Inc(Names);
Dec(NamesDataLeft);
end;
Inc(Names);
end;
Result:= True;
finally
UnmapViewOfFile(ImagePointer);
end;
finally
CloseHandle(ImageHandle);
end;
finally
CloseHandle(FileHandle);
end;
end;
function IsMyDLL(const Filename: String): Bool;
var
H: THandle;
L: TStringList;
function InList(const Func: String): Bool;
begin
Result:= L.IndexOf(Func) >= 0;
end;
begin
Result:= False;
L:= TStringList.Create;
try
if ExportedFunctionNames(Filename, L) then begin
Result:=//Names of functions which need to exist
InList('GetName') and
InList('GetDescription') and
InList('GetVersion') and
InList('Start') and
InList('Stop');
end;
finally
L.Free;
end;
end;
You have to use LoadLibrary, and then use GetProcAddress for each function you want to check existence for. There's really no other reasonable choice (unless there are specific reasons you need to avoid`LoadLibrary). Since your intent seems to be just to check to see if the functions are present and nothing more, LoadLibrary and GetProcAddress are the simplest means to do so; you can do all of the work in very few lines of code, and error checking is extremely simple and straightforward.
If you are in control of the DLLs and you don't want to load them in order to check capability, then you could use the version resource to indicate capability. This would require the host app to have knowledge of what was the minimum supported version for each optional DLL feature. You can read the version resource cheaply without loading the DLL.
It is perfectly possible, and rather simple, to obtain the list of functions exported by a DLL with loading it into your process with LoadLibrary. The dbghelp.dll system library provides services to do that. However, I suspect that is overkill for your situation.
If it is not a problem to load and unload the DLL then GetProcAddress is probably the preferred solution. If there is some reason why you need to avoid loading the DLL in order to check capability, use the version resource to infer capability. If you need to do this with legacy DLLs that do not have a meaningful version resource then use dbghelp.dll to find the exported functions.
For the sake of completeness, here is some code to read all the exported symbols from a DLL, without loading it with LoadLibrary.
type
PIMAGE_NT_HEADERS = ^IMAGE_NT_HEADERS;
PIMAGE_EXPORT_DIRECTORY = ^IMAGE_EXPORT_DIRECTORY;
function ImageNtHeader(Base: Pointer): PIMAGE_NT_HEADERS; stdcall; external 'dbghelp.dll';
function ImageRvaToVa(NtHeaders: Pointer; Base: Pointer; Rva: ULONG; LastRvaSection: Pointer): Pointer; stdcall; external 'dbghelp.dll';
procedure ImageExportedFunctionNames(const ImageName: string; NamesList: TStrings);
var
i: Integer;
FileHandle: THandle;
ImageHandle: THandle;
ImagePointer: Pointer;
Header: PIMAGE_NT_HEADERS;
ExportTable: PIMAGE_EXPORT_DIRECTORY;
NamesPointer: Pointer;
Names: PAnsiChar;
NamesDataLeft: Integer;
begin
//NOTE: our policy in this procedure is to exit upon any failure and return an empty list
NamesList.Clear;
FileHandle := CreateFile(
PChar(ImageName),
GENERIC_READ,
FILE_SHARE_READ,
nil,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0
);
if FileHandle=INVALID_HANDLE_VALUE then begin
exit;
end;
Try
ImageHandle := CreateFileMapping(FileHandle, nil, PAGE_READONLY, 0, 0, nil);
if ImageHandle=0 then begin
exit;
end;
Try
ImagePointer := MapViewOfFile(ImageHandle, FILE_MAP_READ, 0, 0, 0);
if not Assigned(ImagePointer) then begin
exit;
end;
Try
Header := ImageNtHeader(ImagePointer);
if not Assigned(Header) then begin
exit;
end;
if Header.Signature<>$00004550 then begin // "PE\0\0" as a DWORD.
exit;
end;
ExportTable := ImageRvaToVa(Header, ImagePointer, Header.OptionalHeader.DataDirectory[0].VirtualAddress, nil);
if not Assigned(ExportTable) then begin
exit;
end;
NamesPointer := ImageRvaToVa(Header, ImagePointer, Cardinal(ExportTable.AddressOfNames), nil);
if not Assigned(NamesPointer) then begin
exit;
end;
Names := ImageRvaToVa(Header, ImagePointer, Cardinal(NamesPointer^), nil);
if not Assigned(Names) then begin
exit;
end;
NamesDataLeft := Header.OptionalHeader.DataDirectory[0].Size;
for i := 0 to ExportTable.NumberOfNames-1 do begin
NamesList.Add(Names);
// Locate the next name
while (Names^<>chr(0)) and (NamesDataLeft>0) do begin
inc(Names);
dec(NamesDataLeft);
end;
inc(Names);
end;
Finally
UnmapViewOfFile(ImagePointer); // Ignore error as there is not much we could do.
End;
Finally
CloseHandle(ImageHandle);
End;
Finally
CloseHandle(FileHandle);
End;
end;
Is there support in the Delphi XE VCL for ensuring only a single instance of an application is running?
In the past, I've used library code to control a Mutex which has always seemed complicated. As I'm starting a new project in Delphi XE, I wonder if I need to dig up that old code, or if there is support built into XE already? Or is there another easy to apply code that is nice and modern?
You create a named Mutex when you start the application. Check GetLastError to see if an other instance is already running.
Put this code right after "begin" in your DPR file. Replace the GUID with one of your own. When I need a text constant that's unlikely to be used for anything else, I usually just hit Ctrl+G to get a GUID!
if CreateMutex(nil, True, '6EACD0BF-F3E0-44D9-91E7-47467B5A2B6A') = 0 then
RaiseLastOSError;
if GetLastError = ERROR_ALREADY_EXISTS then
Exit;
It might look like the code is leaking an handle because it's not saving the return of CreateMutex. It's not. Windows will automatically release the handle when our application is terminated, and that's absolutely fine with us.
I use JCL to do this:
program MyProgram;
uses
JclAppInst;
begin
JclAppInstances.CheckSingleInstance; // Added instance checking
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
Application.Run;
end.
Documentation for this, and the notification scheme, is at the JCL Wiki.
I use this, works in XE2 through to Alexandria, has the benefit of being able to bring the currently running instance to the front.
Those that say it shouldn't do that, well, given the last thing the user did was to try launch the app, bringing a currently running instance to the front makes sense
unit CheckPrevious;
interface
uses
Windows, SysUtils, WinSock;
function RestoreIfRunning(const AppHandle : THandle; MaxInstances : integer = 1) : boolean;
implementation
type
PInstanceInfo = ^TInstanceInfo;
TInstanceInfo = packed record
PreviousHandle : THandle;
RunCounter : integer;
end;
var
MappingHandle: THandle;
InstanceInfo: PInstanceInfo;
MappingName : string;
RemoveMe : boolean = True;
function RestoreIfRunning(const AppHandle : THandle; MaxInstances : integer = 1) : boolean;
begin
Result := True;
MappingName := StringReplace(ParamStr(0),'\','',[rfReplaceAll, rfIgnoreCase]);
MappingHandle := CreateFileMapping($FFFFFFFF,nil,PAGE_READWRITE,0,SizeOf(TInstanceInfo),PChar(MappingName));
if MappingHandle = 0 then
RaiseLastOSError
else
begin
if GetLastError <> ERROR_ALREADY_EXISTS then
begin
InstanceInfo := MapViewOfFile(MappingHandle,FILE_MAP_ALL_ACCESS,0,0,SizeOf(TInstanceInfo));
InstanceInfo^.PreviousHandle := AppHandle;
InstanceInfo^.RunCounter := 1;
Result := False;
end
else //already runing
begin
MappingHandle := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, PChar(MappingName));
if MappingHandle <> 0 then
begin
InstanceInfo := MapViewOfFile(MappingHandle,FILE_MAP_ALL_ACCESS,0,0,SizeOf(TInstanceInfo));
if InstanceInfo^.RunCounter >= MaxInstances then
begin
RemoveMe := False;
if IsIconic(InstanceInfo^.PreviousHandle) then
ShowWindow(InstanceInfo^.PreviousHandle, SW_RESTORE);
SetForegroundWindow(InstanceInfo^.PreviousHandle);
end
else
begin
InstanceInfo^.PreviousHandle := AppHandle;
InstanceInfo^.RunCounter := 1 + InstanceInfo^.RunCounter;
Result := False;
end
end;
end;
end;
end;
initialization
finalization
//remove one instance
if RemoveMe then
begin
MappingHandle := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, PChar(MappingName));
if MappingHandle <> 0 then
begin
InstanceInfo := MapViewOfFile(MappingHandle, FILE_MAP_ALL_ACCESS, 0, 0, SizeOf(TInstanceInfo));
InstanceInfo^.RunCounter := -1 + InstanceInfo^.RunCounter;
end
else
RaiseLastOSError;
end;
if Assigned(InstanceInfo) then
UnmapViewOfFile(InstanceInfo);
if MappingHandle <> 0 then
CloseHandle(MappingHandle);
end.
In your project DPR, add the CheckPrevious unit to the uses, then just after begin put the following
if RestoreIfRunning(Application.Handle, 1) then
Exit;
I have no idea of where this code originated, otherwise I would gladly credit the author. (A search of RestoreIfRunning may suggest it was from Zarko Gajic)
This is how i do it.
closeProc(extractfilename(paramstr(0)));
function TForm1.closeProc(pname : string): integer;
const
PROCESS_TERMINATE = $0001;
var
ContinueLoop: BOOL;
FSnapshotHandle: THandle;
FProcessEntry32: TProcessEntry32;
i : integer;
pname2 : string;
begin
try
Result := 0;
i := 0;
FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
FProcessEntry32.dwSize := SizeOf(FProcessEntry32);
ContinueLoop := Process32First(FSnapshotHandle, FProcessEntry32);
while Integer(ContinueLoop) <> 0 do
begin
pname2 := trim(UpperCase(ExtractFileName(FProcessEntry32.szExeFile)));
if ( pname2 = uppercase(pname)) then
if FProcessEntry32.th32ProcessID <> GetCurrentProcessId then
begin
Result := Integer(TerminateProcess(OpenProcess(PROCESS_TERMINATE, BOOL(0), FProcessEntry32.th32ProcessID), 0));
inc(i);
end;
ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32);
if i > 50 then
break;
end;
CloseHandle(FSnapshotHandle);
except
end;
end;