Link to home
Start Free TrialLog in
Avatar of Clive Henson
Clive HensonFlag for United Kingdom of Great Britain and Northern Ireland

asked on

EnumProcessModules - error 299 - 'Only part of a Read/WriteProcessMemory

When using the API on W7 64 bit and looking at a 64 bit process the error above occurs.
I am trying to get the Application Name and Path from the Window Handle

The reason is explained in:
http://msdn.microsoft.com/en-us/library/ms682631(v=vs.85).aspx

I think the only solution is a 64 bit DLL or COM object which can be called from a 32 bit application.  Are you aware of such a component.
Avatar of Ephraim Wangoya
Ephraim Wangoya
Flag of United States of America image


I just did this in Delphi 2010, Windows 7 64 Bit and it worked fine for me
unit Unit3;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ShellAPI, StdCtrls, PSapi;

type
  TForm3 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    FProcessID: Integer;
  public
    { Public declarations }
  end;

var
  Form3: TForm3;

implementation

{$R *.dfm}

procedure GetProcessNames(AList: TStrings);
var
  PIDArray: array [0..1023] of DWORD;
  cbNeeded: DWORD;
  I: Integer;
  ProcessCount: Integer;
  hMod: HMODULE;
  hProcess: THandle;
  ModuleName: array [0..512] of Char;
begin
  if EnumProcesses(@PIDArray, SizeOf(PIDArray), cbNeeded) then
  begin
    ProcessCount := cbNeeded div SizeOf(DWORD);
    for I := 0 to ProcessCount - 1 do
    begin
      hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, PIDArray[I]);
      if (hProcess <> 0) then
      try
        EnumProcessModules(hProcess, @hMod, SizeOf(hMod), cbNeeded);
        GetModuleFilenameEx(hProcess, hMod, ModuleName, SizeOf(ModuleName));
        AList.Add(string(ModuleName));
      finally
        CloseHandle(hProcess);
      end;
    end;
  end;
end;

procedure TForm3.Button1Click(Sender: TObject);
begin
  GetProcessNames(Memo1.Lines);
end;

end.

Open in new window

Can't you do a 64 bit build of the app (and use EnumProcessModulesEx) ?
Are you using C++ (native)?

Anyway, consider using WMI. It will allow you to lista all you need.
Avatar of Clive Henson

ASKER

No current version of Delphi outputs 64 bit code, so suggestion from AndyAinscow will not work.
I have considered building a 64 bit DLL to do this but this would involve working in some other development system which I would prefer to avoid if possible.

I will look at the other suggestions.

GHG-RCH

Can you post the code you are using
Well the question is posted in the MFC question area.  
It was not clear from the beginning what development tool OOP is using.
That is why i asked question:
Are you using C++ (native)?

I did not get an answer but from following posts it looks like the answer is NO it is Delphi.
ewangoya
Thanks for your help
BUT
this code appears to work, but it only shows 32 bit applications.
If you run on W7 64 bit then 64 bit applications appear in the list but the file Path/Name is gibberish.

See the link in the original questions for details

I have extended the code with a stripped down version of my ExeFromWindow routine and some additional buttons and a timer to assist in testing. The timer picks up the active window handle unless it it is this form. Then clicking the ActiveWin button calls ExeFromWindow for that handle. Try it after focssing a 32 bit application (e.g. Word 2003) then try NotePad or Windows Explorer, both 64 bit apps on  W7-64. The same gibberish is returned.
unit ProcessListU;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ShellAPI, StdCtrls, PSapi, ExtCtrls;


type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Button2: TButton;
    Button3: TButton;
    Timer1: TTimer;
    Button4: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button4Click(Sender: TObject);
  private
    FProcessID: Integer;
        fActiveWindow:  HWnd;
    fWinCount:      Integer;
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure GetProcessNames(AList: TStrings);
var
  PIDArray: array [0..1023] of DWORD;
  cbNeeded: DWORD;
  I: Integer;
  ProcessCount: Integer;
  hMod: HMODULE;
  hProcess: THandle;
  ModuleName: array [0..512] of Char;
begin
  if EnumProcesses(@PIDArray, SizeOf(PIDArray), cbNeeded) then
  begin
    ProcessCount := cbNeeded div SizeOf(DWORD);
    for I := 0 to ProcessCount - 1 do
    begin
      hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, PIDArray[ I ]);
      if (hProcess <> 0) then
      try
        EnumProcessModules(hProcess, @hMod, SizeOf(hMod), cbNeeded);
        GetModuleFilenameEx(hProcess, hMod, ModuleName, SizeOf(ModuleName));
        AList.Add(string(ModuleName));
      finally
        CloseHandle(hProcess);
      end;
    end;
  end;
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Lines.Clear;
  GetProcessNames(Memo1.Lines);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Memo1.SelectAll;
  Memo1.CopyToClipboard;
  Memo1.SelLength := 0;
end;

Function ExeFromWindow(ThisHandle: HWnd; NameOnly:Boolean=False):String;
   {Use API calls to return the EXE name for the specified window}
   {This one based on Brian Long reply}
   {See old function below}


 function Wnd2PID(Wnd: THandle): DWord;
 var
  ReturnVal: DWord;
 begin
  Result := 0;
  try
   GetWindowThreadProcessId(Wnd, @ReturnVal);
   Result := ReturnVal;
  except
    {ShowException('Wnd2PID');}
  end;{except}
 end;

 function PID2Name(PID: DWord): String;
 var
   PH, MH: THandle;
   Needed: DWord;
   ModName: array[0..MAX_PATH] of Char;
 begin
  try
    Result := '';
    PH := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, PID);
    try
      try
       Win32Check(EnumProcessModules(PH, @MH, SizeOf(MH), Needed));
      except
        {Technical Debt: Throw away exceptions 299 error in 64bit W7}
      end;
      try
       Win32Check(Bool(GetModuleFileNameEx(PH, MH, ModName, SizeOf(ModName))));
      except
        {Technical Debt: Throw away exceptions 299 error in 64bit W}
      end;
      Result := ModName
    finally
      CloseHandle(PH)
    end{finally}
  except
    {ShowException('PID2Name');}
  end;{except}
end; {PID2Name}

var
 ExeText: String;
begin
  try
    try
      if ThisHandle=0
      then result := ''
      else begin
          ExeText := PID2Name(Wnd2PID(ThisHandle));
          if NameOnly
          then Result := ExtractFileName(ExeText)
          else Result := ExeText;
          end;
    except
      Result := '';;
      {ShowException('ExeFromWindow')}
    end;{except}
  Finally
  end;{Finally}
end;{ExeFromWindow}

procedure TForm1.Button3Click(Sender: TObject);
begin
  Memo1.Lines.Add(IntToStr(fWinCount)+':'+ExeFromWindow(fActiveWindow,False));
  Inc(fWinCount);
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  Memo1.Lines.Clear;
  fWinCount := 1;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1.Lines.Clear;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  ActiveWindow:  HWnd;
begin
  ActiveWindow := GetForeGroundWindow;  {Check the differences later}
  if ActiveWindow<>self.Handle then
     fActiveWindow:=ActiveWindow;
end;

end.
AndyAinscow JohnCz:
My apologies, I should have mentioned in the original question that i was working in Delphi 7 (though I also have the current version of Delphi 2010).

The Zones I intended to add were:
Microsoft Operating Systems, Delphi Programming,

Somehow: Windows MFC Programming, Microsoft Visual C++.Net also got added, this was not intentional.

However because at present there is no Delphi 32 bit compiler I was asking if there are any components that can provide process information on 64 bit applications to 32 bit code.
JohnCz:
I am not familiar with WMI.
The issue I am trying to sort out at present is determining the executable for a particular window from the Window handle. See the code in my previous submission.

The code I have works fine on W7-32 bit and for all 32 bit processes on W7-64 bit.

If WMI can provide this information I would be more than happy to use it.
What WMI functionality is available for this.
GHG-RCH,
As previously mentioned, 32-bbit application will not retrieve/enumerate 64-process.
WMI (Windows Management Instrumentation) is not a set of API. It is a set classes created to provide certain information about the system in general.

From MSDN:
Windows Management Instrumentation (WMI) is the Microsoft implementation of Web-based Enterprise Management (WBEM), which is an industry initiative to develop a standard technology for accessing management information in an enterprise environment. WMI uses the Common Information Model (CIM) industry standard to represent systems, applications, networks, devices, and other managed components. CIM is developed and maintained by the Distributed Management Task Force (DMTF).

I am not efficient enough with Delphi. My primary language is C++ (and C#, VB after). I have not used Delphi for eons.
I could give you some help if you were using above mentioned languages.
The only think I can mention is general remark:
WMI is designed for C++ developers, however I think any language that is able to handle ActiveX can be used. I am not sure if Delphi is one of them. Check MSDN.

I have suggested WMI since you can use it in 32-bit application and retrieve tons of information just about anything in any version of Windows 32 and 64-bit.

I am assuming that you are familiar with DLL. If it is really important for you to have 32-bit app achieving what you want in 64-bit world, I may consider writing a sample C++ dll that will do it (retrieve module given windows handle).
JohnCz:

Thanks fof this.

I have found some other answers on WMI which include Delphi code, so I should be able to get access to WMI working.

The problem I usually find with such large sets is finding the appropriate objects and properties.
if you are familiar with the WMI Process Information Objects could you point me at the relevant object names.

I will follow up WMI as it could be very useful for various things.

I am familiar with implementing and calling DLLs. I implement DLLs in Delphi for calling from some of our apps which are written in VB. I also need to interface to 3rd party DLLs particularly for device management (we do speech recognition and need to interface to various audio devices).

A 64 bit DLL that takes a Windows handle and returns the Module path would be extremely helpful. The API calls are in the ExeFromWindow routine above.

SOLUTION
Avatar of Janusz Czopowik
Janusz Czopowik
Flag of United States of America image

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
Sorry for a post spam but i have inadvertently pressed SUMBIT second time before closing page.
Please disregard this post.
ASKER CERTIFIED SOLUTION
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
I have given most of the points to ewangoya as his solution was very easy to include in my existing code and he had obviously spent some time researching and sorting out working code.

I have given the remaining points to JohnCz as the WMI solution looks capable of solving the problem and has the potential for being helpful in all sorts of similar areas. To implement it however will require quie a lot of additional reaearch and development on my side in getting WMI working in Delphi, and then identifying the objects relevant to my specific current problem. I do not have time for this at present as the problem raised is causing us serious problems on 64 bit Windows. I will look at this approach as a medium term project however
Ewangoya,
I had intended to say thank you very much for your work on this question. It was much appreciated, and resolved the problem for us.
You are welcome