Paul Thomas Posted February 16, 2012 Share Posted February 16, 2012 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 Quote Link to comment Share on other sites More sharing options...
Paul Thomas Posted February 16, 2012 Author Share Posted February 16, 2012 Wow, "GetTopWindow" is in User32.dll but "GetNextWindow" is only available in Winuser.h? That is so lame. Quote Link to comment Share on other sites More sharing options...
Paul Thomas Posted February 16, 2012 Author Share Posted February 16, 2012 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. Quote Link to comment Share on other sites More sharing options...
Josh Posted February 16, 2012 Share Posted February 16, 2012 Cool, so you can make your own task manager? There's a predefined BlitzMax tag you can specify for your posts, FYI. Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Paul Thomas Posted February 16, 2012 Author Share Posted February 16, 2012 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. Quote Link to comment Share on other sites More sharing options...
Josh Posted February 16, 2012 Share Posted February 16, 2012 I'll be interested in this if you get a working example of the messages thing. Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Paul Thomas Posted February 17, 2012 Author Share Posted February 17, 2012 Got this working but now I need to clean everything up. Quote Link to comment Share on other sites More sharing options...
Paul Thomas Posted February 18, 2012 Author Share Posted February 18, 2012 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. Quote Link to comment Share on other sites More sharing options...
Paul Thomas Posted February 19, 2012 Author Share Posted February 19, 2012 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. Quote Link to comment Share on other sites More sharing options...
Paul Thomas Posted February 19, 2012 Author Share Posted February 19, 2012 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. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.