; Author: Carlo Somigliana (for PB4.40) ; email: somic@libero.it, site: http://semelinanno.com ; Date: 6. Dicember 2010 ; OS: Windows ; ----------------------------------------------------------------------------- ; Public domain ; ----------------------------------------------------------------------------- ; Inter Process SendMessage ; ----------------------------------------------------------------------------- ; InterProcessMssg ;-------------------------- ; This code has been inspired by an article on CodeProject site (Reference: CProcessData ; A template class To ease up SendMessage calls across processes: By Nishant Sivakumar ; link: http://www.codeproject.com/KB/winsdk/CProcessData.aspx) ; I started trying to translate the C++ Class in PB for the PB comunity to solve an old problem (for me) ; of the Send/PostMessage functions not working across different processes (not always, only when the ; result is not in the returned parameter but is a buffer/structure). ; ; In fact, in the usual way the destination buffer can not be mapped in the other process address ; space and the message function fails. With this functions (IPM_...) you should be able to overcome this ; limit (if by design, it is not very well documented) and access with the functions VirtualAllocEx_ ; And Read/WriteProcessMemory_ the required data ; ; Soon the exercise expanded (or exploded) to become more general and requiring additional coding and ; testing of various solutions. ; ; This code has 2 sections: a DLL part and a Test (CompilerIf) part ; Run it should show the results of various examples, while compiled it should give a DLL with the ; IPM and some other usefull functions (without the test code). ; The DLL is not working (see 3rd WARINIG below). ; ; The _DllTest_ assemble the code written to test various solutions. Take it as is: it is badly written ; and poorly commented, but I hope understandable (with some efforts) ; It looks for a specific rathet window using different combinations of parameters for SenMessage; ; If Not found it runs the relevant program and then search again for the window ; ; IMPORTANT: 3 WARNINGS: ; 1. It was born as a DLL but the code has not been written to be a real DLL (not yet) ; 2. Example 7 (with WINWORD) is not working and I've decided to stop spending to much time with MS products ; 3. Procedure FindWindowBy there is a CompilerIf directive to avoid a *Memory causes a "Invalid Memory Access" ; after approx. 160 callbacks of EnumChildWindows_ ; I don't have the same problem with Asci Executable or using a string as buffer. ; I leave to the many programmers more expert than me the job to identify the bug in this code ; or in PB IDE). Just a small request: let me know if you discover something. ; ; VERY IMPORTANT: ; The entire code takes benefit from the free contribution of other PB experts (much more than me) ; and is then released FREE for the PB community. Do what you want with it (at your own risk...;-) ; If you want to drop me a message for feedback (any comments and bugs), feel free to do it ; ; Well, this has been my 2009 Christmas fun (???) and my modest gift to the forum. ; ; Happy 2010 To each PB programmer. ; ; Regards, ; Somic EnableExplicit #Assert_IPM_Proc_ON = 1 ; setting this activate auto assert for every IPM_ procedure Global gSizeOfData, m_hProcess, m_lpData, exe$ Global dwDesiredAccess = #PROCESS_ALL_ACCESS Global flAllocationType = #MEM_COMMIT Global flProtect = #PAGE_READWRITE Macro QUOTE " EndMacro Macro sDebug(sString) Debug (Str(#PB_Compiler_Line)+": " + sString) OutputDebugString_(Str(#PB_Compiler_Line)+": " + sString) EndMacro Macro Assert(Expression, EndOnError=1, Text="") Define st1.s, st2.s, st3.s, st4.s,GLE ; sDebug(" - Assert: "+QUOTE Expression QUOTE +" = " + Str(expression) If Expression <= 0 ; And Left(#PB_Compiler_Procedure, 4*#Assert_IPM_Proc_ON) = "IPM_" GLE = GetLastError_() st1 = "------>>>>>> Error at line "+ Str(#PB_Compiler_Line)+" <<<<<<--------" st2 = " "+GetFilePart(#PB_Compiler_Procedure)+"("+Str(#PB_Compiler_Line)+") - "+Text+QUOTE Expression QUOTE + Str(Expression ) st3 = " GLE = "+Str(GLE)+ ", "+GetLastErrorAsText(GLE) OutputDebugString_(st1) ; this is for Debug the Relase version OutputDebugString_(st2) OutputDebugString_(st3) Debug st1 ; this is for Debug the Debug version Debug st2 Debug st3 ; If Text <> "" ; OutputDebugString_(st4) ; Debug st4 ; EndIf If EndOnError = 1 IPM_TermProcessData(0, 0) End GLE EndIf EndIf EndMacro Procedure.s GetLastErrorAsText(LastError.l) Protected *ErrorBuffer, Message.s If LastError *ErrorBuffer = AllocateMemory(1024) FormatMessage_(#FORMAT_MESSAGE_FROM_SYSTEM, 0, LastError, 0, *ErrorBuffer, 1024, 0) message.s=PeekS(*ErrorBuffer) FreeMemory(*ErrorBuffer) EndIf ProcedureReturn message EndProcedure ProcedureDLL AttachProcess(Instance) EndProcedure ProcedureDLL AttachThread(Instance) EndProcedure ProcedureDLL DetachThread(Instance) EndProcedure DeclareDLL DetachProcess(Instance) ProcedureDLL IPM_TermProcessData(l_hProcess = 0, l_lpData = 0) Protected lret, SkiphProcess If l_hProcess = 0 l_hProcess = m_hProcess ElseIf l_hProcess = -1 SkiphProcess = 1 l_hProcess = m_hProcess EndIf If (l_lpData = 0) Or (l_hProcess > 0) ; if close hProcess must Free also lpData l_lpData = m_lpData EndIf If(l_hProcess) ; if l_hProcess = -1 execute VirtualFreeEx_ but not CloseHandle_ If(l_lpData) lret = VirtualFreeEx_(l_hProcess, l_lpData, #Null, #MEM_RELEASE); m_lpData = 0 Assert(lret, 0) EndIf If l_hProcess And Not SkiphProcess lret = CloseHandle_(l_hProcess); m_hProcess = 0 Assert(lret, 0) EndIf ProcedureReturn lret EndIf ProcedureReturn 0 EndProcedure ProcedureDLL DetachProcess(Instance) ProcedureReturn IPM_TermProcessData(0, 0) EndProcedure ProcedureDLL IPM_InitProcessData (dwProcessId, hOpenProcess, lSizeOfData) Protected lret gSizeOfData = lSizeOfData If dwProcessId = 0: dwProcessId = GetCurrentProcessId_(): EndIf If hOpenProcess m_hProcess = hOpenProcess Else m_hProcess = OpenProcess_(dwDesiredAccess, #False, dwProcessId) hOpenProcess = m_hProcess; EndIf ASSERT(m_hProcess); If(m_hProcess) m_lpData = VirtualAllocEx_(m_hProcess, #Null, lSizeOfData, flAllocationType, flProtect); ASSERT(m_lpData); EndIf ProcedureReturn m_lpData EndProcedure ProcedureDLL IPM_InitProcessDataEx (lPID, hOpenProcess, lSizeOfData, ldwDesiredAccess, lflAllocationType, lflProtect) dwDesiredAccess = ldwDesiredAccess flAllocationType = lflAllocationType flProtect = lflProtect m_lpData = IPM_InitProcessData (lPID, hOpenProcess, lSizeOfData) EndProcedure ProcedureDLL.l IPM_WriteData(*pSrcBuff, SizeOfBuff, lpNewDestAddress=0) Protected lret, bytesWritten, l_lpData If lpNewDestAddress = 0 ; if specified <> 0 then Write at this specific location l_lpData = m_lpData Else l_lpData = lpNewDestAddress EndIf If (m_hProcess And l_lpData) lret = WriteProcessMemory_(m_hProcess, l_lpData, *pSrcBuff, SizeOfBuff, @bytesWritten) Else lret = #False EndIf Assert(lret) ProcedureReturn bytesWritten EndProcedure ProcedureDLL.l IPM_ReadData(*pDestBuff, SizeOfBuff, lpNewSrcAddress=0) ; ProcedureDLL.l IPM_ReadData(*T, lpBaseAddress=0) Protected lret, BytesRead, l_lpData If lpNewSrcAddress = 0 ; if specified <> 0 then Read at this specific location l_lpData = m_lpData Else l_lpData = lpNewSrcAddress EndIf If (m_hProcess And l_lpData) lret = ReadProcessMemory_(m_hProcess, l_lpData, *pDestBuff, SizeOfBuff, @BytesRead) ; lret = ReadProcessMemory_(m_hProcess, m_lpData, *T, gSizeOfData, #Null) Else lret = #False EndIf Assert(lret) ProcedureReturn BytesRead EndProcedure ProcedureDLL.l IPM_GetDataAddr() Protected lret If (m_hProcess And m_lpData) lret = m_lpData Else lret = 0 EndIf Assert(lret) ProcedureReturn lret EndProcedure ProcedureDLL.l IPM_GethOpenProcess() ProcedureReturn m_hProcess EndProcedure Procedure.s GetWinTextEx(hWnd, Mode.b=1) Protected Text.s, lret, ln If Mode = 0 Text=Space(256) lret = GetWindowText_(hWnd, Text, 256) Else ; seems more reliable if window hung ln = SendMessage_(hWNd, #WM_GETTEXTLENGTH, 0, 0);, #SMTO_ABORTIFHUNG, 1000, 0) Text = Space(ln+1) lret = SendMessage_(hWNd, #WM_GETTEXT, ln+1, Text);, #SMTO_ABORTIFHUNG, 1000, 0) EndIf ProcedureReturn Left(Text, lret) EndProcedure Procedure.s GetClassName(hWnd) Protected Text.s, lret Text=Space(256) lret = GetClassName_(hWnd, Text, 256) ProcedureReturn Left(Text, lret) EndProcedure Procedure.s GetWinData(hWnd) Protected st.s st = GetClassName(hWnd)+"/"+GetWinTextEx(hWnd) ProcedureReturn st EndProcedure Structure WINDATA ; these are the parameter used to identify the Target window PID.l ; Process ID Process.s ; process name CurrPID.l ; Current PID to exclude SkipCurrApp.l ; Flag (if CurrPID = 0) TID.l ; Thread ID MainWindow.l ; Top Level Window Handle MainwClass.s ; Top Level Window Class MainwTitle.s ; Top Level Window Title GWL_STYLE.l cClass.s ; Control Class Name cText.s ; Control Text Module.s ; Module name CID.l ; Control ID RethWnd.l ; Return Window Handle (if any) ResetF.l ; Reset search EndStructure #FindNotDef = 1024; Not defined yet #FindNothing = 0; Disabled #FindwClass = 1 ; Class Name #FindwTitle = 2 ; Window Title #FindcClass = 4 ; Control Class #FindcText = 8 ; Control Text #FindPID = 16 ; Process ID #FindTID = 32 ; Thread ID #FindCID = 64 ; Control ID #FindProcess = 128 ; Process Name #FindModule = 256 ; Module Name ; #TH32CS_SNAPHEAPLIST = $1 ; ;#TH32CS_SNAPPROCESS = $2 ; #TH32CS_SNAPTHREAD = $4 ; #TH32CS_SNAPMODULE = $8 ; #TH32CS_SNAPALL = #TH32CS_SNAPHEAPLIST | #TH32CS_SNAPPROCESS | #TH32CS_SNAPTHREAD | #TH32CS_SNAPMODULE ; #TH32CS_INHERIT = $80000000 ; #INVALID_HANDLE_VALUE = -1 ; Global NewList Process32.PROCESSENTRY32 () Procedure SetFindF(*MainFindF, *pwData.WINDATA) Protected FindF = #FindNotDef, MainFindF, lExactMatchF If FindF = #FindNotDef FindF = #FindNothing ; only first time MainFindF = #FindNothing ; only first time If *pwData\PID > 0 FindF = FindF + #FindPID EndIf If *pwData\TID > 0 FindF = FindF + #FindTID EndIf If Not (FindF & #FindPID) And Not (FindF & #FindTID) If *pwData\MainwClass <> "" ; use flag for speed FindF = FindF + #FindwClass EndIf If *pwData\MainwTitle <> "" FindF = FindF + #FindwTitle EndIf EndIf If *pwData\Process <> "" FindF = FindF + #FindProcess EndIf MainFindF = FindF If *pwData\cClass <> "" FindF = FindF + #FindcClass EndIf If *pwData\cText <> "" FindF = FindF + #FindcText EndIf If *pwData\CID <> 0 ; control ID can be > 0 or < 0 FindF = FindF + #FindCID EndIf If *pwData\Module <> "" FindF = FindF + #FindModule EndIf EndIf If *pwData\SkipCurrApp And *pwData\CurrPID = 0 *pwData\CurrPID = GetCurrentProcessId_() EndIf PokeL(*MainFindF, MainFindF) ProcedureReturn FindF EndProcedure ; Procedure derived from PureArchive, by 2003-2007 by Andre Beer / PureBasic-Team ; Author: Hi-Toro (updated For PB4.00 by blbltheworm) Procedure lListProcesses(List Process32.PROCESSENTRY32(), pmName$="") #MAX_PATH = 260 #PROCESS32LIB = 9999 Protected Proc32.PROCESSENTRY32, snap pmName$ = LCase(pmname$) ClearList (Process32 ()) ; Add processes to Process32 () list... snap = CreateToolhelp32Snapshot_(#TH32CS_SNAPPROCESS, 0) ; #TH32CS_SNAPALL Assert(snap, 0) If snap Proc32\dwSize = SizeOf (PROCESSENTRY32) If Process32First_(snap, @Proc32) AddElement (Process32 ()) CopyMemory (@Proc32, @Process32 (), SizeOf (PROCESSENTRY32)) While Process32Next_(snap, @Proc32) ; If pmName$ = "" Or (FindString(LCase(PeekS(@Process32 ()\szExeFile)), pmName$, 1)) AddElement (Process32 ()) CopyMemory (@Proc32, @Process32 (), SizeOf (PROCESSENTRY32)) ; sDebug (PeekS(@Process32 ()\szExeFile)+", PID:"+Hex(Process32 ()\th32ProcessID)) ; EndIf Wend EndIf CloseHandle_(snap) EndIf ProcedureReturn ListSize(Process32()) EndProcedure ProcedureDLL ListProcesses(List Process32.PROCESSENTRY32(), pmName$="") #MAX_PATH = 260 #PROCESS32LIB = 9999 Protected Proc32.PROCESSENTRY32, snap pmName$ = LCase(pmname$) ClearList (Process32 ()) ; Add processes to Process32 () list... snap = CreateToolhelp32Snapshot_(#TH32CS_SNAPPROCESS, 0) ; #TH32CS_SNAPALL Assert(snap, 0) If snap Proc32\dwSize = SizeOf (PROCESSENTRY32) If Process32First_(snap, @Proc32) AddElement (Process32 ()) CopyMemory (@Proc32, @Process32 (), SizeOf (PROCESSENTRY32)) While Process32Next_(snap, @Proc32) ; If pmName$ = "" Or (FindString(LCase(PeekS(@Process32 ()\szExeFile)), pmName$, 1)) AddElement (Process32 ()) CopyMemory (@Proc32, @Process32 (), SizeOf (PROCESSENTRY32)) ; sDebug (PeekS(@Process32 ()\szExeFile)+", PID:"+Hex(Process32 ()\th32ProcessID)) ; EndIf Wend EndIf CloseHandle_(snap) EndIf ProcedureReturn ListSize(Process32()) EndProcedure Procedure lFindWindowProcessName (window, List Process32.PROCESSENTRY32()) Protected TID, PID ResetList (Process32 ()) TID = GetWindowThreadProcessId_ (window, @pid) While NextElement (Process32 ()) If pid = Process32 ()\th32ProcessID exe$ = GetFilePart (PeekS (@Process32 ()\szExeFile)) LastElement (Process32 ()) EndIf Wend ProcedureReturn @exe$ EndProcedure ProcedureDLL FindWindowProcessName (window, List Process32.PROCESSENTRY32()) Protected TID, PID ; PokeL(Process32 = *pProcess32 ResetList (Process32 ()) TID = GetWindowThreadProcessId_ (window, @pid) While NextElement (Process32 ()) If pid = Process32 ()\th32ProcessID exe$ = GetFilePart (PeekS (@Process32 ()\szExeFile)) LastElement (Process32 ()) EndIf Wend ProcedureReturn @exe$ EndProcedure ; Procedure CompareStrings(St1$, St2$) Protected pos st2$ = LCase(st2$) st1$ = LCase(st1$) If Right(st2$, 2) = #CRLF$ ; exact match st2$ = #CRLF$ + st2$ Else st2$ = Left(st2$, Len(st2$)-2) EndIf pos = FindString(#CRLF$+st1$+#CRLF$, st2$, 1) ProcedureReturn pos EndProcedure ProcedureDLL FindWindowBy (window, *pwData.WINDATA) Protected FoundF, *Memory, Title$, Class$, MainWindowF Protected TID, PID, CID, hProcess.q, ProcessN$, hWnd, lret, sBuff.s Static MainFindF, FindF = #FindNotDef, prevPID, MainFoundF, wLong, MainWindow Static prevhProcess , NewList Proc32.PROCESSENTRY32(), lExactMatchF ; If window = $0013055A ; CallDebugger ; EndIf If (*pwData <> 0 And FindF = #FindNotDef) Or *pwData\ResetF = 1 FindF = #FindNotDef FindF = SetFindF(@MainFindF, *pwData) ; , @ExactMatchF) If FindF & #FindProcess ; only if necessary (for performance reasons) lListProcesses(@Proc32()) EndIf *pwData\ResetF = 0 EndIf FoundF = 0 If GetParent_(window) = 0 MainWindowF = #True MainWindow = window ; Else ; MainWindowF = #False EndIf CompilerIf #PB_Compiler_Unicode sBuff = Space(255) GetClassName_ (window, @sBuff, 255) Class$ = PeekS (@sBuff) sBuff = Space(255) GetWindowText_(window, @sBuff, 255) Title$ = PeekS (@sBuff) CompilerElse ; this code gives null memory after some Enum calls ; don't know why... it is left if someone wants to investigate... *Memory = AllocateMemory (255) GetClassName_ (window, *Memory, 255) Class$ = PeekS (*Memory) FreeMemory (*Memory) *Memory = AllocateMemory (*Memory, 255) GetWindowText_ (window, *Memory, 255) Title$ = PeekS (*Memory) FreeMemory (*Memory) Debug *Memory CompilerEndIf TID = GetWindowThreadProcessId_(window, @PID) CID = GetDlgCtrlID_(window) wLong = GetWindowLong_(window, #GWL_STYLE) If FindF & #FindProcess And prevPID <> PID ProcessN$ = PeekS(lFindWindowProcessName(window, Proc32())) ProcessN$ = LCase(ProcessN$) ; sDebug("Process: " + ProcessN$) EndIf ; sDebug("hWnd="+Hex(window)+", PID=" + Hex(PID)+", TID="+Hex(TID)+", CID="+Hex(CID)) ; sDebug(" Process: " + ProcessN$+ ", Title: "+title$ + " / Class: " + class$ ) If PID = *pwData\CurrPID ; skip launching App ProcedureReturn #True EndIf If PID <> prevPID MainFoundF = 0 prevPID = PID EndIf If FindF & #FindPID If PID = *pwData\PID FoundF = FoundF + #FindPID MainFoundF = MainFoundF | #FindPID EndIf EndIf If FindF & #FindTID If TID = *pwData\TID FoundF = FoundF + #FindTID EndIf EndIf If FindF & #FindProcess If LCase(ProcessN$) =LCase(*pwData\Process) FoundF = FoundF + #FindProcess MainFoundF = MainFoundF | #FindProcess EndIf EndIf If FindF & #FindwClass If LCase(class$) = LCase(*pwData\MainwClass) FoundF = FoundF + #FindwClass MainFoundF = MainFoundF | #FindwClass EndIf EndIf If FindF & #FindwTitle If CompareStrings(title$, *pwData\MainwTitle) FoundF = FoundF + #FindwTitle MainFoundF = MainFoundF | #FindwTitle EndIf EndIf If MainFoundF = MainFindF FoundF = MainFoundF If FindF & #FindCID If CID = *pwData\CID FoundF = FoundF + #FindCID EndIf EndIf If FindF & #FindcClass If LCase(class$) = LCase(*pwData\cClass) FoundF = FoundF + #FindcClass EndIf EndIf If FindF & #FindcText If CompareStrings(title$, *pwData\cText) FoundF = FoundF + #FindcText EndIf EndIf EndIf ; when window found, return everything If FoundF = FindF If (*pwData\GWL_STYLE = 0) Or (*pwData\GWL_STYLE <> 0 And wlong & *pwData\GWL_STYLE) With *pwData \MainWindow = MainWindow \RetHWnd = window \PID = PID \TID = TID \CID = CID \Process = ProcessN$ \GWL_STYLE = wLong If FindF & #FindwClass Or FindF & #FindcClass ; one one of the two can be returned If MainWindowF \MainwClass = Class$ Else \cClass = Class$ EndIf EndIf If FindF & #FindwTitle Or FindF & #FindcText If MainWindowF \MainwTitle = Title$ Else \cText = Title$ EndIf EndIf EndWith EndIf ProcedureReturn #False Else ProcedureReturn #True EndIf EndProcedure ProcedureDLL GetOrRunProgr(*pTarget.WINDATA, Process.s) Protected sinfo.STARTUPINFO Protected pinfo.PROCESS_INFORMATION Protected lret, PID, TID, hOpenProcess, hThread *pTarget\ResetF = 1 EnumChildWindows_ (GetDesktopWindow_(), @FindWindowBy(), *pTarget) If *pTarget\RetHWnd = 0 If (*pTarget\Process <> "" Or Process <> "") If Process = "" Process = *pTarget\Process EndIf ; sDebug(process.s sinfo\cb = SizeOf(STARTUPINFO) Protected pclass, sec1.SECURITY_ATTRIBUTES, sec2.SECURITY_ATTRIBUTES ;Not used, but needed ;Set the structure size sec1\nLength = SizeOf(sec1) sec2\nLength = SizeOf(sec2) ;Set the flags sinfo\dwFlags = #STARTF_USESHOWWINDOW ;Set the window's startup position sinfo\wShowWindow = #SW_NORMAL ;Set the priority class pclass = #NORMAL_PRIORITY_CLASS ; #HIGH_PRIORITY_CLASS lret = CreateProcess_(0, process, @sec1, @sec2, 0, #SYNCHRONIZE + pclass , 0, 0, @sinfo.STARTUPINFO, @pinfo.PROCESS_INFORMATION) ; lret = CreateProcess_(0, process, 0, 0, 0, #SYNCHRONIZE , 0, 0, @sinfo.STARTUPINFO, @pinfo.PROCESS_INFORMATION) ; The handle returned by the CreateThread function has THREAD_ALL_ACCESS access to the thread object. Assert(lret, 1) PID = pinfo\dwProcessId TID = pinfo\dwThreadId hOpenProcess = pinfo\hProcess hThread = pinfo\hThread ; this returns immediately for explorer.exe WaitForInputIdle_(hOpenProcess, 5000) ; < -------------- FONDAMENTAL !!!! DebugLevel 15 *pTarget\PID = PID ; if <> 0 overwrites MainwTitle & MainwClass *pTarget\ResetF = 1 If process = "explorer.exe" ; can't use PID (doesn't work), must use ProcessName *pTarget\Process = GetFilePart(process) *pTarget\PID = 0 lret = CloseHandle_(hOpenProcess) lret = CloseHandle_(hThread) hOpenProcess = 0 hThread = 0 EndIf Delay(1000) ; necessary for explorer only EnumChildWindows_ (GetDesktopWindow_(), @FindWindowBy(), *PTarget) ProcedureReturn lret ; Else ;TID = GetWindowThreadProcessId_(thnd, @PID) Else sDebug("Process not defined. Unhable to launch program") ProcedureReturn 0 EndIf EndIf EndProcedure ; + DLL TEST CODE CompilerIf #PB_Compiler_Debugger Procedure PrintTestHeader(Process.s, *Target.WINDATA) ; sDebug("") With *Target sDebug("Process: " + UCase(GetFilePart(Process))) sDebug("ProcessID: PID: $" + Hex(\PID)+", ("+Str(\PID)+"), TID: $" + Hex(\TID)+", ("+Str(\TID)+")") sDebug("GetDlgItem: CtrlID: "+Hex(\CID)) sDebug("TARGET WIN: thnd: " + Hex(\RetHWnd)+", ("+Str(\RethWnd)+"), "+GetWindata(\RetHWnd)) Assert(\RetHWnd) EndWith EndProcedure Procedure _DllTest_() Protected thnd, chnd Protected lpTabItemAddr, lpTextAddr, *p, pid, tid, hOpenProcess, ln, frmt Protected hWnd, lret, process.s, index, sBuff.s, CtrlID, Msg1, Msg2, sClass.s, sText.s Protected pBUTTON.TBBUTTON, pTabItem.TC_ITEM, sz, TestNr, wClass.s, wText.s, bytesRead Protected Target.WINDATA DebugLevel 5 For TestNr = 0 To 8 FillMemory(@Target, SizeOf(Target), 0) Target\ResetF = 1 ; reset the target (force routine to rebuild lists) sDebug("-------- Test Nr: "+Str(testnr)+" -----------------------------------") Select TestNr ;+ Case 0 process.s = "explorer.exe" With Target \MainwClass = "ExploreWClass" ; exact match \CID = $A000 ; control id of the Tools bar EndWith *p = pBUTTON Sz = SizeOf(pBUTTON) Msg1 = #TB_GETBUTTON Msg2 = #TB_GETBUTTONTEXT ; --------------------------------------------------------------------- pTabItem\mask = #TCIF_TEXT ; set parameters pTabItem\cchTextMax = #MAX_PATH sBuff = Space(#MAX_PATH) pTabItem\pszText = @sBuff Sz = SizeOf(pTabItem) ; ////////////////////////////////////////////////////////////////////// ; hwnd = GetOrRunProgr(@Target, process.s) thnd = Target\RetHWnd PID = Target\PID PrintTestHeader(Process.s, @Target) ; ; /////////////////////////////////////////////////////////////////////// lret = IPM_InitProcessData(PID, hOpenProcess, sz) sDebug("IPM_InitProcessData: lret: " + Str(lret)+", m_hProcess: " + Str(m_hProcess)) ASSERT(lret) index = 0 SetLastError_(0) lret = SendMessage_(thnd, Msg1, index, IPM_GetDataAddr()); Zero based list ASSERT(lret+1, 0) ; +1 to avoid error message (0 is allowed) sDebug("SendMessage_: lret: "+Str(lret)) bytesRead = IPM_ReadData(*p, sz, 0) ; lret = IPM_ReadData(*p, 0) sDebug(">>> pBUTTON\idCommand: "+Str(pBUTTON\idCommand)) sDebug(">>> pBUTTON\fsState: "+Str(pBUTTON\fsState)) sDebug(">>> pBUTTON\dwData: "+Str(pBUTTON\dwData)) index = 0 ; read text/data of 1st button in program control (Back Arrow for explorer) lret = SendMessage_(thnd, Msg2, index, IPM_GetDataAddr()); sDebug("SendMessage_: lret: "+Str(lret)) sBuff = Space(lret+1) lret = IPM_ReadData(@sBuff, lret, 0) sDebug("IPM_ReadData: lret: "+Str(lret)) sDebug(">>> sBuff: "+sBuff) ;- Case 1 ; #TB_GETBUTTON message to Purebasic ;+ process.s = Chr(34)+"c:\program files\purebasic_440\purebasic.exe"+Chr(34) With Target \MainwClass = "WindowClass_2" ; ALWAYS exact match \MainwTitle = "PureBasic 4.40 (x86)"+#CRLF$ ; #CRLF$ means exact match \CID = $FFFFD8F0 ; control id of the Tools bar \ResetF = 1 EndWith *p = pBUTTON Sz = SizeOf(pBUTTON) Msg1 = #TB_GETBUTTON Msg2 = #TB_BUTTONCOUNT ; gets Tools Button Text or Item pTabItem\mask = #TCIF_TEXT ; set parameters pTabItem\cchTextMax = #MAX_PATH sBuff = Space(#MAX_PATH) pTabItem\pszText = @sBuff Sz = SizeOf(pTabItem) lret = IPM_InitProcessData(PID, hOpenProcess, sz) sDebug("IPM_InitProcessData: lret: " + Str(lret)+", m_hProcess: " + Str(m_hProcess)) ASSERT(lret) index = 0 SetLastError_(0) lret = SendMessage_(thnd, Msg1, index, IPM_GetDataAddr()); Zero based list ASSERT(lret+1, 0) ; +1 to avoid error message (0 is allowed) sDebug("SendMessage_: lret: "+Str(lret)) bytesRead = IPM_ReadData(*p, sz, 0) ; lret = IPM_ReadData(*p, 0) sDebug(">>> pBUTTON\idCommand: "+Str(pBUTTON\idCommand)) sDebug(">>> pBUTTON\fsState: "+Str(pBUTTON\fsState)) sDebug(">>> pBUTTON\dwData: "+Str(pBUTTON\dwData)) ; ////////////////////////////////////////////////////////////////////// ; hwnd = GetOrRunProgr(@Target, process.s) thnd = Target\RetHWnd PID = Target\PID PrintTestHeader(Process.s, @Target) ; ; /////////////////////////////////////////////////////////////////////// ; get button count lret = SendMessage_(thnd, Msg2, 0, 0); ; works also in the noromal way, because sDebug("SendMessage_: lret: "+Str(lret)) ; value is returned by the function itself sDebug(">>> Toolbar Button Count: " +Str(lret)) ; not thru a Structure ;- Case 2 ; #TCM_GETITEM message to Purebasic ;+ process.s = Chr(34)+"c:\program files\purebasic_440\purebasic.exe"+Chr(34) process.s = process.s + " "+Chr(34) + "InterProcMssg-ok.pb"+Chr(34) With Target \MainwClass = "WindowClass_2"; PureBasic Editor \MainwTitle = "PureBasic 4.40 (x86) - InterProcMssg-ok.pb" ; specific Tab selected ; replace InterProcMssg-ok.pb with the one ; you prefer \CID = $1: ; Control ID of the Tab bar \SkipCurrApp = 1 GetWindowThreadProcessId_(FindWindow_("WindowClass_2", 0), @\CurrPID) \GWL_STYLE = #WS_VISIBLE | #WS_CHILD ; Tab must be visible EndWith Msg1 = #TCM_GETCURSEL Msg2 = #TCM_GETITEM *P = pTabItem Sz = SizeOf(pTabItem) ; ////////////////////////////////////////////////////////////////////// ; hwnd = GetOrRunProgr(@Target, process.s) thnd = Target\RetHWnd PID = Target\PID PrintTestHeader(Process.s, @Target) ; ; /////////////////////////////////////////////////////////////////////// ; gets Tools Button Text or Item pTabItem\mask = #TCIF_TEXT ; set parameters pTabItem\cchTextMax = #MAX_PATH sBuff = Space(#MAX_PATH) pTabItem\pszText = @sBuff Sz = SizeOf(pTabItem) lret = IPM_InitProcessData(PID, hOpenProcess, sz) sDebug("IPM_InitProcessData: lret: " + Str(lret)+", m_hProcess: " + Str(m_hProcess)) ASSERT(lret) index = 0 SetLastError_(0) lret = SendMessage_(thnd, Msg1, index, IPM_GetDataAddr()); Zero based list ASSERT(lret+1, 0) ; +1 to avoid error message (0 is allowed) sDebug("SendMessage_: lret: "+Str(lret)) bytesRead = IPM_ReadData(*p, sz, 0) ; lret = IPM_ReadData(*p, 0) sDebug(">>> Selected Tab: "+Str(lret)) ; get Tabs' Text (all those available) sz = SizeOf(TC_ITEM) ; a TC_ITEM strucnture has to be defined in the OTHER ; process address space lret = IPM_InitProcessData(PID, hOpenProcess, sz) ; allocate memory for pTabItem structure lpTabItemAddr = IPM_GetDataAddr() ; in the other process address space lret = IPM_InitProcessData(PID, hOpenProcess, #MAX_PATH) ; allocate memory for Item Name lpTextAddr = IPM_GetDataAddr() ; in the other process address space pTabItem\mask = #TCIF_TEXT ; set parameters pTabItem\cchTextMax = #MAX_PATH pTabItem\pszText = lpTextAddr ; ==>> assign to address of the OTHER ; process lret = IPM_WriteData(pTabItem, sz, lpTabItemAddr) ; write Message parameter in the other ; process address space sDebug("IPM_WriteData: bytesWritten: " + Str(lret)) Assert(lret, 1) index = 0 Repeat ; //////////////////////////////////////////////////////// ; read text of 1st button (zero based) in program control (Back Arrow for explorer) lret = SendMessage_(thnd, Msg2, index, lpTabItemAddr); ;#TCM_GETITEM sDebug("index: "+Str(index)) sDebug("SendMessage_: lret: "+Str(lret)) Assert(lret, 0, "LOOP ENDED") ; ///////////////////////////////////////////////////////// sBuff = Space(#MAX_PATH) bytesRead = IPM_ReadData(@sBuff, bytesRead, lpTextAddr) sDebug(">>> sBuff: "+PeekS(@sBuff, bytesRead, #PB_Ascii)+"'") ; address space (but should be pbFileName) index + 1 ; increment tab index Until lret = 0 ; /////////////////////////////////////// ;- Case 3 ; #TCM_GETITEM message to Notepad++ ;+ process.s = Chr(34)+ "c:\test\npp\unicode\notepad++.exe" + Chr(34) With Target \MainwClass = "Notepad++" \cClass = "SysTabControl32" \cText = "Tab"+#CRLF$ ; exact match (notepad++ has other windows with Tab... titles) EndWith *P = pTabItem Sz = SizeOf(pTabItem) Msg1 = #TCM_GETCURSEL Msg2 = #TCM_GETITEM ; ////////////////////////////////////////////////////////////////////// ; hwnd = GetOrRunProgr(@Target, process.s) thnd = Target\RetHWnd PID = Target\PID PrintTestHeader(Process.s, @Target) ; ; /////////////////////////////////////////////////////////////////////// lret = IPM_InitProcessData(PID, hOpenProcess, sz) sDebug("IPM_InitProcessData: lret: " + Str(lret)+", m_hProcess: " + Str(m_hProcess)) ASSERT(lret) index = 0 SetLastError_(0) lret = SendMessage_(thnd, Msg1, 0, 0); Zero based list ASSERT(lret+1, 0) ; +1 to avoid error message (0 is allowed) sDebug("SendMessage_: lret: "+Str(lret)) sDebug(">>> Item Selected: "+Str(lret)) sz = SizeOf(TC_ITEM) ; a TC_ITEM strucnture has to be defined in the OTHER ; process address space lret = IPM_InitProcessData(PID, hOpenProcess, sz) ; allocate memory for pTabItem structure lpTabItemAddr = IPM_GetDataAddr() ; in the other process address space lret = IPM_InitProcessData(PID, hOpenProcess, #MAX_PATH) ; allocate memory for Item Name lpTextAddr = IPM_GetDataAddr() ; in the other process address space pTabItem\mask = #TCIF_TEXT ; set parameters pTabItem\cchTextMax = #MAX_PATH pTabItem\pszText = lpTextAddr ; ==>> assign to address of the OTHER ; process lret = IPM_WriteData(pTabItem, sz, lpTabItemAddr) ; write Message parameter in the other ; process address space sDebug("IPM_WriteData: bytesWritten: " + Str(lret)) Assert(lret, 1) index = 0 Repeat ; //////////////////////////////////////////////////////// ; read text of 1st button (zero based) in program control (Back Arrow for explorer) lret = SendMessage_(thnd, Msg2, index, lpTabItemAddr); ;#TCM_GETITEM sDebug("index: "+Str(index)) sDebug("SendMessage_: lret: "+Str(lret)) Assert(lret, 0, "LOOP ENDED") ; ///////////////////////////////////////////////////////// sBuff = Space(#MAX_PATH) bytesRead = IPM_ReadData(@sBuff, bytesRead, lpTextAddr) sDebug(">>> sBuff: "+PeekS(@sBuff, bytesRead, #PB_Ascii)+"'") ; address space (but should be pbFileName) index + 1 ; increment tab index Until lret = 0 ;- Case 4 ; get code from Purebasic Editor ;+ process.s = Chr(34)+"c:\program files\purebasic_440\purebasic.exe"+Chr(34) process.s = process.s + " "+Chr(34) + "InterProcMssg-ok.pb"+Chr(34) With Target \MainwClass = "WindowClass_2"; PureBasic Editor \MainwTitle = "PureBasic 4.40 (x86) - InterProcMssg-ok.pb" ; specific Tab selected ; replace InterProcMssg-ok.pb with the one ; you prefer \cClass = "Scintilla" ; get text from Scintilla window \GWL_STYLE = #WS_VISIBLE | #WS_CHILD ; Tab must be visible EndWith Msg1 = #SCI_GETTEXTLENGTH Msg2 = #SCI_GETTEXT ; gets Tools Button Text or Item ; ////////////////////////////////////////////////////////////////////// ; hwnd = GetOrRunProgr(@Target, process.s) thnd = Target\RetHWnd PID = Target\PID PrintTestHeader(Process.s, @Target) ; ; /////////////////////////////////////////////////////////////////////// sz = SendMessage_(thnd, Msg1, 0, 0); sDebug("SendMessage_: sz: "+Str(sz)) sBuff = Space(sz) lret = IPM_InitProcessData(PID, hOpenProcess, sz) ; allocate memory for pTabItem structure Assert(lret) lpTextAddr = IPM_GetDataAddr() ; in the other process address space lret = SendMessage_(thnd, Msg2, sz, lpTextAddr); sDebug("SendMessage_: lret: "+Str(lret)) *p = AllocateMemory(sz*2) lret = IPM_ReadData(*p, sz, 0) ; sDebug("IPM_ReadData: lret: "+Str(lret)) ; ClearClipboard() sBuff = PeekS(*p, sz, #PB_Ascii) ; SetClipboardText(sBuff) sDebug(">>> LEN(sBuff): "+StrU(Len(sBuff))) FreeMemory(*p) MessageRequester("Result (first 1000 chars): ", Left(sBuff, 1000)) ; /////////////////////////////////////// ;- Case 5 ;+ process.s = Chr(34)+ "c:\test\npp\unicode\notepad++.exe" + Chr(34) With Target \MainwClass = "Notepad++"; \MainwTitle = "Notepad++" \cClass = "Scintilla" ; If TestNr = 4: \cid = 0: EndIf EndWith Msg1 = #SCI_GETTEXTLENGTH Msg2 = #SCI_GETTEXT ; ////////////////////////////////////////////////////////////////////// ; hwnd = GetOrRunProgr(@Target, process.s) thnd = Target\RetHWnd PID = Target\PID PrintTestHeader(Process.s, @Target) ; ; /////////////////////////////////////////////////////////////////////// sz = SendMessage_(thnd, Msg1, 0, 0); sDebug("SendMessage_: sz: "+Str(sz)) sBuff = Space(sz) lret = IPM_InitProcessData(PID, hOpenProcess, sz) ; allocate memory for pTabItem structure Assert(lret) lpTextAddr = IPM_GetDataAddr() ; in the other process address space lret = SendMessage_(thnd, Msg2, sz, lpTextAddr); sDebug("SendMessage_: lret: "+Str(lret)) *p = AllocateMemory(sz*2) lret = IPM_ReadData(*p, sz, 0) sDebug("IPM_ReadData: lret: "+Str(lret)) ; ClearClipboard() sBuff = PeekS(*p, sz, #PB_Ascii) ; SetClipboardText(sBuff) sDebug(">>> LEN(sBuff): "+StrU(Len(sBuff))) FreeMemory(*p) MessageRequester("Result (first 1000 chars): ", Left(sBuff, 1000)) ; /////////////////////////////////////// ;- Case 6 ; ; send Text to MS Notepad ;+ ; process.s = Chr(34)+ "notepad.exe c:\windows\win.ini" + Chr(34) process.s = "notepad.exe" With Target \MainwClass = "Notepad"; PureBasic Editor \cClass = "Edit" EndWith ; Msg1 = #WM_GETTEXT Msg2 = #WM_SETTEXT ; ////////////////////////////////////////////////////////////////////// ; hwnd = GetOrRunProgr(@Target, process.s) thnd = Target\RetHWnd PID = Target\PID PrintTestHeader(Process.s, @Target) ; ; /////////////////////////////////////////////////////////////////////// ; with Notepad the std Sendmessage works (good to know) sBuff = "STD HELLO WORLD!" sz = MemoryStringLength(@sBuff) SetLastError_(0) lret = SendMessage_(thnd, Msg2, 0, @sBuff); Assert(lret, 0) ; ... while the CPD... one apparently doesn't (...) sBuff = "CPD HELLO WORLD!" sz = MemoryStringLength(@sBuff) lret = IPM_InitProcessData(PID, hOpenProcess, sz) ; allocate memory for Text Assert(lret) lpTextAddr = IPM_GetDataAddr() ; in the other process address space lret = IPM_WriteData(@sBuff, sz, 0) ; write text in the other process address space sDebug("IPM_WriteData: lret: "+Str(lret)) SetLastError_(0) lret = SendMessage_(thnd, Msg2, sz, lpTextAddr); Assert(lret, 0) ShowWindow_(Target\MainWindow, #SW_RESTORE) SetForegroundWindow_(Target\MainWindow) Delay(1000) ShowWindow_(Target\MainWindow, #SW_MINIMIZE) ; /////////////////////////////////////// ;- Case 7 ; ; send Text to PureBasic Editor ;+ process.s = Chr(34)+"c:\program files\purebasic_440\purebasic.exe"+Chr(34) With Target \MainwClass = "WindowClass_2"; PureBasic Editor \MainwTitle = "PureBasic 4.40 (x86)" ; specific Tab selected ; replace InterProcMssg-ok.pb with the one ; you prefer \cClass = "Scintilla" ; get text from Scintilla window \SkipCurrApp = 1 GetWindowThreadProcessId_(FindWindow_("WindowClass_2", 0), @\CurrPID) \GWL_STYLE = #WS_VISIBLE | #WS_CHILD ; Tab must be visible EndWith ; Msg1 = #WM_GETTEXT Msg1 = #WM_SETTEXT Msg2 = #SCI_ADDTEXT ; ////////////////////////////////////////////////////////////////////// ; hwnd = GetOrRunProgr(@Target, process.s) thnd = Target\RetHWnd PID = Target\PID PrintTestHeader(Process.s, @Target) ; ; /////////////////////////////////////////////////////////////////////// ; with PB IDE, standard sendmessage doesn't work or, sometime, crasches it (why) sBuff = "STD HELLO WORLD!" sz = MemoryStringLength(@sBuff) lret = SendMessage_(thnd, Msg1, sz, @sBuff); Assert(lret, 0) ; with PB IDE, CPD sendmessage seems to work well (why) sBuff = "CPD HELLO WORLD!" sz = MemoryStringLength(@sBuff) lret = IPM_InitProcessData(PID, hOpenProcess, sz) ; allocate memory for Text Assert(lret) ; in the other process address space lpTextAddr = IPM_GetDataAddr() lret = IPM_WriteData(@sBuff, sz, lpTextAddr) ; write text in the other process address space sDebug("IPM_WriteData: lret: "+Str(lret)) SetLastError_(0) lret = SendMessage_(thnd, Msg2, lret, lpTextAddr) ; works (using Scintilla message) Assert(lret, 0) ShowWindow_(Target\MainWindow, #SW_RESTORE) SetForegroundWindow_(Target\MainWindow) Delay(1000) ShowWindow_(Target\MainWindow, #SW_MINIMIZE) ;- Case 8 ; send Text to MS Word ;+ process.s = Chr(34)+"C:\Program Files\Microsoft Office97\Office\WINWORD.EXE"+Chr(34) With Target \MainwClass = "OpusApp"; PureBasic Editor \MainwTitle = "Microsoft Word" ; \cClass = "_WwG" ; \cClass = "_WwB" ; \cText = "Documento1" ; \GWL_STYLE = #WS_VISIBLE | #WS_CHILD ; Tab must be visible EndWith ; Msg1 = #WM_GETTEXT Msg1 = #WM_SETTEXT ;Msg2 = #SCI_ADDTEXT ; ////////////////////////////////////////////////////////////////////// ; hwnd = GetOrRunProgr(@Target, process.s) thnd = Target\RetHWnd PID = Target\PID PrintTestHeader(Process.s, @Target) ; ; /////////////////////////////////////////////////////////////////////// ; with WINWORD, standard sendmessage doesn't work sBuff = "STD HELLO WORLD!" sz = MemoryStringLength(@sBuff) lret = SendMessage_(thnd, Msg1, 0, @sBuff); Assert(lret, 0) ; with PB IDE, CPD sendmessage seems to work well (why) sBuff = "CPD HELLO WORLD!" sz = MemoryStringLength(@sBuff) lret = IPM_InitProcessData(PID, hOpenProcess, sz) ; allocate memory for Text Assert(lret) ; in the other process address space lpTextAddr = IPM_GetDataAddr() lret = IPM_WriteData(@sBuff, sz, lpTextAddr) ; write text in the other process address space sDebug("IPM_WriteData: lret: "+Str(lret)) SetLastError_(0) lret = SendMessage_(thnd, Msg1, 0, lpTextAddr) Assert(lret, 0) ShowWindow_(Target\MainWindow, #SW_RESTORE) SetForegroundWindow_(Target\MainWindow) Delay(1000) ShowWindow_(Target\MainWindow, #SW_MINIMIZE) ; /////////////////////////////////////// ;- EndSelect IPM_TermProcessData(0, 0) sDebug("-------- Test Nr: " + Str(TestNr)+" Completed ----------------------------") sDebug("") Next TestNr ProcedureReturn 1 EndProcedure _DllTest_() CompilerEndIf ; IDE Options = PureBasic 4.40 (Windows - x86) ; ExecutableFormat = Shared Dll ; CursorPosition = 2 ; Folding = E+HA8-- ; Markers = 965 ; EnableUnicode ; EnableXP ; Executable = ..\Tracer\pbTracer\TypeOf\debug.exe ; EnabledTools = XCOPY