Okay, this interested me enough to proceed anyhow.
The problem, as explained above, is that the dll is being injected into every process running. Now the strings in the global array are only available to the process creating the array - global or not and thus all other processes into which the dll is injected will not be able to access the strings. In fact, all hell breaks loose when you even try!
One solution is to create a chunk of 'shared memory' (accessible by all instances of the dll) and physically copy the string array into it (this is done before the hook is installed). Then, when the global hook is installed into all existing processes, use the AttachProcess() dll function to load a copy of the original array into an array useable by the particular process into which the dll is injected. This means that each running process will have a physical (and separate) copy of the array to which it alone has access.
The dll code: (testdll.dll)
Code: Select all
#FileMapSize = 10000 ;Enusre this is big enough for the string array.
Global hMapObject, hMapView
Global Dim Name$(0)
#_FileMappingObject = "dllSharedMem"
;The following is called once BEFORE the global hook is installed and copies the string array
;to shared memory.
;All other processes into which this dll is injected will retrieve the strings in the AttachProcess()
;procedure.
;Returns non-zero if everything is okay.
ProcedureDLL.l _InitHOOK(*AddressOfGlobalArray, arrayUpperBound)
Protected i, *ptrString.String, ptr
;Create a named file mapping object.
hMapObject = CreateFileMapping_(-1, 0, #PAGE_READWRITE, 0, #FileMapSize, #_FileMappingObject)
If hMapObject
hMapView = MapViewOfFile_(hMapObject,#FILE_MAP_WRITE,0,0,0)
If hMapView
;Copy the upper bound to the memory mapped file.
PokeL(hMapView, arrayUpperBound)
;Now copy the strings.
ptr = hMapView+SizeOf(Long)
*ptrString = *AddressOfGlobalArray
Redim Name$(arrayUpperBound)
For i = 0 To arrayUpperBound
Name$(i) = *ptrString\s
PokeS(ptr, *ptrString\s, -1, #PB_UTF8)
ptr+StringByteLength(*ptrString\s, #PB_UTF8)+1
*ptrString+SizeOf(Long)
Next
ProcedureReturn #True
EndIf
EndIf
ProcedureReturn #False ;Error creating the file mapping object.
EndProcedure
;The following is called whenever the dll is attached to a new process.
;In all cases except the main application, we copy the string array from the shared memory
;into a string array accessible by the particular process into which the dll is injected.
ProcedureDLL AttachProcess(Instance)
Protected i, *ptrString.String, ptr, arrayUpperBound
;Check that this is not the main application.
If hMapObject = 0 ;The shared memory needs to be opened and mapped.
hMapObject = CreateFileMapping_(-1, 0, #PAGE_READWRITE, 0, #FileMapSize, #_FileMappingObject)
If hMapObject
hMapView = MapViewOfFile_(hMapObject,#FILE_MAP_WRITE,0,0,0)
If hMapView
;Copy the string array from shared memory.
arrayUpperBound = PeekL(hMapView)
Redim Name$(arrayUpperBound)
ptr = hMapView+SizeOf(Long)
For i = 0 To arrayUpperBound
Name$(i) = PeekS(ptr, -1, #PB_UTF8)
ptr+StringByteLength(Name$(i), #PB_UTF8)+1
Next
;Now unmap the memory mapped file from this process as it is no longer required.
UnmapViewOfFile_(hMapView)
hMapView=0
EndIf
;Delete the named file mapping object from this process as it is no longer required.
CloseHandle_(hMapObject)
hMapObject = 0
EndIf
EndIf
EndProcedure
;The following is called whenever the dll is detached from an existing process.
;We use it to unmap the memory mapped file and delete the file mapping object from the
;main application as it has already been removed from all other processes.
ProcedureDLL DetachProcess(Instance)
If hMapView
UnmapViewOfFile_(hMapView)
EndIf
If hMapObject
CloseHandle_(hMapObject)
EndIf
Redim Name$(0)
EndProcedure
ProcedureDLL.l CalcRename(nCode.l,wParam.l,*lParam.CWPSTRUCT)
If *lParam\message=#WM_ACTIVATE
fg=*lParam\hwnd
fg$=Space(999)
GetWindowText_(fg,fg$,999)
If Name$(3) = fg$
SetWindowText_(fg,"Ya boo sucks to you!")
EndIf
EndIf
ProcedureReturn CallNextHookEx_(@CalcRename(),nCode,wParam,*lParam)
EndProcedure
Test code :
Code: Select all
Dim Name$(4)
Name$(3)="jaPBe V3" ;Change this to the window title of some existing process on your system.
If OpenWindow(0,300,300,200,100,"Hook example",#PB_Window_SystemMenu)
If OpenLibrary(9,"testdll.dll")
dllInstance=LibraryID(9)
dllFunction=GetFunction(9,"_InitHOOK")
CallFunctionFast(dllFunction,Name$(), 4)
dllFunction=GetFunction(9,"CalcRename")
HookID=SetWindowsHookEx_(#WH_CALLWNDPROC,dllFunction,dllInstance,0)
EndIf
Repeat : Until WaitWindowEvent()=#PB_Event_CloseWindow
If HookID : UnhookWindowsHookEx_(HookID) : CloseLibrary(9) : EndIf
EndIf
On my system (running jaPBe), I run the program and click into the jaPBe window to see the title changed.
Warning, I haven't done much testing!
Also, as commented in the post above, it is not possible (at least with the code posted here) to update the array in the main application and have each process also update their copies of the array. Although you could get around this by using a user defined message since each instance of the dll is monitoring message queues anyhow! I'll leave this to someone else though.