DelphiOnly
asked on
How to make "GetModuleFileName" work in Win95 ?
Has anyone "successfully" used GetModuleFileName in W95 ? it always returns "the calling exe name", or in rare instances "ole32.dll" & a few other dlls, but never works as it should.
Any comments....
as per Microsoft this shd be the function to used except in NT ? (http://support.microsoft.com/support/kb/articles/q119/1/63.asp)
Any comments....
as per Microsoft this shd be the function to used except in NT ? (http://support.microsoft.com/support/kb/articles/q119/1/63.asp)
ASKER
Here goes a quick example of what i would normally do:
GetModuleFileName(
GetWindowLong(
GetWindow(GetDesktopWindow , GW_child),
GWL_HInstance),
@c, 255);
as you can see, ModuleHandle comes from GetWindowLong (GWL) and the HWND for GWL comes from GetWindow.
GetModuleFileName(
GetWindowLong(
GetWindow(GetDesktopWindow
GWL_HInstance),
@c, 255);
as you can see, ModuleHandle comes from GetWindowLong (GWL) and the HWND for GWL comes from GetWindow.
ASKER
ok.. here goes an actual code snipet from my program:
dh := GetDesktopWindow;
h := GetWindow(dh, GW_child);
ih := GetWindowLong(h, GWL_HInstance);
GetModuleFileName(ih, @c, 255);
This returns "J:\Borland\Delphi\Bin\Pro ject.exe" for all child windows of Desktop !
dh := GetDesktopWindow;
h := GetWindow(dh, GW_child);
ih := GetWindowLong(h, GWL_HInstance);
GetModuleFileName(ih, @c, 255);
This returns "J:\Borland\Delphi\Bin\Pro
Your GetWindow(dh, GW_Child) will return the child window of the desktop topmost in the Z order, which will be your app!
Try GW_OWNER (or failing that either GW_HWNDFIRST or GW_HWNDLAST).
Cheers,
Raymond.
Try GW_OWNER (or failing that either GW_HWNDFIRST or GW_HWNDLAST).
Cheers,
Raymond.
My I know what do you want do do by calling this function?
ASKER
rwilson:
if you read my secod comment, you'll see that i said "it returns blah..blah.. for ALL CHILD WINDOWS of desktop !". -(those you get by using GetWindow(h, HWNDNEXT).
btw, as i have originally asked in my question, Have you EVER successfully used that function ?
because if one tries to answer from MS Knowledgebase then the function would have to work, but does it happen in reality :-)
dwwang:
you "supposedly" use that function to find the executable name that spawned a given process.
if you read my secod comment, you'll see that i said "it returns blah..blah.. for ALL CHILD WINDOWS of desktop !". -(those you get by using GetWindow(h, HWNDNEXT).
btw, as i have originally asked in my question, Have you EVER successfully used that function ?
because if one tries to answer from MS Knowledgebase then the function would have to work, but does it happen in reality :-)
dwwang:
you "supposedly" use that function to find the executable name that spawned a given process.
The functions in toolhelp32 unit have the ability do find all modules as well as processes currently loaded into win95 system. If what you want is this, I can give you an example, it even has the ability to "kill" a program.
Are the handles valid, or NULL (in which case they would all map to your app...)
Raymond.
Raymond.
With windows 3.1 every DLL was loaded only once. There worked this GetModuleFileName function like you expected it. But in 32bit windows each and every process has it's own copy of the DLL in it's memory context. It's like the DLL was just a part of the process. So the function returns always the name of the process file, not the name of the dll.
You could use something like toolhelp functions (only win95/98/NT5 or NT4 with service pack 4) to get the list of all dlls (like dwwang suggested). Then you could check which DLL has "your" module handle.
Regards, Madshi.
You could use something like toolhelp functions (only win95/98/NT5 or NT4 with service pack 4) to get the list of all dlls (like dwwang suggested). Then you could check which DLL has "your" module handle.
Regards, Madshi.
ASKER
rwilson:
The window handles are valid. i can get the ClassName & Window Title and they are ok.
Madshi, dwwang:
i find all the processes running using enumwindows.
In this case, I am trying to find the executable that started a process. e.g.:
say we have the notepad running. (one find the corresponding hWND & THandle). i want to find the executable that started this program (in this case i expect GetModuleFileName to return 'c:\Windows\notepad.exe')
i like to find the EXEs as well as DLLs.
The window handles are valid. i can get the ClassName & Window Title and they are ok.
Madshi, dwwang:
i find all the processes running using enumwindows.
In this case, I am trying to find the executable that started a process. e.g.:
say we have the notepad running. (one find the corresponding hWND & THandle). i want to find the executable that started this program (in this case i expect GetModuleFileName to return 'c:\Windows\notepad.exe')
i like to find the EXEs as well as DLLs.
Well, for processes, I can send you an example showing this function; for modules, please see this site to get an example.
http://www.innotts.co.uk/~zephyr/modules.html
They all use ToolHelp32 Modules, that mean you must have Win95/98.
http://www.innotts.co.uk/~zephyr/modules.html
They all use ToolHelp32 Modules, that mean you must have Win95/98.
dwwang, you're right and you're not right. Toolhelp functions work with NT5 and with NT4 (with service pack 4 installed), too.
However, I'm not sure if this is what DelphiOnly wants.
DelphiOnly, how do you get the processes by using enumWindows? I guess, you're using GetWindowProcessThreadID to get the process ID. Is that right?
Ok, now you have the process ID. Perhaps you can call OpenProcess to get a handle to it. But you can't use all these values with GetModuleFileName.
It's difficult. Let me explain: Every process has it's own address context. Your process can use the addresses 0-FFFFFFFF and notepad can use the same addresses, too. There's no conflict because every process has it's own addresses.
GetModuleFileName wants a module handle. A module handle is just the pointer to the beginning of the executable/dll in YOUR address context. Now if you have a value of another process, like an ID or a handle, it's another type of value. It's no module handle. Or if it is, it's at least not valid in your address context. So you give a invalid value to GetModuleFileName. Perhaps a pointer, that is valid only in the context of another process.
How can you make this all work? You could use the toolhelp functions to enumerate all processes (with all IDs and all exe names). But you can't get the dlls that other processes have loaded in their address context. That just doesn't work.
There is a way to get the dlls of other processes, too. But it's very very hard. You would have to "inject" the other processes with a self written dll. Then this dll could call the toolhelp functions in the context of the other process to enumerate the dlls. But it's very hard.
I hope, you now understand the problems a little bit better...
Regards, Madshi.
However, I'm not sure if this is what DelphiOnly wants.
DelphiOnly, how do you get the processes by using enumWindows? I guess, you're using GetWindowProcessThreadID to get the process ID. Is that right?
Ok, now you have the process ID. Perhaps you can call OpenProcess to get a handle to it. But you can't use all these values with GetModuleFileName.
It's difficult. Let me explain: Every process has it's own address context. Your process can use the addresses 0-FFFFFFFF and notepad can use the same addresses, too. There's no conflict because every process has it's own addresses.
GetModuleFileName wants a module handle. A module handle is just the pointer to the beginning of the executable/dll in YOUR address context. Now if you have a value of another process, like an ID or a handle, it's another type of value. It's no module handle. Or if it is, it's at least not valid in your address context. So you give a invalid value to GetModuleFileName. Perhaps a pointer, that is valid only in the context of another process.
How can you make this all work? You could use the toolhelp functions to enumerate all processes (with all IDs and all exe names). But you can't get the dlls that other processes have loaded in their address context. That just doesn't work.
There is a way to get the dlls of other processes, too. But it's very very hard. You would have to "inject" the other processes with a self written dll. Then this dll could call the toolhelp functions in the context of the other process to enumerate the dlls. But it's very hard.
I hope, you now understand the problems a little bit better...
Regards, Madshi.
ASKER
Madshi,
Looks like i got mixed up in terminology. Processes & Modules. I want to find the path of the executables for all running programs.
Look at the following code & suggest what shd i be doing to find the exe names:
dh := GetDesktopWindow;
h := GetWindow(dh, GW_child);
repeat
ih := GetWindowLong(h, GWL_HInstance);
if ih <> 0 then
GetModuleFileName(ih, @c, 255); //how do you find the exe for ModuleHandle ih ?
h := GetWindow(h, GW_HWNDNext);
until h = 0;
Looks like i got mixed up in terminology. Processes & Modules. I want to find the path of the executables for all running programs.
Look at the following code & suggest what shd i be doing to find the exe names:
dh := GetDesktopWindow;
h := GetWindow(dh, GW_child);
repeat
ih := GetWindowLong(h, GWL_HInstance);
if ih <> 0 then
GetModuleFileName(ih, @c, 255); //how do you find the exe for ModuleHandle ih ?
h := GetWindow(h, GW_HWNDNext);
until h = 0;
ASKER
dwwang:
unless i see your example how can i accept your answer ?
I'll take a look at ToolHelp functions, but it doesn't solve my problem.
unless i see your example how can i accept your answer ?
I'll take a look at ToolHelp functions, but it doesn't solve my problem.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
yup. i want the answer to be posted here and not through email.
thx.
thx.
DelphiOnly,
I didn't know the GetWindowLong with GWL_HInstance. I don't know what type the result has. However, even if it is the pointer to the beginning of the other application, it's valid only in the address context of that application. So you can't get the application's name/path from that value. Sorry.
You'll have to use the toolhelp functions. I guess, dwwang's example is just a short implementation of these functions. And I think it will solve your problem. It's quite overdressed, but AFAIK there's no other way in 32bit windows.
Regards, Madshi.
BTW: For winNT 4 there exists this function:
function GetModuleFileHandleEx(hPro cess,hModu le: cardinal; fileName: PChar; nSize: cardinal) : cardinal; stdcall;
But it's hidden in an extra dll (psapi.dll) which you would have to download from Microsoft. And this would work only for winNT 4.
I didn't know the GetWindowLong with GWL_HInstance. I don't know what type the result has. However, even if it is the pointer to the beginning of the other application, it's valid only in the address context of that application. So you can't get the application's name/path from that value. Sorry.
You'll have to use the toolhelp functions. I guess, dwwang's example is just a short implementation of these functions. And I think it will solve your problem. It's quite overdressed, but AFAIK there's no other way in 32bit windows.
Regards, Madshi.
BTW: For winNT 4 there exists this function:
function GetModuleFileHandleEx(hPro
But it's hidden in an extra dll (psapi.dll) which you would have to download from Microsoft. And this would work only for winNT 4.
Below is the whole contents of the source file, there are one string grid and two buttons(for the purpose of visually viewing the results), then main idea is in the TForm1.btShowProcClick.
unit frmProcF;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Tlhelp32, Grids;
type
TForm1 = class(TForm)
btShowProc: TButton;
StringGrid1: TStringGrid;
Label1: TLabel;
Label2: TLabel;
btKill: TButton;
procedure btShowProcClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure btKillClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.btShowProcClick(Sen der: TObject);
var
ProcessSnap: THandle;
ProcessEntry32: TProcessEntry32;
More: Boolean;
i:Integer;
begin
StringGrid1.RowCount:=1;
i:=0;
try
ProcessSnap := CreateToolhelp32Snapshot(T H32CS_SNAP Process,
GetCurrentProcessID);
if ProcessSnap = -1 then Exit;
ProcessEntry32.dwSize := SizeOf(ProcessEntry32);
More := Process32First(ProcessSnap , ProcessEntry32);
while More do
begin
StringGrid1.Cells[0,i]:=In tToStr(Pro cessEntry3 2.th32Proc essID);
StringGrid1.Cells[1,i]:=St rPas(Proce ssEntry32. szExeFile) ;
StringGrid1.RowCount:=Stri ngGrid1.Ro wCount+1;
i:=i+1;
More := Process32Next(ProcessSnap, ProcessEntry32);
end;
finally
CloseHandle(ProcessSnap);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
stringgrid1.ColWidths[0]:= 100;
stringgrid1.ColWidths[1]:= 400;
end;
procedure TForm1.btKillClick(Sender: TObject);
var
ProcHandle:THandle;
begin
ProcHandle:=OpenProcess(PR OCESS_ALL_ ACCESS,Fal se,StrToIn t64(String Grid1.Cell s[0,String Grid1.row] ));
TerminateProcess(ProcHandl e,0);
end;
end.
unit frmProcF;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Tlhelp32, Grids;
type
TForm1 = class(TForm)
btShowProc: TButton;
StringGrid1: TStringGrid;
Label1: TLabel;
Label2: TLabel;
btKill: TButton;
procedure btShowProcClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure btKillClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.btShowProcClick(Sen
var
ProcessSnap: THandle;
ProcessEntry32: TProcessEntry32;
More: Boolean;
i:Integer;
begin
StringGrid1.RowCount:=1;
i:=0;
try
ProcessSnap := CreateToolhelp32Snapshot(T
GetCurrentProcessID);
if ProcessSnap = -1 then Exit;
ProcessEntry32.dwSize := SizeOf(ProcessEntry32);
More := Process32First(ProcessSnap
while More do
begin
StringGrid1.Cells[0,i]:=In
StringGrid1.Cells[1,i]:=St
StringGrid1.RowCount:=Stri
i:=i+1;
More := Process32Next(ProcessSnap,
end;
finally
CloseHandle(ProcessSnap);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
stringgrid1.ColWidths[0]:=
stringgrid1.ColWidths[1]:=
end;
procedure TForm1.btKillClick(Sender:
var
ProcHandle:THandle;
begin
ProcHandle:=OpenProcess(PR
TerminateProcess(ProcHandl
end;
end.
By the way, I use D4 so there is a StrToInt64
ASKER
yup, your code lists all executables running in the system, but now how do i find which program created which "Window" (with known window handles).
I can iterate through all window handles and all EXEs (that'll be two loops within each other: one for ToolHelp functions and the other for enumwindows), but obviously that is not the elegant/fast way.
Can you give any pointers regarding this ?
and btw, where (in which unit in D2) is PROCESS_ALL_ACCESS defined ?
I can iterate through all window handles and all EXEs (that'll be two loops within each other: one for ToolHelp functions and the other for enumwindows), but obviously that is not the elegant/fast way.
Can you give any pointers regarding this ?
and btw, where (in which unit in D2) is PROCESS_ALL_ACCESS defined ?
You should call GetWindowThreadProcessID to get exe's process ID. Then use the toolhelp loop to find the matching entry to that process ID.
You're right. It's not very elegant at all. But this is due to the lack of appropriate windows APIs. AFAIK, there's no better *documented* way. Perhaps there are some undocumented ones...
Regards, Madshi.
You're right. It's not very elegant at all. But this is due to the lack of appropriate windows APIs. AFAIK, there's no better *documented* way. Perhaps there are some undocumented ones...
Regards, Madshi.
As Madshi said, there may be no easy way for the first question.
If process_all_access is defined in D2, it should be in Windows.pas, below is the definition in D4:
PROCESS_TERMINATE = $0001;
{$EXTERNALSYM PROCESS_TERMINATE}
PROCESS_CREATE_THREAD = $0002;
{$EXTERNALSYM PROCESS_CREATE_THREAD}
PROCESS_VM_OPERATION = $0008;
{$EXTERNALSYM PROCESS_VM_OPERATION}
PROCESS_VM_READ = $0010;
{$EXTERNALSYM PROCESS_VM_READ}
PROCESS_VM_WRITE = $0020;
{$EXTERNALSYM PROCESS_VM_WRITE}
PROCESS_DUP_HANDLE = $0040;
{$EXTERNALSYM PROCESS_DUP_HANDLE}
PROCESS_CREATE_PROCESS = $0080;
{$EXTERNALSYM PROCESS_CREATE_PROCESS}
PROCESS_SET_QUOTA = $0100;
{$EXTERNALSYM PROCESS_SET_QUOTA}
PROCESS_SET_INFORMATION = $0200;
{$EXTERNALSYM PROCESS_SET_INFORMATION}
PROCESS_QUERY_INFORMATION = $0400;
{$EXTERNALSYM PROCESS_QUERY_INFORMATION}
PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE or $FFF);
{$EXTERNALSYM PROCESS_ALL_ACCESS}
If process_all_access is defined in D2, it should be in Windows.pas, below is the definition in D4:
PROCESS_TERMINATE = $0001;
{$EXTERNALSYM PROCESS_TERMINATE}
PROCESS_CREATE_THREAD = $0002;
{$EXTERNALSYM PROCESS_CREATE_THREAD}
PROCESS_VM_OPERATION = $0008;
{$EXTERNALSYM PROCESS_VM_OPERATION}
PROCESS_VM_READ = $0010;
{$EXTERNALSYM PROCESS_VM_READ}
PROCESS_VM_WRITE = $0020;
{$EXTERNALSYM PROCESS_VM_WRITE}
PROCESS_DUP_HANDLE = $0040;
{$EXTERNALSYM PROCESS_DUP_HANDLE}
PROCESS_CREATE_PROCESS = $0080;
{$EXTERNALSYM PROCESS_CREATE_PROCESS}
PROCESS_SET_QUOTA = $0100;
{$EXTERNALSYM PROCESS_SET_QUOTA}
PROCESS_SET_INFORMATION = $0200;
{$EXTERNALSYM PROCESS_SET_INFORMATION}
PROCESS_QUERY_INFORMATION = $0400;
{$EXTERNALSYM PROCESS_QUERY_INFORMATION}
PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE or $FFF);
{$EXTERNALSYM PROCESS_ALL_ACCESS}
ASKER
Madshi,
are there any "documents" on the net about the "undocumented" APIs :-)
dwwang,
Thx for introducing me to ToolHelp functions !
are there any "documents" on the net about the "undocumented" APIs :-)
dwwang,
Thx for introducing me to ToolHelp functions !
Maybe there are some, but I don't know where. Sorry...
You need to make sure you are passing the correct module handle for the module you are getting the filename for. How are you getting the module handle?
Cheers.
Raymond.