Hello
Is there a way to get information about Windows XP processes from Squeak? Smth like how much RAM is the process using? What is PID of the process? And so on. Any hints or workaraunds are very much welcome. Best regards, Valdas Bucinskas |
On Tue, Jan 31, 2006 at 02:34:25PM +0200, Valdas Bucinskas wrote:
> Hello > > Is there a way to get information about Windows XP processes from Squeak? > Smth like how much RAM is the process using? What is PID of the process? And > so on. > Any hints or workaraunds are very much welcome. See OSProcess and OSProcessPlugin on Squeak Map. There is an old version of the plugin (Win32OSProcessPlugin.dll) included in the OSProcess distribution, but you'll need to build your own from source with VMMaker if you want the latest. The old one will give you pid, and the more recent OSPP also gives the environment, the current working directory, main thread ID, etc. For the other things you might be interested in (memory use), you might want to just use one of the existing OSPP primitives as a template and put in whatever you want. One word of warning, the latest SVN sources for Windows VM have a security related "feature" that will break some of the OSPP file-related functions. Let me know if this is a problem for you and I'll try to come up with some sort of workaround. It's not something I'm working on at the moment though. Dave |
In reply to this post by Valdas Bucinskas
"Valdas Bucinskas" <[hidden email]> wrote:
> Hello > > Is there a way to get information about Windows XP processes from Squeak? > Smth like how much RAM is the process using? What is PID of the process? > And so on. > Any hints or workaraunds are very much welcome. The FFI interface is the framework that you can use to program what you need. (see also: http://minnow.cc.gatech.edu/squeak/1414 and http://minnow.cc.gatech.edu/squeak/2422) To get the process id you have to do this: 1. In Win32Window, add this instance method: apiGetCurrentProcessId <apicall: long 'GetCurrentProcessId' (void) module: 'kernel32.dll'> ^self externalCallFailed 2. Then try in a workspace: Win32Window new apiGetCurrentProcessId Getting a handle to the current process is not much more difficult: 1. you have to add these instance methods: apiGetCurrentProcess <apicall: Win32Handle 'GetCurrentProcess' (void) module: 'kernel32.dll'> ^self externalCallFailed apiGetGuiResources: h type: int <apicall: long 'GetGuiResources' (Win32Handle long) module: 'user32.dll'> ^self externalCallFailed 2. Then you can try: | interface | " gdi resources " interface := Win32Window new. interface apiGetGuiResources: interface apiGetCurrentProcess type: 0. | interface | " user resources " interface := Win32Window new. interface apiGetGuiResources: interface apiGetCurrentProcess type: 1. The attached change set contains these methods and a class definition that is needed to obtain memory usage information. Please have a look at the class comment for Win32ProcessMemoryCounters to find out how you can obtain memory information. It is perhaps necessary to explicitely release (with free) some of the Win32 structures, but I am not sure about that. Comments are highly welcome. Hope that helps a bit. Boris FFIWin.1.cs (4K) Download Attachment |
In reply to this post by David T. Lewis
Thanks for the response.
For those who are interested, I have found another workaraound using external utility Pslist (http://www.sysinternals.com/Utilities/PsList.html). The code, returning how much RAM a Windows process is using, looks like this (needs some refactoring though): Prerequests: directory C:\Temp2 should exist, pslist.exe should be copied into the directory that is listed in Windows Paths variable. WindowsOS >> class getInfo: aProcessName | path batch batchFile infoFromOutTxt numbers oc tmp filedir | path := 'C:\Temp2'. batch := 'pslist -m ' , aProcessName , ' >>' , path , '\out.txt'. filedir := FileDirectory new. filedir deleteFileNamed: path , '\out.txt' ifAbsent: nil. filedir deleteFileNamed: path , '\getProcessInfo.bat' ifAbsent: nil. [batchFile := (FileDirectory on: path) fileNamed: 'getProcessInfo.bat'. batchFile nextPutAll: batch] ensure: [batchFile close]. Win32Shell new shellOpen: path , '\' , 'getProcessInfo.bat'. [(filedir fileExists: path , '\out.txt') not] whileTrue: [(Delay forSeconds: 0.1) wait]. infoFromOutTxt := (FileDirectory new oldFileNamed: path , '\out.txt') upToEnd. numbers := (infoFromOutTxt copyFrom: (infoFromOutTxt findString: aProcessName startingAt: 1) + aProcessName size to: infoFromOutTxt size) withBlanksTrimmed. oc := OrderedCollection new. tmp := ''. numbers do: [:e | e = $ ifTrue: [oc add: tmp. tmp := ''] ifFalse: [tmp := tmp asString , e asString]]. oc removeAllSuchThat: [:e | e = '']. ^ oc fourth asNumber E.g.: Running from workspace: WindowsOS getInfo: 'explorer' shows how much RAM is explorer.exe using Best regards, Valdas Bucinskas -----Original Message----- From: [hidden email] [mailto:[hidden email]]On Behalf Of David T. Lewis Sent: Tuesday, January 31, 2006 2:54 PM To: The general-purpose Squeak developers list Subject: Re: Windows process info from Squeak On Tue, Jan 31, 2006 at 02:34:25PM +0200, Valdas Bucinskas wrote: > Hello > > Is there a way to get information about Windows XP processes from Squeak? > Smth like how much RAM is the process using? What is PID of the process? And > so on. > Any hints or workaraunds are very much welcome. See OSProcess and OSProcessPlugin on Squeak Map. There is an old version of the plugin (Win32OSProcessPlugin.dll) included in the OSProcess distribution, but you'll need to build your own from source with VMMaker if you want the latest. The old one will give you pid, and the more recent OSPP also gives the environment, the current working directory, main thread ID, etc. For the other things you might be interested in (memory use), you might want to just use one of the existing OSPP primitives as a template and put in whatever you want. One word of warning, the latest SVN sources for Windows VM have a security related "feature" that will break some of the OSPP file-related functions. Let me know if this is a problem for you and I'll try to come up with some sort of workaround. It's not something I'm working on at the moment though. Dave |
In reply to this post by Boris.Gaertner
Hello,
I liked your example a lot. I was playing with it recently, and got some questions. 1. Is there any documentation on what methods can be called from user32.dll, kernel32.dll, psapi.dll (the dlls I see in Win32Window instance methods)? 2. I tried to know some information about other process than Squeak.exe, but couldn't figure out how to do that ? Comments are as always welcome. Best regards, Valdas Bucinskas -----Original Message----- From: [hidden email] [mailto:[hidden email]]On Behalf Of Boris Gaertner Sent: Tuesday, January 31, 2006 8:27 PM To: The general-purpose Squeak developers list Subject: Re: Windows process info from Squeak "Valdas Bucinskas" <[hidden email]> wrote: > Hello > > Is there a way to get information about Windows XP processes from Squeak? > Smth like how much RAM is the process using? What is PID of the process? > And so on. > Any hints or workaraunds are very much welcome. The FFI interface is the framework that you can use to program what you need. (see also: http://minnow.cc.gatech.edu/squeak/1414 and http://minnow.cc.gatech.edu/squeak/2422) To get the process id you have to do this: 1. In Win32Window, add this instance method: apiGetCurrentProcessId <apicall: long 'GetCurrentProcessId' (void) module: 'kernel32.dll'> ^self externalCallFailed 2. Then try in a workspace: Win32Window new apiGetCurrentProcessId Getting a handle to the current process is not much more difficult: 1. you have to add these instance methods: apiGetCurrentProcess <apicall: Win32Handle 'GetCurrentProcess' (void) module: 'kernel32.dll'> ^self externalCallFailed apiGetGuiResources: h type: int <apicall: long 'GetGuiResources' (Win32Handle long) module: 'user32.dll'> ^self externalCallFailed 2. Then you can try: | interface | " gdi resources " interface := Win32Window new. interface apiGetGuiResources: interface apiGetCurrentProcess type: 0. | interface | " user resources " interface := Win32Window new. interface apiGetGuiResources: interface apiGetCurrentProcess type: 1. The attached change set contains these methods and a class definition that is needed to obtain memory usage information. Please have a look at the class comment for Win32ProcessMemoryCounters to find out how you can obtain memory information. It is perhaps necessary to explicitely release (with free) some of the Win32 structures, but I am not sure about that. Comments are highly welcome. Hope that helps a bit. Boris |
From: "Valdas Bucinskas" <[hidden email]> > Hello, > I liked your example a lot. I was playing with it recently, and got some > questions. > 1. Is there any documentation on what methods can be called from user32.dll, > kernel32.dll, psapi.dll (the dlls I see in Win32Window instance methods)? Documentation is a problem. You really need the documentation of (one of the newer) Windows SDK. Programmers that have connection time for free or at a very low rate can read this documentation in the internet. Programmers that have access to the IBM Smalltalk system can look into class OSCall and pool dictionary PlatformFunctions to see what is available for Windows 95 (To the best of my knowledge, the calls for many newer API functions are not provided in OSCall, but can easily be added by an user that needs them.) > 2. I tried to know some information about other process than Squeak.exe, but > couldn't figure out how to do that ? Newer Windows versions have the API function EnumProcesses (in psapi.dll) that let you da that. The function writes the IDs of all processes into an array. In squeak, you use a ByteArray as argument for the method call and some auxiliary protocol form ByteArray to convert the bytes into integers. Once you have the process IDs, you can collect all process-related information for the processes. For autentic information, please read this page: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/perfmon/bas e/enumprocesses.asp The attached changes set contains additional methods for these API functions: EnumProcesses OpenProcess CloseHandle LastError With these you can evaluate this example (you find it also in the preambble of the change set. ): | interface array uint cb x err handleArray i j upperIdx oc processHandle | interface := Win32Window new. array := ByteArray new: 8000. " sufficient for up to 2000 processes " uint := ByteArray new: 4. cb := 8000. x := interface apiEnumProcesses: array size: cb bytesReturned: uint . x ifFalse: [ err := interface getLastError. err]. handleArray := OrderedCollection new: 2000. " convert the bytes into unsigned integers " upperIdx := uint unsignedLongAt: 1. j := i := 1. [j < upperIdx] whileTrue: [ handleArray add: (array unsignedLongAt: j). i := i + 1. j := j + 4. ]. " use the process IDs to open the processes and to query the amount of resources they use. " oc := OrderedCollection new. handleArray do: [:idx | processHandle := interface getProcessHandleWithAccessRights: 16r400 inherit: true processId: idx. processHandle notNil ifTrue: [ oc add: (Array with: idx with: (interface apiGetGuiResources: processHandle type: 0) with: (interface apiGetGuiResources: processHandle type: 1) ). interface closeHandle: processHandle.] ]. oc inspect " It is a nice idea to compare this with the data that are shown in the Task Manager " ====================================== I also fixed a bug from the example that I posted yesterday: The typeSIZE_T is an unsigned long integer (That is what I found in the file _stddef.h of the Borland c++ compiler). In my example from yesterday I interpreted these values as signed integers. This is wrong and should be changed. The subclasses of ExternalStructure understand the class method defineFields that creates the accessors for all items that are declared in the fields method. You see that the use of FFI is not really difficult; what you have to do it to translate the specifications from the Microsoft Documentation into api calls. Have success. Gretings Boris FFIWin.2.cs (8K) Download Attachment |
Hello,
Wow, your examples are really interesting and helps incredibly much. I modified your your last example slightly, incorporating RAMs used by the process. And then tried to find the .exe that calls the process. It appears, the method GetModuleBaseName from psapi.dll should be used, which is described in http://msdn.microsoft.com/library/default.asp?url=/library/en-us/perfmon/bas e/getmodulebasename.asp. It gives the method declaration is DWORD GetModuleBaseName( HANDLE hProcess, HMODULE hModule, LPTSTR lpBaseName, DWORD nSize ); What I miss now is the knowledge, how do I convert types from c++ like into Sueak ones. I guess hProcess corresponds to Win32Handle, lpBaseName corresponds to char*, and nSize corresponds to long. GetModuleBaseName returns long. 1. But what does hModule correspond to? My guess <apicall: long 'GetModuleBaseName' (Win32Handle Win32Handle char* long) module: 'psapi.dll'> throughs Error 13, which is 'The data is invalid.' 2. Should a class similar to Win32Handle be created in order to hModule typecasting work? ================ Modified piece of code (stopped while trying to make the apiGetProcessName work ): "use the process IDs to open the processes and to query the amount of resources they use." oc := OrderedCollection new. handleArray do: [:idx | processHandle := interface getProcessHandleWithAccessRights: 1024 inherit: true processId: idx. processHandle notNil ifTrue: [memRec := Win32ProcessMemoryCounters new. interface apiGetProcessMemoryInfoFor: processHandle into: memRec descriptorSize: memRec class byteSize. processName := ''. processInfo := Dictionary new. processInfo at: 'PID' put: idx. processInfo at: 'GDI Objects' put: (interface apiGetGuiResources: processHandle type: 0). processInfo at: 'USER Objects' put: (interface apiGetGuiResources: processHandle type: 1). processInfo at: 'Mem Usage' put: memRec WorkingSetSize. processInfo at: 'Process Name' put: nil. "(interface apiGetProcessName: processHandle module: nil into: processName size: 40)." oc add: processInfo. interface closeHandle: processHandle]]. ^ oc "It is a nice idea to compare this with the data that are shown in the Task Manager Yeah, it is the same! It is like a little Task Manager in Squeak! " Best regards, Valdas Bucinskas |
Am 02.02.2006 um 12:30 schrieb Valdas Bucinskas: > Hello, > > Wow, your examples are really interesting and helps incredibly much. > I modified your your last example slightly, incorporating RAMs used > by the > process. And then tried to find the .exe that calls the process. It > appears, > the method GetModuleBaseName from psapi.dll should be used, which is > described in > http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ > perfmon/bas > e/getmodulebasename.asp. > It gives the method declaration is > DWORD GetModuleBaseName( > HANDLE hProcess, > HMODULE hModule, > LPTSTR lpBaseName, > DWORD nSize > ); > What I miss now is the knowledge, how do I convert types from c++ > like into > Sueak ones. I guess hProcess corresponds to Win32Handle, lpBaseName > corresponds to char*, and nSize corresponds to long. > GetModuleBaseName > returns long. > 1. But what does hModule correspond to? > My guess > <apicall: long 'GetModuleBaseName' (Win32Handle Win32Handle char* > long) > module: 'psapi.dll'> > throughs Error 13, which is 'The data is invalid.' You have to check what HMODULE actually is. Look in the documentation, the header files, or some cpp output. Or guess ;-) > 2. Should a class similar to Win32Handle be created in order to > hModule > typecasting work? My guess would be hModule is just a handle as well (it starts with an h, and Windows API uses Simonyi's ["the Hungarian"] notation), so try Win32Handle as type. Or just "long", because Windows handles are ints in the end, right? We only deal with bits here, no compiler clue cudgel needed. - Bert - |
In reply to this post by Valdas Bucinskas
"Valdas Bucinskas" <[hidden email]> wrote: > Hello, > > Wow, your examples are really interesting and helps incredibly much. > I modified your your last example slightly, incorporating RAMs used by the > process. And then tried to find the .exe that calls the process. It > appears, > the method GetModuleBaseName from psapi.dll should be used, which is > described in > http://msdn.microsoft.com/library/default.asp?url=/library/en-us/perfmon/bas > e/getmodulebasename.asp. > It gives the method declaration is > DWORD GetModuleBaseName( > HANDLE hProcess, > HMODULE hModule, > LPTSTR lpBaseName, > DWORD nSize > ); > What I miss now is the knowledge, how do I convert types from c++ like > into > Sueak ones. I guess hProcess corresponds to Win32Handle, lpBaseName > corresponds to char*, and nSize corresponds to long. GetModuleBaseName > returns long. > 1. But what does hModule correspond to? > My guess > <apicall: long 'GetModuleBaseName' (Win32Handle Win32Handle char* long) > module: 'psapi.dll'> almost correct, but the name of the function is 'GetModuleBaseNameA' where the last character stands for ASCII. A DLL contents viewer helps a lot to find out such details, I use this one: http://www.nirsoft.net/utils/dll_export_viewer.html > throughs Error 13, which is 'The data is invalid.' > 2. Should a class similar to Win32Handle be created in order to hModule > typecasting work? No, that is not needed, hModule is a handle, Win32Handle is the right choice. No please try this: 1. add this api method to Win32Window: apiGetModuleBaseName: hProcess module: hModule into: lpString bufferSize: size <apicall: long 'GetModuleBaseNameA' (Win32Handle Win32Handle char* long) module: 'psapi.dll'> ^self externalCallFailed 2. Ensure that you open the process handle with access rights 16r410 (this is PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, the access right alone PROCESS_QUERY_INFORMATION does not allow you tho read the name) (The processes System, Idle and some others will not tell you their names anyway) 3. Do something like this: handleArray do: [:idx | processHandle := interface getProcessHandleWithAccessRights: 16r410 inherit: true processId: idx. processHandle notNil ifTrue: [ | buffer processName cnt | buffer := ByteArray new: 80. interface apiGetModuleBaseName: processHandle module: nil into: buffer bufferSize: 80. processName := WriteStream on: (String new: 80). cnt := 1. [i <= 80 and: [(buffer at: cnt) ~= 0]] whileTrue: [processName nextPut: (Character value: (buffer at: cnt)). cnt := cnt + 1]. processName := processName contents. oc add: (Array with: idx with: (interface apiGetGuiResources: processHandle type: 0) with: (interface apiGetGuiResources: processHandle type: 1) with: processName). interface closeHandle: processHandle. ] ]. When you have to provide buffers that are filled with some information, it is always a good choice to use ByteArrays of suitable size. You can always convert a ByteArray into a number or into a string. Hope this helps, Boris |
Hello Boris,
Thanks man! You helped me greatly, saving some weaks (months) of tedious work. Your examples were the enormous help. You could be a tallented teacher :) Best regards, Valdas Bucinskas -----Original Message----- From: [hidden email] [mailto:[hidden email]]On Behalf Of "Boris Gartner" Sent: Thursday, February 02, 2006 5:12 PM To: The general-purpose Squeak developers list Subject: RE: [FFI newbie] Windows process info from Squeak No please try this: 1. add this api method to Win32Window: apiGetModuleBaseName: hProcess module: hModule into: lpString bufferSize: size <apicall: long 'GetModuleBaseNameA' (Win32Handle Win32Handle char* long) module: 'psapi.dll'> ^self externalCallFailed 2. Ensure that you open the process handle with access rights 16r410 (this is PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, the access right alone PROCESS_QUERY_INFORMATION does not allow you tho read the name) (The processes System, Idle and some others will not tell you their names anyway) 3. Do something like this: handleArray do: [:idx | processHandle := interface getProcessHandleWithAccessRights: 16r410 inherit: true processId: idx. processHandle notNil ifTrue: [ | buffer processName cnt | buffer := ByteArray new: 80. interface apiGetModuleBaseName: processHandle module: nil into: buffer bufferSize: 80. processName := WriteStream on: (String new: 80). cnt := 1. [i <= 80 and: [(buffer at: cnt) ~= 0]] whileTrue: [processName nextPut: (Character value: (buffer at: cnt)). cnt := cnt + 1]. processName := processName contents. oc add: (Array with: idx with: (interface apiGetGuiResources: processHandle type: 0) with: (interface apiGetGuiResources: processHandle type: 1) with: processName). interface closeHandle: processHandle. ] ]. When you have to provide buffers that are filled with some information, it is always a good choice to use ByteArrays of suitable size. You can always convert a ByteArray into a number or into a string. Hope this helps, Boris |
Free forum by Nabble | Edit this page |