Jump to content
Paul Thomas

Processes List

Recommended Posts

I started working on this today, got it working, and figured I'd share. I still have more work to do but this is a good start. Could use cleaning up but that's for later.

 

SuperStrict

Import "-lpsapi"

Const PROCESS_QUERY_INFORMATION:Int = $0400
Const PROCESS_VM_READ:Int = $0010
Const PROCESS_VM_WRITE:Int = $0020

Extern "Win32"
Function GetLastError:Int()
Function OpenProcess:Int(dwDesiredAccess:Int, bInheritHandle:Int, dwProcessId:Int)
Function EnumProcesses(pProcessIds:Byte Ptr, cb:Int, pBytesReturned:Int Ptr)="EnumProcesses@12"
Function EnumProcessModules(hProcess:Int, lphModule:Byte Ptr, cb:Int, lpcbNeeded:Int Ptr)
Function GetModuleBaseName(hProcess:Int, hModule:Int, lpBaseName:Byte Ptr, nSize:Int) = "GetModuleBaseNameA@16"
Function TerminateProcess(hProcess:Int, uExitCode:Int)
Function CloseHandle(hObject:Int)
Function GetModuleFileName(hModule:Int, lpFileName:Byte Ptr, nSize:Int)
Function GetModuleFileNameExA(hProcess:Int, hModule:Int, lpFilename:Byte Ptr, nSize:Int)
Function GetProcessImageFileNameA(hProcess:Int, lpImageFileName:Byte Ptr, nSize:Int)
EndExtern

' enumerate all processes '
Function EnumerateProcesses:Int[]()
Local pProcessIds:Byte Ptr
Local cb:Int = 1024
Local bBytesReturned:Int
Local processes:Int[]

pProcessIds = MemAlloc(cb)
	If Not EnumProcesses(pProcessIds , cb, Varptr bBytesReturned)
		MemFree(pProcessIds )
		Return Null
	EndIf
processes = New Int[bBytesReturned / 4]
MemCopy(Varptr processes[0], pProcessIds, bBytesReturned)
MemFree(pProcessIds)

Return processes
EndFunction

' get process name from process id '
Function GetProcessName:String(id:Int)
Local hProcess:Int
Local hModule:Int = 0
Local lpBaseName:Byte Ptr[1024]
Local nSize:Int = 1024
Local processName:String = ""

hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, id)
	If hProcess = 0 Return ""

GetModuleBaseName(hProcess, hModule, Varptr lpBaseName[0], nSize)

processName = String.FromCString(lpBaseName)

	If processName = "?" Or processName = Null
		GetProcessImageFileNameA(hProcess, Varptr lpBaseName[0], nSize)
		processName = StripDir(String.FromCString(lpBaseName))
	EndIf

CloseHandle(hProcess)

Return processName
EndFunction

' EXAMPLE USEAGE '

Local pList:Int[] = EnumerateProcesses()

  If pList.length > 0

	 For Local i:Int = 0 To pList.length - 1

		   If pList[i] <> 0

			  Local pname:String = GetProcessName(pList[i])

				 If pname <> "" Notify(pname)

		   EndIf

	 Next

  EndIf

Share this post


Link to post

xwinuser.c

#include "xwinuser.h"

HWND _GetNextWindow(HWND hWnd, UINT wCmd)
{
  return GetNextWindow(hWnd, wCmd);
}

 

xwinuser.h

#include <Windows.h>
#include <Winuser.h>

#ifdef __cplusplus
extern "C"{
#endif

extern HWND _GetNextWindow(HWND hWnd, UINT wCmd);

#ifdef __cplusplus
}
#endif

 

BMAX

Import "xwinuser.c"

Const GW_HWNDNEXT:int = 2
Const GW_HWNDPREV:int = 3

Extern "Win32"
   Function _GetNextWindow:Int(hWnd:Int, wCmd:Int) = "_GetNextWindow"
EndExtern

 

Not sure if this would be called "proper" but it works.

Share this post


Link to post

Cool, so you can make your own task manager?

 

There's a predefined BlitzMax tag you can specify for your posts, FYI.

Share this post


Link to post

lol, technically yeah, I could make my own task manager.

 

However, my purpose is to deny multiple instances of my application, and eventually send messages (SendMessageW) to the application - which I'm working on now. My attempt is to deny a second application execution and send the application arguments to the application already running (this way double clicking a file that my application is registered to will open up in the application already running).

 

Forgot about the tags.

 

And fixed.

Share this post


Link to post

I'll be interested in this if you get a working example of the messages thing.

Share this post


Link to post

Had this working but never tested actually getting the message, I only confirmed it worked with a notification, and so I spent all day trying to actually get the message. Going to give this more of a shot but if I still can't get it working strictly in BMax I'm probably going to take a shortcut and provide a helper function in C.

Share this post


Link to post

I just now got this working and I haven't made it all organized yet, I'm going to do that when I add it to my actual application, instead of using this testing application. One portion turned out to be a problem. When trying to get the window handle from the process ID it actually is a list of the window and basically any/all gadgets that can receive messages (active panels for example). I don't know for fact that this is true but that's what it seems like to me, otherwise I don't see how all of these returned results from "GetWindowThreadProcessId" are all from the same process ID.

 

Here is the function get the results from a process ID, one of which will be the window, and this has to be filtered in some way (send a message, check the title, something along those lines; I haven't done this filter yet):

 

' get the list of threads/windows matching the passed in process id '
Function GetProcessWindow:int[](id:int)
 Local hWnd:int = GetTopWindow(0)
 Local hWnds:int[]

While(hWnd)
  Local pId:int
  Local dThreadId:int = GetWindowThreadProcessId(hWnd, Varptr pId)

	If pId = id
	  hWnds = hWnds[..hWnds.Length+1]
	  hWnds[hWnds.Length-1] = hWnd
	EndIf

  hWnd = _GetNextWindow(hWnd, GW_HWNDNEXT)
Wend

 Return hWnds
EndFunction

 

Now, you can use this list and send the message to all of them, which I did to be quick about it, and it seems safe because the message will just fail to send if it's not a correct window. That seems odd, so a filtering process should be done, but I haven't done that yet. Either way you will still be sending a message of some kind to all of the returned results before having the actual window handle.

 

Anyway, the following is how to send the message (which would be a separate application at the time) and receive it (which would be the already running instance of the application).

 

Sending:

Global ChangeWindowMessageFilter:Int(message:Int, dwFlag:Int) = GetProcAddress(LoadLibraryA("user32.dll"), "ChangeWindowMessageFilter")

Local MsgStr:String = "THIS IS A TEST"
Local MsgData:int[3]
MsgData[0] = 1
MsgData[1] = Len(MsgStr) + 1
MsgData[2] = Int(MsgStr.ToCString())

ChangeWindowMessageFilter(WM_COPYDATA, 1)
SendMessageW(pHandle, WM_COPYDATA, 0, Int Byte Ptr MsgData)

 

Receiving:

Local MsgData:int[3]
MemCopy(MsgData, Byte Ptr lp, 12)

' MsgData[0] - 1 '
' MsgData[1] - Size '
' MsgData[2] - String '

Notify(String.FromCString(Byte Ptr MsgData[2]))

 

And there you have it. It will notify "THIS IS A TEST" correctly. You can use "MsgData[0]" as a form of message identification so you can decide what to do with the string you've received.

 

 

 

For receiving, it's best to edit "win32maxguiex.bmx" and explains the "lp" variable. Line 372, you would do something like:

' Select msg '
 Case WM_COPYDATA
local msgData:int[3]
MemCopy(msgData, Byte Ptr lp, 12)

PostGuiEvent(EVENT_DATARECEIVED, Null, 0, 0, 0, 0, String.FromCString(Byte Ptr msgData[2]))

 

Add the "EVENT_DATARECEIVED" to "brl.mod/event.bmx", around line 193:

Const EVENT_DATARECEIVED=$50000

 

And around line 234:

TEvent.RegisterId EVENT_DATARECEIVED,"DataReceived"

 

Rebuild the two modules, and now you can use it like:

repeat
 waitevent()

 select eventid()

case EVENT_DATARECEIVED
  notify(eventextra().tostring())

 

That should be it.

 

 

Thanks goes out to "col" from the BlitzMax forums for his help in figuring out the sending/receiving of the message.

Share this post


Link to post

C++ version to send a message to BMax window (yes, it's specific, any other way doesn't work correctly that I've noticed):

 

HWND pHandle = FindWindow(NULL, _T("WINDOW TITLE"));

if(pHandle != NULL && GetLastError() != ERROR_SUCCESS)
{
 std::string msgString = "THIS IS A TEST";

 COPYDATASTRUCT dataStruct;
 dataStruct.dwData = 3;  // this just tells my app that this is coming from C++ DLL - use any number
 dataStruct.cpData = (msgString.length() + 1);
 dataStruct.lpData = (PVOID)msgString.c_str();

 LRESULT msgSent = SendMessage(pHandle, WM_COPYDATA, (WPARAM)pHandle, (LPARAM)(LPVOID)&dataStruct);
}

 

I still cannot figure out why BMax cannot read from the named pipe (different subject, just using this spot for quick thoughts). Using pure C++ I have no problems, BMax can only send, but not receive. I'm guessing it's something wrong I'm doing in BMax but it's hard to tell.

Share this post


Link to post

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...