Link to home
Start Free TrialLog in
Avatar of DelphiOnly
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)
Avatar of rwilson032697
rwilson032697

Can you post the code you use to call it?

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.
Avatar of DelphiOnly

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.
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\Project.exe" for all child windows of Desktop !
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.
My I know what do you want do do by calling this function?
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.
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.
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.
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.
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.
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.
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;


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.
ASKER CERTIFIED SOLUTION
Avatar of dwwang
dwwang

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
yup. i want the answer to be posted here and not through email.
 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(hProcess,hModule: 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.
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(Sender: TObject);
var
   ProcessSnap: THandle;
   ProcessEntry32: TProcessEntry32;
   More: Boolean;
   i:Integer;

begin
     StringGrid1.RowCount:=1;
     i:=0;
     try
        ProcessSnap := CreateToolhelp32Snapshot(TH32CS_SNAPProcess,
        GetCurrentProcessID);
        if ProcessSnap = -1 then Exit;
        ProcessEntry32.dwSize := SizeOf(ProcessEntry32);
        More := Process32First(ProcessSnap, ProcessEntry32);
        while More do
              begin
                 StringGrid1.Cells[0,i]:=IntToStr(ProcessEntry32.th32ProcessID);
                 StringGrid1.Cells[1,i]:=StrPas(ProcessEntry32.szExeFile);
                 StringGrid1.RowCount:=StringGrid1.RowCount+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(PROCESS_ALL_ACCESS,False,StrToInt64(StringGrid1.Cells[0,StringGrid1.row]));
     TerminateProcess(ProcHandle,0);
end;

end.
By the way, I use D4 so there is a StrToInt64
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 ?
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.
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}
Madshi,

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