OK, I've got it and have attached working code below.
As mentioned above, I added some subroutines to APISPY32.c
I modified the DllMain routine to keep APISPY from installing when the dll
attaches to the VB end of things
Code:
case DLL_PROCESS_ATTACH:
HInstance = hInst;
FChicago = (BOOL)((GetVersion() & 0xC0000000) == 0xC0000000);
if (hWndVB > 0){//only do this for external process
if ( InitializeAPISpy32() == FALSE )
return 0;
if ( InitThreadReturnStack() == FALSE )
return 0;
hWndSPY = hInst;//mark this instance
}
break;
case DLL_THREAD_ATTACH:
if (HInstance = hWndSPY)
if ( InitThreadReturnStack() == FALSE )
return 0;
break;
case DLL_THREAD_DETACH:
if (HInstance = hWndSPY)
if ( ShutdownThreadReturnStack() == FALSE )
return 0;
break;
case DLL_PROCESS_DETACH:
if (HInstance = hWndSPY){
ShutDownAPISpy32();
ShutdownThreadReturnStack();
if (hhookHooks != 0){
result = UnhookWindowsHookEx(hhookHooks); hhookHooks = 0;
hWndVB = 0;
}
}
break;
Added two exported functions to be called from VB:
A function to Set a hook from VB; we pass to it the ThreadID to hook as well as a handle to a window in VB to receive callback messages from dll.
Code:
__declspec(dllexport) int _stdcall InstallFilterDLL(DWORD dwThreadId, HANDLE hWndReturn)
{
if (hhookHooks==0)
hhookHooks = SetWindowsHookEx(WH_CALLWNDPROC,(HOOKPROC) CallWndProc, (HINSTANCE)
HInstance, dwThreadId);
if (hhookHooks==0) return GetLastError();
hWndVB = hWndReturn;
return 0;
}
And one to remove the hook
Code:
__declspec(dllexport) int UnInstallFilterDLL(void)
{
LRESULT result;
if (hhookHooks != 0){
result = UnhookWindowsHookEx(hhookHooks);
hhookHooks = 0;
hWndVB = 0;
}
return 0;
}
The Window Procedure function for monitoring the messages
Currently it only serves to monitor for shutdown of target app
Code:
LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
CWPSTRUCT *lpMsg;
LRESULT result = 0;
//return immediately for negative nCodes
if (nCode >= 0){
if (nCode==HC_ACTION){
lpMsg = (CWPSTRUCT *) lParam;
switch (lpMsg->message){
case WM_CLOSE://we're shutting down
//notify VB we're shutting down
if (hWndVB>0) SendMessage(hWndVB, UM_STOP, 0, (LPARAM)result);
//remove the hook
if (hhookHooks != 0){
result = UnhookWindowsHookEx(hhookHooks);
hhookHooks = 0;
hWndVB = 0;
}
break;
}//end switch
}//end HC_ACTION
}//end nCode >=0
return(CallNextHookEx(hhookHooks, nCode, wParam, lParam));
}
Here we have three two user-defined messages and two other constants
Code:
#define UM_STOP WM_USER + 0x101 //message from dll to VB; dll is shutting down
#define UM_ERROR WM_USER + 0x102 //message sent from dll to VB; an error has occurred
#define ERR_INITSPY 1 //Error type; InitializeAPISpy32 failed
#define ERR_INITTHREAD 2 //Error type; InitThreadReturnStack failed
I've also added some shared variables
Code:
#pragma data_seg(".shared")
HANDLE hWndVB = 0; //handle back to subclassed VB Window
HHOOK hhookHooks = 0; // Hook handle
HANDLE hWndSPY = 0; //external process handle
#pragma data_seg()
#pragma comment(linker, "/SECTION:.shared,RWS")
Finally, Ive added a local routine to send data back to the VB App
Code:
void SendData(char* pszOutStr)
{
if (hWndVB > 0){
COPYDATASTRUCT MyCDS;
MyCDS.dwData = 1; // function identifier
MyCDS.cbData = strlen( pszOutStr ); // size of data
MyCDS.lpData = pszOutStr; // data structure
SendMessage(hWndVB, WM_COPYDATA, (WPARAM) HInstance, (LPARAM) &MyCDS);
}
}
In Addition to these modifications, I modified LOG.C to send data back to VB
Define our sending function which resides in WINAPISPY32.c
Code:
void SendData(char* pszOutStr);
At two spots LOG.c writes data, so at those points send data to our function also
One is in function LogCall
Code:
void __stdcall LogCall(PSTR pszName, PBYTE pParams, PDWORD pFrame)
{
char szOutstr[1024];//buffer to hold entire data string
.
.
.
fprintf(PLogFile, "%s%s(%s)\n", szIndent, pszName, szParams);
fflush(PLogFile);
//********* Added By Moeur **************************************
wsprintf(szOutstr, "%s%s(%s)\n", szIndent, pszName, szParams);
SendData(szOutstr);
//-----------------------------------------------------------------
.
.
.
}
The other is in LogReturn
Code:
void LogReturn(PSTR pszName, DWORD returnValue, DWORD level)
{
char szIndent[128];
//********* Added By Moeur **************************************
char szOutstr[1024];//buffer to hold output string
//-----------------------------------------------------------------
if ( !PLogFile ) return;
MakeIndentString(szIndent, level);
fprintf(PLogFile, "%s%s returns: %X\n", szIndent, pszName, returnValue);
fflush(PLogFile);
//********* Added By Moeur **************************************
wsprintf(szOutstr, "%s%s returns: %X\n", szIndent, pszName, returnValue);
SendData(szOutstr);
//-----------------------------------------------------------------
}
Don't forget your .def file
On the VB end, we have to declare our two dll routines as well as some other API stuff
VB Code:
Option Explicit
'Change to where your dll is located
Private Declare Function InstallFilterDLL Lib "C:\bin\APISPY32.dll" ( _
ByVal dwThreadID As Long, _
ByVal lhWnd As Long _
) As Long
Private Declare Function UnInstallFilterDLL Lib "C:\bin\APISPY32.dll" () As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String _
) As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" ( _
ByVal hwnd As Long, _
lpdwProcessId As Long _
) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" ( _
ByVal hwnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
lParam As Any _
) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
Destination As Any, _
Source As Any, _
ByVal Length As Long _
)
Const WM_COPYDATA = &H4A 'message from dll with our data
Const WM_USER = &H400
Const UM_STOP = WM_USER + &H101 'message from dll to VB; dll is shutting down
Const UM_ERROR = WM_USER + &H102 'message sent from dll to VB; an error has occurred
Const ERR_INITSPY = 1 'Error type; InitializeAPISpy32 failed
Const ERR_INITTHREAD = 2 'Error type; InitThreadReturnStack failed
'data structure for WM_COPYDATA
Private Type COPYDATASTRUCT
dwData As Long
cbData As Long
lpData As Long
End Type
Next we subclass something to receive callback messages from the dll, get a handle to the target, and set the hook.
VB Code:
Private Sub Command1_Click()
Dim hWndCalc As Long, ThreadID As Long
'Subclass the subclass control
subClass.Enable 0
'Get handle to Calculator
hWndCalc = FindWindow("SciCalc", "Calculator")
While hWndCalc = 0
If MsgBox("Open Calculator", vbOKCancel) = vbCancel Then Exit Sub
hWndCalc = FindWindow("SciCalc", "Calulator")
Wend
'Get ThreadID to Calculator
ThreadID = GetWindowThreadProcessId(hWndCalc, 0)
'set The Hook which starts APISPY
If InstallFilterDLL(ThreadID, subClass.hwnd) Then
MsgBox "Cannot Install Hook"
End If
End Sub
Before our program ends, remove the hook and subclass
VB Code:
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
UnInstallFilterDLL
subClass.Disable
End Sub
When one of our special messages comes in we deal with it
VB Code:
Private Sub subClass_WindProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long)
Dim inData As COPYDATASTRUCT
Select Case uMsg
Case Is = UM_STOP
MsgBox "App is shutting down"
Case Is = UM_ERROR
If lParam = ERR_INITSPY Then
MsgBox "DLL Error Initializing APISPY"
Else
MsgBox "DLL Error Initializing APISPY"
End If
Case Is = WM_COPYDATA ' our data has arrived
'wParam - Handle to window passing us the data
'lParam - pointer to data
Call CopyMemory(inData, ByVal lParam, 12)
'inData.dwData - function identifier
'inData.cbData - number of bytes in lpData
'inData.lpData - pointer to data
Text1 = Text1 & StrFromPtr(inData.lpData, inData.cbData)
End Select
End Sub
Here is the utility to deal with strings from pointers
VB Code:
Function StrFromPtr(pStr As Long, strCNT As Long) As String
Dim bTemp As Byte
Dim i As Long
Dim x As String
x = ""
For i = 0& To strCNT - 1
CopyMemory bTemp, ByVal pStr + i, 1
If bTemp = 10 Then
x = x & vbCrLf
Else
x = x & Chr(bTemp)
End If
Next i
StrFromPtr = x
End Function
The attached code should work out of the box, but if there is a problem let me know