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