I'm trying to implement the IFileIsUse COM interface in my Delphi program so that Windows Explorer can show more details about my application when it locks a file.
I based my code on the FileIsUse sample from Microsoft (http://msdn.microsoft.com/en-us/library/ee330722%28VS.85%29.aspx) and am up to this:
type
TFileIsInUseImpl = class(TInterfacedObject, IUnknown, IFileIsInUse)
protected
function GetAppName(out ppszName: LPWSTR) : HRESULT; stdcall;
function GetUsage(out pfut : FILE_USAGE_TYPE) : HRESULT; stdcall;
function GetCapabilities(out pdwCapFlags : DWORD) : HRESULT; stdcall;
function GetSwitchToHWND(out phwnd : HWND) : HRESULT; stdcall;
function CloseFile() : HRESULT; stdcall;
public
constructor Create(const AFileName: string);
end;
procedure RegisterFileIsInUse(const AFileName: string);
var
Cookie: Longint;
rot: IRunningObjectTable;
hr: HRESULT;
mk: IMoniker;
FileIsInUse: IFileIsInUse;
begin
hr := GetRunningObjectTable(0, rot);
if SUCCEEDED(hr) then
begin
hr := CreateFileMoniker(PChar(AFileName), mk);
if SUCCEEDED(hr) then
begin
FileIsInUse := TFileIsInUseImpl.Create(AFileName);
hr := rot.Register(ROTFLAGS_REGISTRATIONKEEPSALIVE or ROTFLAGS_ALLOWANYCLIENT, FileIsInUse, mk, Cookie);
if hr = CO_E_WRONG_SERVER_IDENTITY then
hr := rot.Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, FileIsInUse, mk, Cookie);
if SUCCEEDED(hr) then
FRegisteredFiles.Add(AFileName, TRegisteredFile.Create(Cookie, FileIsInUse));
end;
end;
end;
I added registry infos in both HKEY_CLASSES_ROOT\AppID\MyApp.exe and HKEY_CLASSES_ROOT\AppID\{MyGUID} to indicate RunAs=Interactive user so that the first call to rot.Register succeeds with ROTFLAGS_ALLOWANYCLIENT
However, none of my TFileIsInUseImpl methods are ever called.
Overriding QueryInterface for it, I discovered that it does get called but only for marshaling related interfaces, never for IFileIsInUse.
Looking around I came to the conclusion that something is not initialized the way it should be in my application, but I can't figure out why.
I already tried this:
Call CoInitializeEx(nil, COINIT_MULTITHREADED) instead of the default CoInitialize(nil)
Add HKEY_CLASSES_ROOT\CLSID\{MyGuid}\InProcServer32\ThreadingModel=Both
but nothing helped. Considering that Microsoft's sample is a standalone application, I should be able to replicate what it does.
Can any of you tell me what I'm doing wrong here?
If the code was build similar to the MSDN example, then it works as expected. The sample code can be downloaded from the code gallery:
Defect link: http://archive.msdn.microsoft.com/Project/Download/FileDownload.aspx?ProjectName=shellapplication&DownloadId=6773
New link: https://msdn.microsoft.com/en-us/library/windows/desktop/ee330722(v=vs.85).aspx
Download from Windows 7 SDK: https://www.microsoft.com/en-us/download/details.aspx?id=8279
Related
Problem solved, refer to my answer, however cannot accept it right now because stack overflow's 2 day rule. Thanks for the input everbody!
edit: The answer is removed, the answer is to remove line:
function registerWindowMessage( lpString : PChar ) : integer; stdcall; external user32 name 'RegisterWindowMessageA';
From the project because it is already defined in delphi windows api files, that's it. No need to redefine it and also the redefine does not match the newer version.
I try to revive/migrate some older Delphi 5 Enterprise (32bit) projects to a new/modern Delphi version (Delphi 10.2, 32bit) however the old versions compiles and run fine on any OS. Overall, pretty compatible.
Now I am running into this strange problem, the Delphi 10.2 form does not like to handle SHELLHOOK messages, the older compiled Delphi 5 version does. Because I don't have the source of Delphi 10.2 (free edition) forms.pas I can't see what is actually going on (different) and can't figure out why it doesn't work. Unable to debug it.
The hook registration seems to be fine, the writeln's in the FormCreate shows the following values (in extra console window):
However the overrided WndProc procedure does not handle any shellhook messages. I made a demo so you can try it yourself by creating a new project, double click on the form's onCreate and onDestroy event and replace the forms code with this:
unit main;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
const
// Constant for shell hook events
HSHELL_WINDOWCREATED = 1;
HSHELL_WINDOWDESTROYED = 2;
HSHELL_ACTIVATESHELLWINDOW = 3;
HSHELL_WINDOWACTIVATED = 4;
HSHELL_GETMINRECT = 5;
HSHELL_REDRAW = 6;
HSHELL_TASKMAN = 7;
HSHELL_LANGUAGE = 8;
HSHELL_ACCESSIBILITYSTATE = 11;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure FormActivate(Sender: TObject);
private
{ Private declarations }
FHookMsg : integer;
procedure WMShellHook(var Msg: TMessage );
protected
procedure WndProc(var Msg : TMessage); override;
public
{ Public declarations }
end;
var
Form1: TForm1;
// Not implemented Windows API functions, available at WinXP and later
function registerWindowMessage( lpString : PChar ) : integer; stdcall; external user32 name 'RegisterWindowMessageA';
function registerShellHookWindow( hWnd : THandle ) : bool; stdcall; external user32 name 'RegisterShellHookWindow';
function deregisterShellHookWindow( hWnd : THandle ) : bool; stdcall; external user32 name 'DeregisterShellHookWindow';
implementation
{$R *.dfm}
procedure TForm1.FormActivate(Sender: TObject);
begin
// send a message
sendMessage( handle, WM_USER+$40, 1, 2 );
postMessage( handle, WM_USER+$40, 3, 4 );
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
writeln( handle );
FHookMsg:=registerWindowMessage('SHELLHOOK'+#0 );
writeln( FHookMsg );
writeln( registerShellHookWindow( handle ) );
writeln( handle ); // handle still the same
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
deregisterShellHookWindow( handle );
writeln( handle ); // set breakpoint here, handle still the same
end;
procedure TForm1.FormShow(Sender: TObject);
begin
writeln( handle ); // handle still the same
end;
procedure TForm1.WndProc(var Msg : TMessage);
begin
// writeln( handle ); even when i showed this, handle is still the same
if( Msg.Msg = WM_USER+$40 ) then
begin
writeln( 'wParam is: ', Msg.wParam );
writeln( 'lParam is: ', Msg.lParam );
exit;
end;
if( Msg.Msg = FHookMsg ) then
begin
// Not executed in Delphi 10.2 generated exe
writeln( 'wParam is: ', Msg.wParam );
WMShellHook( Msg );
exit;
end;
inherited; // call this for default behaviour
end;
procedure TForm1.WMShellHook( var Msg: TMessage );
begin
// Simple however effective way to detect window changes at low costs.
if( Msg.wparam = HSHELL_WINDOWCREATED )
or ( Msg.wparam = HSHELL_WINDOWDESTROYED )
or ( Msg.wparam = HSHELL_WINDOWACTIVATED ) then
begin
// Not executed in Delphi 10.2 generated exe
writeln('here' );
end;
end;
end.
PS: Don't forget to switch linker option "generate console application" on to avoid writeln errors while running this demo.
Can somebody tell what's going on and why it doesn't work?
EDIT:
Example with allocateHwnd and deallocateHwnd, does not receive anything. Why not? Followed this example.
unit unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
const
// Constant for shell hook events
HSHELL_WINDOWCREATED = 1;
HSHELL_WINDOWDESTROYED = 2;
HSHELL_ACTIVATESHELLWINDOW = 3;
HSHELL_WINDOWACTIVATED = 4;
HSHELL_GETMINRECT = 5;
HSHELL_REDRAW = 6;
HSHELL_TASKMAN = 7;
HSHELL_LANGUAGE = 8;
HSHELL_ACCESSIBILITYSTATE = 11;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
FHookWndHandle : THandle;
FHookMsg : integer;
procedure WMShellHook(var Msg: TMessage );
protected
procedure WndMethod(var Msg: TMessage);
public
{ Public declarations }
end;
var
Form1: TForm1;
// Not implemented Windows API functions, available at WinXP and later
function registerWindowMessage( lpString : PChar ) : integer; stdcall; external user32 name 'RegisterWindowMessageA';
function registerShellHookWindow( hWnd : THandle ) : bool; stdcall; external user32 name 'RegisterShellHookWindow';
function deregisterShellHookWindow( hWnd : THandle ) : bool; stdcall; external user32 name 'DeregisterShellHookWindow';
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
FHookWndHandle:=allocateHWnd(WndMethod);
FHookMsg:=registerWindowMessage('SHELLHOOK'+#0 );
writeln( FHookMsg );
writeln( registerShellHookWindow( FHookWndHandle ) );
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
deregisterShellHookWindow( FHookWndHandle );
deallocateHWnd( FHookWndHandle );
end;
procedure TForm1.WndMethod(var Msg: TMessage);
begin
if( Msg.Msg = FHookMsg ) then
begin
// Not executed in Delphi 10.2 generated exe
writeln( 'wParam is: ', Msg.wParam );
WMShellHook( Msg );
exit;
end;
end;
procedure TForm1.WMShellHook( var Msg: TMessage );
begin
// Simple however effective way to detect window changes at low costs.
if( Msg.wparam = HSHELL_WINDOWCREATED )
or ( Msg.wparam = HSHELL_WINDOWDESTROYED )
or ( Msg.wparam = HSHELL_WINDOWACTIVATED ) then
begin
// Not executed in Delphi 10.2 generated exe
writeln('here' );
end;
end;
end.
function registerWindowMessage( lpString : PChar ) : integer; stdcall;
external user32 name 'RegisterWindowMessageA';
This declaration is correct in ANSI versions of Delphi but incorrect in Unicode Delphi. In Unicode Delphi you should be using the W version of the function. As it stands your version sends UTF16 text to a function that expects ANSI and that mismatch means the wrong message name will be received by the function. Correct it like this:
function registerWindowMessage( lpString : PChar ) : integer; stdcall;
external user32 name 'RegisterWindowMessageW';
That's probably the most important problem. Because of this text encoding mismatch you will be registering a window message with the wrong name and so won't receive the messages you expect.
Note also that the return type should be UINT. You should change this, and the type of FHookMsg, although doing so won't actually change any behaviour.
VCL windowed controls are subject to window recreation. There are plenty of reasons that it might happen, but the window handle behind the form can be destroyed and recreated at any point in the lifetime of the form.
Your code has always been wrong but you appear to have got away with it. There are two solutions:
Register and unregister the hook in overridden CreateWnd or DestroyWnd.
Use a non VCL window to handle the hook. Use AllocateHWnd and DeallocateHWnd.
Personally I regard the second option to be preferable.
Those are the mistakes that can I can see in the code provided. There are other possible problems. You describe this as happening inside a console application but of course we cannot see how you create the form, how you run the message loop and so on. So I guess there could well be other mistakes in the code that we cannot see.
Change your declaration of RegisterWindowMessage to this:
function RegisterWindowMessage( lpString : PChar ) : integer; stdcall; external user32 name 'RegisterWindowMessageW';
Guys I got a source from a friend that is suppose to help me learn RE a lot but I got error
Error:identifier not found "TResourceStream"
"Bool"
"TMemoryStream"
"TResourceInfo"
"try"
And
Fatal: Syntax error, ";" expected but " identifier MS" found
Please help I need to compile this.
Excuse me please I know nothing yet, about programming.
Here is the source
function EnumResourceNames(hModule: HMODULE; // EXE handle returned from LoadLibrary/Ex
lpType: PChar; // resource type (eg: RT_RCDATA)
lpEnumFunc: ENUMRESNAMEPROC; // callback function address
lParam: Integer // long integer (eg: pointer to an object)
): BOOL; stdcall;
function CB_EnumDfmNameProc(hModule: THandle; lpszType, lpszName: PChar;
lParam: Integer): Boolean; stdcall;
var
ms: TMemoryStream;
rs: TResourceStream;
Buffer: array of Byte;
begin
with TResourceInfo(lParam) do
begin
rs := TResourceStream.Create(TResourceInfo(lParam).Module,
lpszname, lpszType); // load resource in memory
try
ms := TMemoryStream.Create;
try
try
SetLength(Buffer, 4);
rs.Read(Buffer[0], SizeOf(Buffer)); // read the first 4 bytes
if string(Buffer) = 'TPF0' then // is it a DFM resource?
begin
rs.Seek(0, 0);
ObjectBinaryToText(rs, ms); // decode DFM
ms.Seek(0, 0);
AddDfm(StrPas(lpszName), ms); // add it to our own list
end;
except
raise;
end;
finally
ms.Free;
end;
finally
rs.free;
end;
end;
Result := True;
end;
procedure TResourceInfo.EnumDfmNames;
begin
if FModule > 0 then // if an EXE file has been loaded
EnumResourceNames(FModule, RT_RCDATA, // go and search RCDATA resources
#CB_EnumDfmNameProc, Integer(Self));
end;
I have been writing a program that ideally will run on a server in the background without ever closing - therefore it is important that any memory leaks are non existent. My program involves retrieving live session information using the Windows Terminal Services API (wtsapi32.dll) and since the information must be live the function is being run every few seconds, I have found that calling the WTSEnumerateSessionsEx function has lead to a fairly sizable memory leak. It seems the call to WTSFreeMemoryEx as instructed in the MSDN documentation seems to have no impact yet I receive no error messages from either call.
To summarize: the problem is not in execution of WTSEnumerateSessionsEx since valid data is returned; the memory is simply not being freed and this leads to problems when left to run for extended periods of time.
Currently the short-term solution has been to restart the process when used memory exceeds a threshold however this doesn't seem to be a satisfactory solution and rectifying this leak would be most desirable.
The enumeration types have been taken directly from the Microsoft MSDN documentation.
Attached is the relevant source file.
unit WtsAPI32;
interface
uses Windows, Classes, Dialogs, SysUtils, StrUtils;
const
WTS_CURRENT_SERVER_HANDLE = 0;
type
WTS_CONNECTSTATE_CLASS = (WTSActive, WTSConnected, WTSConnectQuery,
WTSShadow, WTSDisconnected, WTSIdle, WTSListen, WTSReset, WTSDown,
WTSInit);
type
WTS_TYPE_CLASS = (WTSTypeProcessInfoLevel0, WTSTypeProcessInfoLevel1,
WTSTypeSessionInfoLevel1);
type
WTS_SESSION_INFO_1 = record
ExecEnvId: DWord;
State: WTS_CONNECTSTATE_CLASS;
SessionId: DWord;
pSessionName: LPtStr;
pHostName: LPtStr;
pUserName: LPtStr;
pDomainName: LPtStr;
pFarmName: LPtStr;
end;
type
TSessionInfoEx = record
ExecEnvId: DWord;
State: WTS_CONNECTSTATE_CLASS;
SessionId: DWord;
pSessionName: string;
pHostName: string;
pUserName: string;
pDomainName: string;
pFarmName: string;
end;
TSessions = array of TSessionInfoEx;
function FreeMemoryEx(WTSTypeClass: WTS_TYPE_CLASS; pMemory: Pointer;
NumberOfEntries: Integer): BOOL; stdcall;
external 'wtsapi32.dll' name 'WTSFreeMemoryExW';
function FreeMemory(pMemory: Pointer): DWord; stdcall;
external 'wtsapi32.dll' name 'WTSFreeMemory';
function EnumerateSessionsEx(hServer: THandle; var pLevel: DWord;
Filter: DWord; var ppSessionInfo: Pointer; var pCount: DWord): BOOL;
stdcall; external 'wtsapi32.dll' name 'WTSEnumerateSessionsExW';
function EnumerateSessions(var Sessions: TSessions): Boolean;
implementation
function EnumerateSessions(var Sessions: TSessions): Boolean;
type
TSessionInfoExArr = array[0..2000 div SizeOf(WTS_SESSION_INFO_1)] of WTS_SESSION_INFO_1;
var
ppSessionInfo: Pointer;
pCount: DWord;
hServer: THandle;
level: DWord;
i: Integer;
ErrCode: Integer;
Return: DWord;
begin
pCount := 0;
level := 1;
hServer := WTS_CURRENT_SERVER_HANDLE;
ppSessionInfo := NIL;
if not EnumerateSessionsEx(hServer, level, 0, ppSessionInfo, pCount) then
begin
ErrCode := GetLastError;
ShowMessage('Error in EnumerateSessionsEx - Code: ' + IntToStr(ErrCode)
+ ' Message: ' + SysErrorMessage(ErrCode));
en
else
begin
SetLength(Sessions, pCount);
for i := 0 to pCount - 1 do
begin
Sessions[i].ExecEnvId := TSessionInfoExArr(ppSessionInfo^)[i].ExecEnvId;
Sessions[i].State := TSessionInfoExArr(ppSessionInfo^)[i].State;
Sessions[i].SessionId := TSessionInfoExArr(ppSessionInfo^)[i].SessionId;
Sessions[i].pSessionName := WideCharToString
(TSessionInfoExArr(ppSessionInfo^)[i].pSessionName);
Sessions[i].pHostName := WideCharToString
(TSessionInfoExArr(ppSessionInfo^)[i].pHostName);
Sessions[i].pUserName := WideCharToString
(TSessionInfoExArr(ppSessionInfo^)[i].pUserName);
Sessions[i].pDomainName := WideCharToString
(TSessionInfoExArr(ppSessionInfo^)[i].pDomainName);
Sessions[i].pFarmName := WideCharToString
(TSessionInfoExArr(ppSessionInfo^)[i].pFarmName);
end;
if not FreeBufferEx(WTSTypeSessionInfoLevel1, ppSessionInfo, pCount);
begin
ErrCode := GetLastError;
ShowMessage('Error in EnumerateSessionsEx - Code: ' + IntToStr(ErrCode)
+ ' Message: ' + SysErrorMessage(ErrCode));
end;
ppSessionInfo := nil;
end;
end;
end.
Here's is a minimal SSCCE that demonstrates the issue. When this program executes, it exhausts available memory in short time.
program SO17839270;
{$APPTYPE CONSOLE}
uses
SysUtils, Windows;
const
WTS_CURRENT_SERVER_HANDLE = 0;
type
WTS_TYPE_CLASS = (WTSTypeProcessInfoLevel0, WTSTypeProcessInfoLevel1,
WTSTypeSessionInfoLevel1);
function WTSEnumerateSessionsEx(hServer: THandle; var pLevel: DWORD;
Filter: DWORD; var ppSessionInfo: Pointer; var pCount: DWORD): BOOL; stdcall;
external 'wtsapi32.dll' name 'WTSEnumerateSessionsExW';
function WTSFreeMemoryEx(WTSTypeClass: WTS_TYPE_CLASS; pMemory: Pointer;
NumberOfEntries: Integer): BOOL; stdcall;
external 'wtsapi32.dll' name 'WTSFreeMemoryExW';
procedure EnumerateSessionsEx;
var
ppSessionInfo: Pointer;
pCount: DWORD;
level: DWORD;
begin
level := 1;
if not WTSEnumerateSessionsEx(WTS_CURRENT_SERVER_HANDLE, level, 0,
ppSessionInfo, pCount) then
RaiseLastOSError;
if not WTSFreeMemoryEx(WTSTypeSessionInfoLevel1, ppSessionInfo, pCount) then
RaiseLastOSError;
end;
begin
while True do
EnumerateSessionsEx;
end.
To summarise the comment trail, I think that there is a fault in the WTS library code, that afflicts the WTSEnumerateSessionsEx and WTSFreeMemoryEx functions. The SSCCE that I added to the question gives a pretty clear demonstration of that.
So, your options to work around the fault would appear to be:
Only call WTSEnumerateSessionsEx when you get notified that a session is created or destroyed. That would minimise the number of calls you make. You'd still be left with a leak, but I suspect that it would take a very long time before you encountered problems.
Switch to WTSEnumerateSessions and then call WTSQuerySessionInformation to obtain any extra information that you need. From my trials, WTSEnumerateSessions would appear not to be afflicted by the same problem as WTSEnumerateSessionsEx.
I created the same sample in MSVC:
#include <Windows.h>
#include <WtsApi32.h>
#pragma comment(lib, "wtsapi32")
int _tmain(int argc, _TCHAR* argv[])
{
DWORD Level = 1;
PWTS_SESSION_INFO_1 pSessionInfo;
DWORD Count = 0;
BOOL bRes;
while (WTSEnumerateSessionsEx(WTS_CURRENT_SERVER_HANDLE, &Level, 0, &pSessionInfo, &Count))
{
if (!WTSFreeMemoryEx(WTSTypeSessionInfoLevel1, pSessionInfo, Count))
{
break;
}
}
return 0;
}
I am observing the same behaviour in Task Manager and even though Task Manager is not a tool to track memory leaks this behaviour is clearly a leak and it seems like a bug.
It happens both in x86 and x64 build (x64 uses the x64 version of WtsApi32.dll).
When you have finished using the array, free it by calling the WTSFreeMemoryEx function. You should also set the pointer to NULL.
(C) https://learn.microsoft.com/en-us/windows/desktop/api/wtsapi32/nf-wtsapi32-wtsenumeratesessionsexa
What is the best way to find whether a web-browser is running?
Using Delphi XE2 and on Windows, I need to find whether the following web-browsers are currently running:
A) Mozilla Firefox
B) Apple Safari
C) Google Chrome
If found, the process will be terminated because the home page of the web-browser needs to be changed programmatically by modifying the web-browser configuration files (which is either not possible or could result in unpredictable results if done when the web-browser is running).
Does the output from the EnumWindows API function contain sufficient information needed to handle the above task? If yes, then are the window class names for each of the above web-browsers documented anywhere? If no, then which method is most reliable?
TIA.
Terminate a process without the user permission is not good practice, instead you must ask to the user if he wants terminate the app (in this case the web browser).
Now back to your question, you can detect if a app(webbroser) is running checking for the process name (firefox.exe, chrome.exe , safari.exe) using the CreateToolhelp32Snapshot method.
uses
Windows,
tlhelp32,
SysUtils;
function IsProcessRunning(const ListProcess: Array of string): boolean;
var
hSnapshot : THandle;
lppe : TProcessEntry32;
I : Integer;
begin
result:=false;
hSnapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if hSnapshot <> INVALID_HANDLE_VALUE then
try
lppe.dwSize := SizeOf(lppe);
if Process32First(hSnapshot, lppe) then
repeat
for I := Low(ListProcess) to High(ListProcess) do
if SameText(lppe.szExeFile, ListProcess[i]) then
Exit(True);
until not Process32Next(hSnapshot, lppe);
finally
CloseHandle(hSnapshot);
end;
end;
and use like so
IsProcessRunning(['firefox.exe','chrome.exe','safari.exe'])
Now if you want a more reliable way you can search for the class name of the Window (using the FindWindowEx method) and then the PID of the process owner of the handle (using GetWindowThreadProcessId), from here you can use the PID of the process to resolve the name of exe.
{$APPTYPE CONSOLE}
uses
Windows,
tlhelp32,
SysUtils;
function GetProcessName(const th32ProcessID: DWORD): string;
var
hSnapshot : THandle;
lppe : TProcessEntry32;
begin
result:='';
hSnapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if hSnapshot <> INVALID_HANDLE_VALUE then
try
lppe.dwSize := SizeOf(lppe);
if Process32First(hSnapshot, lppe) then
repeat
if lppe.th32ProcessID=th32ProcessID then
Exit(lppe.szExeFile);
until not Process32Next(hSnapshot, lppe);
finally
CloseHandle(hSnapshot);
end;
end;
function IsWebBrowserRunning(const ClassName, ExeName :string) : Boolean;
var
hWindow : THandle;
dwProcessId: DWORD;
begin
result:=False;
hWindow:= FindWindowEx(0, 0, PChar(ClassName), nil);
if hWindow<>0 then
begin
dwProcessId:=0;
GetWindowThreadProcessId(hWindow, dwProcessId);
if dwProcessId>0 then
exit(Sametext(GetProcessName(dwProcessId),ExeName));
end;
end;
begin
try
if IsWebBrowserRunning('MozillaWindowClass','firefox.exe') then
Writeln('Firefox is Running');
if IsWebBrowserRunning('{1C03B488-D53B-4a81-97F8-754559640193}','safari.exe') then
Writeln('Safari is Running');
if IsWebBrowserRunning('Chrome_WidgetWin_1','chrome.exe') then
Writeln('Chrome is Running');
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
readln;
end.
The information on the version Exe-file I receive by means of VerQueryValue. Is there an inverse function (WinApi or Delphi) which can register (establish or change) such information?
Here, for example, there is a program which is able to do so. How may it work (http://www.angusj.com/resourcehacker)?
The version information is stored via resources; to edit that you simply need to edit that resource. Here is a unit I found that can clone an existing file version information and attach it to another file. It's very easy to do what you want starting from this code (it's coded by a friend of mine and is available public):
unit cloneinfo;
interface
uses Windows, SysUtils;
type
LANGANDCODEPAGE = record
wLanguage: Word;
wCodePage: Word;
end;
procedure clone(sFile,output:string);
implementation
procedure clone(sFile,output:string);
var
dwHandle, cbTranslate: cardinal;
sizeVers: DWord;
lpData, langData: Pointer;
lpTranslate: ^LANGANDCODEPAGE;
hRes : THandle;
begin
sizeVers := GetFileVersionInfoSize(PChar(sFile), dwHandle);
If sizeVers = 0 then
exit;
GetMem(lpData, sizeVers);
try
ZeroMemory(lpData, sizeVers);
GetFileVersionInfo (PChar(sFile), 0, sizeVers, lpData);
If not VerQueryValue (lpData, '\VarFileInfo\Translation', langData, cbTranslate) then
exit;
hRes := BeginUpdateResource(pchar(output), FALSE);
//For i := 0 to (cbTranslate div sizeof(LANGANDCODEPAGE)) do
//begin
lpTranslate := Pointer(Integer(langData) + sizeof(LANGANDCODEPAGE));
UpdateResource(hRes, RT_VERSION, MAKEINTRESOURCE(VS_VERSION_INFO), lpTranslate^.wLanguage,lpData, sizeVers);
//end;
EndUpdateResource(hRes, FALSE);
finally
FreeMem(lpData);
end;
end;
end.