www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Windows API: Strange behaviour after calling GetModuleFileNameExA

reply Tobias Wassermann <mail tobias-wassermann.de> writes:
Hi all,

I have the following code, including some functions out of the psapi.dll
(generated a lib with implib /noi /system psapi.lib
C:\windows\system32\psapi.lib):

extern (Windows)
{
  HANDLE OpenProcess(uint dwDesiredAccess, BOOL bInheritHandle, uint
dwProcessId);  
  BOOL CloseHandle(HANDLE hHandle);    
}
extern (C)
{
  BOOL EnumProcesses(DWORD* pProcessIds, DWORD cb, DWORD* pBytesReturned);
  uint GetModuleFileNameExA(HANDLE hProcess, HMODULE hModule, LPTSTR fileName,
uint size);  
}

void main(char[][] args) {
 ...
 uint[256] processIds;
 int ret = EnumProcesses(&processIds[0], processIds.length * uint.sizeof,
&byteCount);
 ...
 for(uint i=0; i<processIds.length && i<byteCount/uint.sizeof; i++) {
   uint pid =  processIds[i];   
   writefln("Process #%d - PID: %d", i, pid);   
   HANDLE hProcess = OpenProcess(0x410 /* QueryInformation | VMRead */, false,
pid);
   if(cast(int)hProcess>0)
     CloseHandle(hProcess);
 }
}

Running this code works fine. I'll get a list like:

Process #1 - PID: 4
Process #2 - PID: 780
Process #3 - PID: 836
...

But now I'll change the for-loop:

for(uint i=0; i<processIds.length && i<byteCount/uint.sizeof; i++)
{
  uint pid =  processIds[i];   
  writefln("Process #%d - PID: %d", i, pid);   
  HANDLE hProcess = OpenProcess(0x410 /* QueryInformation | VMRead */, false,
pid);
  if(cast(int)hProcess>0)
  {
    processFileName.length = 300;
    uint namelength = 0;
    namelength = GetModuleFileNameExA(hProcess, cast(HMODULE)null,
processFileName.ptr, processFileName.length);        
    CloseHandle(hProcess);
  }
}

With the call of GetModuleFileNameExA() my process id list will be corrupted,
I'll get something like this:

Process #1 - PID: 4
Process #2 - PID: 780
Process #3 - PID: 836
Process #4 - PID: 4298544
Process #6 - PID: 1244976
Process #7 - PID: 4202711
Process #8 - PID: 1040
Process #9 - PID: 1288
Process #10 - PID: 1332

Process #4 to Process #8: These are invalid process id's - processes with these
id doesn't exist, the next real id is 1288.

I don't know why this happens, looking to the MSDN it gives me the following
function header:

DWORD GetModuleFileNameEx(
  HANDLE hProcess,
  HMODULE hModule,
  LPTSTR lpFilename,
  DWORD nSize
);

So I think, my declaration within D should be correct:

uint GetModuleFileNameExA(HANDLE hProcess, HMODULE hModule, LPTSTR fileName,
uint size);  

Do anybody have any suggestions?

Thanks,

Tobias
Nov 26 2007
next sibling parent Regan Heath <regan netmail.co.nz> writes:
How have you declared processFileName?  Perhaps "char[] 
processFileName;"?  Is it declared in main right next to processIds?
Nov 26 2007
prev sibling parent reply Tobias Wassermann <mail tobias-wassermann.de> writes:
Regan Heath Wrote:

 How have you declared processFileName?  Perhaps "char[] 
 processFileName;"?  Is it declared in main right next to processIds?
Yes, it is declared just before the for-loop as: char[] processFileName;
Nov 26 2007
parent reply Regan Heath <regan netmail.co.nz> writes:
Tobias Wassermann wrote:
 Regan Heath Wrote:
 
 How have you declared processFileName?  Perhaps "char[] 
 processFileName;"?  Is it declared in main right next to processIds?
Yes, it is declared just before the for-loop as: char[] processFileName;
The thought I had was that perhaps GetModuleFileNameExA was overwriting the wrong memory and corrupting the process list. Can you post a complete working piece of code which exhibits the bug, I'd like to help by debugging it locally. Regan
Nov 27 2007
parent reply Tobias Wassermann <mail ingrid-wassermann.de> writes:
Hi,

here is the code:

import std.stdio;
import std.c.windows.windows;

extern (Windows) HANDLE OpenProcess(uint dwDesiredAccess, BOOL bInheritHandle,
uint dwProcessId);    

extern (C) 
{
  BOOL EnumProcesses(DWORD* pProcessIds, DWORD cb, DWORD* pBytesReturned);
  DWORD GetModuleFileNameExA(HANDLE hProcess, HMODULE hModule, char* fileName,
uint size);  
}

void main(char[][] args)
{
  uint[256] processIds;
  uint byteCount;
  char[] processFileName;
  int ret = EnumProcesses(processIds.ptr, processIds.length*uint.sizeof,
&byteCount);
  if(ret!=0)
  {
    for(uint i=0; i<processIds.length && i<byteCount/uint.sizeof; i++)
    {
      if(processIds[i]==0)
        continue;
      uint pid =  processIds[i];   
      writefln("Process #%d - PID: %d", i, pid);   
      
      HANDLE hProcess = OpenProcess(0x410 /* QueryInformation | VMRead */,
false, pid);
      if(cast(int)hProcess>0)
      {
        processFileName.length = 300;
        uint namelength = 0;
        namelength = GetModuleFileNameExA(hProcess, cast(HMODULE)0,
processFileName.ptr, processFileName.length);
        processFileName.length = namelength;
        writefln("=> %s", processFileName);        
        CloseHandle(hProcess);
      }
    }
  }
}

If you comment out the  namelength = GetModuleFileNameExA(hProcess,
cast(HMODULE)0, processFileName.ptr, processFileName.length); line, all works
fine.

I also tried a char* allocated memory block with malloc() instead of the
processFileName.ptr - so I thought about a bug between the communication
between D and the Windows API, but the result is the same: a corrupted uint[]
process-id-array.

Bye

Tobias


Regan Heath Wrote:

 
 The thought I had was that perhaps GetModuleFileNameExA was overwriting 
 the wrong memory and corrupting the process list.
 
 Can you post a complete working piece of code which exhibits the bug, 
 I'd like to help by debugging it locally.
 
 Regan
Nov 27 2007
next sibling parent reply Regan Heath <regan netmail.co.nz> writes:
This is totally whacky...  using the code posted below I get the 
following output:

a 4
b 2000
c
d
e 883C00 300
f 883C00 300
g 883C00 300
h 884FF0 4296896

The output for both g and h are written with:
writefln("h %p %d", processFileName.ptr, processFileName.length);

The line between the two which causes this behaviour is
processFileName = "Unknown".dup;

PID of 4 is of course "System".

Removing the call to GetModuleFileNameExA prevents the problem.

My guess is that GetModuleFileNameExA is corrupting the stack somehow, 
perhaps the implib didn't correctly convert the dll to a lib.  I used:

implib /noi /system psapi.lib C:\windows\system32\psapi.dll

The full code:

import std.stdio;
import std.c.windows.windows;

extern (Windows) HANDLE OpenProcess(uint dwDesiredAccess, BOOL 
bInheritHandle, uint dwProcessId);

extern (C)
{
   BOOL EnumProcesses(DWORD* pProcessIds, DWORD cb, DWORD* pBytesReturned);
   DWORD GetModuleFileNameExA(HANDLE hProcess, HMODULE hModule, char* 
fileName, uint size);
}

void main(char[][] args)
{
   char[] processFileName;
   uint[] processIds;
   uint[] processIds2;
   uint byteCount;

   processIds.length = 256;

   int ret = EnumProcesses(processIds.ptr, 
processIds.length*uint.sizeof, &byteCount);
   if(ret!=0)
   {
     processIds.length = byteCount/uint.sizeof;
     processIds2 = processIds.dup;

     foreach(i, pid; processIds)
     {
       if(pid==0) continue;
       writefln("a %d", pid);
       HANDLE hProcess = OpenProcess(0x410 /* QueryInformation | VMRead 
*/, false, pid);
       writefln("b %d", cast(int)hProcess);
       if(cast(int)hProcess>0)
       {
         writefln("c");
         processFileName.length = 300;
         writefln("d");
         uint namelength = 0;
         writefln("e %p %d", processFileName.ptr, 
processFileName.length);
         namelength = GetModuleFileNameExA(hProcess, cast(HMODULE)0, 
processFileName.ptr, processFileName.length);
         writefln("f %p %d", processFileName.ptr, 
processFileName.length);
         if (namelength == 0)
         {
           writefln("g %p %d", processFileName.ptr, processFileName.length);
           processFileName = "Unknown".dup;
           writefln("h %p %d", processFileName.ptr, processFileName.length);
         }
         else
         {
           writefln("i");
           processFileName.length = namelength;
           writefln("j");
         }
         break;
         writefln("%d. (%d) => %s", i, pid, processFileName);
         CloseHandle(hProcess);
       }
     }
   }
}
Nov 27 2007
parent Tobias Wassermann <mail ingrid-wassermann.de> writes:
Could be an implib problem - I ported the code to C and compiled with DMC, the
same thing.

Used C-Code:

#include <stdio.h>
#include <windows.h>

HANDLE OpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD
dwProcessId);    
CloseHandle(HANDLE hHandle);
BOOL EnumProcesses(DWORD* pProcessIds, DWORD cb, DWORD* pBytesReturned);
DWORD GetModuleFileNameExA(HANDLE hProcess, HMODULE hModule, char* fileName,
DWORD size);  


void main()
{
  unsigned long processIds[256];
  unsigned long byteCount;
  char processFileName[300];
  int ret = EnumProcesses(&processIds[0], 256*sizeof(unsigned long),
&byteCount);
  
  if(ret!=0)
  {
    for(DWORD i=0; i<256 && i<byteCount/sizeof(unsigned long); i++)
    {
      if(processIds[i]==0)
        continue;
      unsigned int pid =  processIds[i];   
      printf("Process #%d - PID: %d\r\n", i, pid);   
      
      HANDLE hProcess = OpenProcess(0x410, FALSE, pid);
      if((int)hProcess>0)
      {
        unsigned int namelength = GetModuleFileNameExA(hProcess, NULL,
&processFileName[0], 300);
        printf("=> %s\r\n", processFileName); 
        CloseHandle(hProcess);
      }
    }
  }
}

Compiled with:

dmc -L/+psapi/noi ProcessFinderTest.c

Uncomment the GetModuleFileNameExA-Call and all works fine - otherthise the
array will be corrupted.

Regan Heath Wrote:

 This is totally whacky...  using the code posted below I get the 
 following output:
 
 a 4
 b 2000
 c
 d
 e 883C00 300
 f 883C00 300
 g 883C00 300
 h 884FF0 4296896
 
 The output for both g and h are written with:
 writefln("h %p %d", processFileName.ptr, processFileName.length);
 
 The line between the two which causes this behaviour is
 processFileName = "Unknown".dup;
 
 PID of 4 is of course "System".
 
 Removing the call to GetModuleFileNameExA prevents the problem.
 
 My guess is that GetModuleFileNameExA is corrupting the stack somehow, 
 perhaps the implib didn't correctly convert the dll to a lib.  I used:
 
 implib /noi /system psapi.lib C:\windows\system32\psapi.dll
Nov 27 2007
prev sibling parent reply Joel Lucsy <jjlucsy gmail.com> writes:
Tobias Wassermann wrote:
 extern (Windows) HANDLE OpenProcess(uint dwDesiredAccess, BOOL bInheritHandle,
uint dwProcessId);    
 extern (C) 
 {
   BOOL EnumProcesses(DWORD* pProcessIds, DWORD cb, DWORD* pBytesReturned);
   DWORD GetModuleFileNameExA(HANDLE hProcess, HMODULE hModule, char* fileName,
uint size);  
 }
Ah, I would tend to think that EnumProcesses and GetModuleFileNameExA should be extern(Windows) as well. All Win32 api's use STDCALL, which is what I believe extern(Windows) sets. -- Joel Lucsy "The dinosaurs became extinct because they didn't have a space program." -- Larry Niven
Nov 27 2007
parent reply Regan Heath <regan netmail.co.nz> writes:
Joel Lucsy wrote:
 Tobias Wassermann wrote:
 extern (Windows) HANDLE OpenProcess(uint dwDesiredAccess, BOOL 
 bInheritHandle, uint dwProcessId);    extern (C) {
   BOOL EnumProcesses(DWORD* pProcessIds, DWORD cb, DWORD* 
 pBytesReturned);
   DWORD GetModuleFileNameExA(HANDLE hProcess, HMODULE hModule, char* 
 fileName, uint size);  }
Ah, I would tend to think that EnumProcesses and GetModuleFileNameExA should be extern(Windows) as well. All Win32 api's use STDCALL, which is what I believe extern(Windows) sets.
I thought the same thing but couldn't get it to link with extern(Windows) on those two. I've just looked at the psapi.h header in my SDK I see: #ifdef __cplusplus extern "C" { #endif BOOL WINAPI EnumProcesses( DWORD * lpidProcess, DWORD cb, DWORD * cbNeeded ); note the extern "C" there. I believe that confirms it's C linkage and not Windows linkage. Thanks for the suggestion though :) It seems the same problem occurs when Tobias calls the routines from C which suggests a problem with the implib used to convert the dll into a library. Regan
Nov 27 2007
parent reply Joel Lucsy <jjlucsy gmail.com> writes:
Regan Heath wrote:
 I've just looked at the psapi.h header in my SDK I see:
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 BOOL
 WINAPI
 EnumProcesses(
     DWORD * lpidProcess,
     DWORD   cb,
     DWORD * cbNeeded
     );
 
 note the extern "C" there.  I believe that confirms it's C linkage and 
 not Windows linkage.
Actually, the WINAPI is what sets STDCALL. I believe the extern "C" is for telling the compiler how to mangle, or not mangle in this case, the name of the function. -- Joel Lucsy "The dinosaurs became extinct because they didn't have a space program." -- Larry Niven
Nov 27 2007
parent reply Tobias Wassermann <mail tobias-wassermann.de> writes:
The problem is:

EnumProcesses() and GetModuleFileNameExA() both are marked as extern "C" within
the psapi.h - the difference is: EnumProcesses works and GetModuleFileNameExA()
in D and with DMC seems to corrupt the stack. If I move the C-ported code to
the Microsoft Visual C++ and compile it, it works fine.

On the other side: extern (C) within D is the only possibility to use for this
two functions, if I use extern (Windows) I'll get linker errors (linker can not
 find symbols _EnumProcesses 12 and _GetModuleFileNameExA 16).

With extern (Windows) it seems to be correct - remember: EnumProcesses() works
fine, only GetModuleFileNameExA() causes the problem. 

Additional note: GetProcessImageFileNameA() (which could be an alternative to
GetModuleFileNameExA()) causes the same problem.

Joel Lucsy Wrote:

 Regan Heath wrote:
 I've just looked at the psapi.h header in my SDK I see:
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 BOOL
 WINAPI
 EnumProcesses(
     DWORD * lpidProcess,
     DWORD   cb,
     DWORD * cbNeeded
     );
 
 note the extern "C" there.  I believe that confirms it's C linkage and 
 not Windows linkage.
Actually, the WINAPI is what sets STDCALL. I believe the extern "C" is for telling the compiler how to mangle, or not mangle in this case, the name of the function. -- Joel Lucsy "The dinosaurs became extinct because they didn't have a space program." -- Larry Niven
Nov 27 2007
parent reply Carlos Santander <csantander619 gmail.com> writes:
Tobias Wassermann escribió:
 The problem is:

 EnumProcesses() and GetModuleFileNameExA() both are marked as extern "C"
 within the psapi.h - the difference is: EnumProcesses works and
 GetModuleFileNameExA() in D and with DMC seems to corrupt the stack. If I
 move the C-ported code to the Microsoft Visual C++ and compile it, it works
 fine.

 On the other side: extern (C) within D is the only possibility to use for
 this two functions, if I use extern (Windows) I'll get linker errors (linker
 can not  find symbols _EnumProcesses 12 and _GetModuleFileNameExA 16).

 With extern (Windows) it seems to be correct - remember: EnumProcesses()
 works fine, only GetModuleFileNameExA() causes the problem.

 Additional note: GetProcessImageFileNameA() (which could be an alternative to
 GetModuleFileNameExA()) causes the same problem.
You could use a .def file to alias _EnumProcesses 12 to EnumProcesses. -- Carlos Santander Bernal
Nov 28 2007
parent reply Tobias Wassermann <mail tobias-wassermann.de> writes:
How could this be done? I never made an alias via a def-file before ;)


  > On the other side: extern (C) within D is the only possibility to use 
 for
  > this two functions, if I use extern (Windows) I'll get linker errors 
 (linker
  > can not  find symbols _EnumProcesses 12 and _GetModuleFileNameExA 16).
  >
  > With extern (Windows) it seems to be correct - remember: EnumProcesses()
  > works fine, only GetModuleFileNameExA() causes the problem.
  >
  > Additional note: GetProcessImageFileNameA() (which could be an 
 alternative to
  > GetModuleFileNameExA()) causes the same problem.
  >
 
 You could use a .def file to alias _EnumProcesses 12 to EnumProcesses.
 
Nov 28 2007
parent Carlos Santander <csantander619 gmail.com> writes:
Tobias Wassermann escribió:
 How could this be done? I never made an alias via a def-file before ;)
 
 
  > On the other side: extern (C) within D is the only possibility to 
 use for
  > this two functions, if I use extern (Windows) I'll get linker 
 errors (linker
  > can not  find symbols _EnumProcesses 12 and _GetModuleFileNameExA 16).
  >
  > With extern (Windows) it seems to be correct - remember: 
 EnumProcesses()
  > works fine, only GetModuleFileNameExA() causes the problem.
  >
  > Additional note: GetProcessImageFileNameA() (which could be an 
 alternative to
  > GetModuleFileNameExA()) causes the same problem.
  >

 You could use a .def file to alias _EnumProcesses 12 to EnumProcesses.
Check http://www.digitalmars.com/ctg/ctgDefFiles.html#exports -- Carlos Santander Bernal
Nov 29 2007