Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people, just like you, are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
Solved

How to make "GetModuleFileName" work in Win95 ?

Posted on 1998-11-05
24
330 Views
Last Modified: 2010-04-04
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)
0
Comment
Question by:DelphiOnly
  • 9
  • 7
  • 5
  • +1
24 Comments
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1345836
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.
0
 

Author Comment

by:DelphiOnly
ID: 1345837
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.
0
 

Author Comment

by:DelphiOnly
ID: 1345838
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 !
0
Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
LVL 12

Expert Comment

by:rwilson032697
ID: 1345839
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.
0
 
LVL 4

Expert Comment

by:dwwang
ID: 1345840
My I know what do you want do do by calling this function?
0
 

Author Comment

by:DelphiOnly
ID: 1345841
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.
0
 
LVL 4

Expert Comment

by:dwwang
ID: 1345842
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.
0
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1345843
Are the handles valid, or NULL (in which case they would all map to your app...)

Raymond.
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1345844
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.
0
 

Author Comment

by:DelphiOnly
ID: 1345845
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.
0
 
LVL 4

Expert Comment

by:dwwang
ID: 1345846
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.
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1345847
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.
0
 

Author Comment

by:DelphiOnly
ID: 1345848
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;


0
 

Author Comment

by:DelphiOnly
ID: 1345849
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.
0
 
LVL 4

Accepted Solution

by:
dwwang earned 100 total points
ID: 1345850
Yes, but surely when I said "I can e-mail you an example", I was asking for your e-mail address. If you don't want to give it here, I can just paste the procedure here. It's tather simple, and do exactly what you want: All running programms' processID and EXE filename/path.
0
 

Author Comment

by:DelphiOnly
ID: 1345851
yup. i want the answer to be posted here and not through email.
 thx.
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1345852
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.
0
 
LVL 4

Expert Comment

by:dwwang
ID: 1345853
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.
0
 
LVL 4

Expert Comment

by:dwwang
ID: 1345854
By the way, I use D4 so there is a StrToInt64
0
 

Author Comment

by:DelphiOnly
ID: 1345855
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 ?
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1345856
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.
0
 
LVL 4

Expert Comment

by:dwwang
ID: 1345857
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}
0
 

Author Comment

by:DelphiOnly
ID: 1345858
Madshi,

are there any "documents" on the net about the "undocumented" APIs :-)

dwwang,
Thx for introducing me to ToolHelp functions !
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1345859
Maybe there are some, but I don't know where. Sorry...
0

Featured Post

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
This video shows how to quickly and easily add an email signature for all users on Exchange 2016. The resulting signature is applied on a server level by Exchange Online. The email signature template has been downloaded from: www.mail-signatures…
The Email Laundry PDF encryption service allows companies to send confidential encrypted  emails to anybody. The PDF document can also contain attachments that are embedded in the encrypted PDF. The password is randomly generated by The Email Laundr…

860 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question