这个问题不好搞,试了半天也没成功,只能通过间接法弄了。
原理
通过VBA 向AHK发送WM_COPYDATA,再由AHK 转送到TC
用法
- 在
usercmd.ini
中配置对应的命令,例如
[em_focusfile]
cmd=CD
param=D:\SoftX\AHK_Scripts_V2\Lib\
- 运行下面的ahk脚本
- 在Excel 中运行下述VBA代码
#Requires AutoHotkey >=2.0
Persistent
; global retVal:=""
MyGui:=Gui()
MyGui.Title := "TC-VBA-Interposer"
MyGui.Show("hide w500 h200")
TC_Interposer()
return
TC_Interposer()
{ ;https://wincmd.ru/forum/viewtopic.php?p=110848&sid=0dfde01723b39e508e96d62c00a7a9b5
If WinExist("ahk_class TTOTAL_CMD") ;&& WinActive("ahk_class TTOTAL_CMD")
OnMessage(0x4a, TC_Receive_WM_COPYDATA) ; 0x4a is WM_COPYDATA
else
MsgBox "TC does NOT exist"
}
TC_Receive_WM_COPYDATA(wParam, lParam, msg, hwnd)
{
PtrPos:=NumGet(lParam + A_PtrSize * 2,0,"Ptr")
retVal:=StrGet(PtrPos)
if(retVal)
{
; MsgBox retVal
TC_SendUserCommand(retVal)
}
}
/*
TC_SendUserCommand()
fucntion: send user defined command in the usercmd.ini to TC
agruments: command name <em_xxxx> in usercmd.ini
return: none
*/
TC_SendUserCommand(userCommand)
{
; https://www.autohotkey.com/boards/viewtopic.php?p=538463&sid=4471e03917209854441ac07ebdc70901#p538463
static dwData := 19781 ;Ord("E") +256*Ord("M")
static WM_COPYDATA := 0x4A
cbData := Buffer(StrPut(userCommand, 'CP0'))
StrPut(userCommand, cbData, 'CP0')
COPYDATASTRUCT := Buffer(A_PtrSize * 3)
NumPut('Ptr', dwData, 'Ptr', cbData.size, 'Ptr', cbData.ptr, COPYDATASTRUCT)
MsgResult:=SendMessage( WM_COPYDATA,, COPYDATASTRUCT,, 'ahk_class TTOTAL_CMD')
return MsgResult
}
Option Explicit
Private Declare PtrSafe Function FindWindow Lib "USER32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare PtrSafe Function SendMessage Lib "USER32" Alias "SendMessageA" (ByVal hWnd As LongPtr, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As LongPtr
Private Declare PtrSafe Function GetWindowTextLength Lib "USER32" Alias "GetWindowTextLengthA" (ByVal hWnd As Long) As Long
Private Declare PtrSafe Function GetWindowText Lib "USER32" Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Private Declare PtrSafe Function GetWindow Lib "USER32" (ByVal hWnd As Long, ByVal wCmd As Long) As Long
Private Declare PtrSafe Function IsWindowVisible Lib "USER32" (ByVal hWnd As Long) As Boolean
Public Declare PtrSafe Function GetParent Lib "user32.dll" (ByVal hWnd As LongPtr) As LongPtr
Public Declare PtrSafe Function GetWindowThreadProcessId Lib "USER32" (ByVal hWnd As LongPtr, lpdwProcessId As Long) As Long
Public Type CopyDataStruct
dwData As LongPtr
cbData As Long
lpData As LongPtr
End Type
Private Const WM_COPYDATA = &H4A
Private Const GW_HWNDNEXT = 2
Function GetHiddenWinHwndByTitle(strWinTitle) As Long
Dim lhWndP As Long
If GetHandleFromPartialCaption(lhWndP, strWinTitle) = True Then
GetHiddenWinHwndByTitle = lhWndP
Else
GetHiddenWinHwndByTitle = 0
End If
End Function
Sub TC_SendUserCMD()
Dim UserCMD As String
UserCMD = "em_focusfile"
Dim cds As CopyDataStruct, result As LongPtr
Dim hwndAHKInterposer As Long
Dim wParam As Long
wParam = 0
hwndAHKInterposer = GetHiddenWinHwndByTitle("TC-VBA-Interposer")
'Debug.Print hwndAHKInterposer
If (hwndAHKInterposer > 0) Then
cds.dwData = Asc("E") + 256 * Asc("M")
cds.cbData = Len(UserCMD) * 2 + 2 'The size, in bytes
cds.lpData = StrPtr(UserCMD)
result = SendMessage(hwndAHKInterposer, WM_COPYDATA, wParam, cds)
Debug.Print hwndAHKInterposer
End If
End Sub
Private Function GetHandleFromPartialCaption(ByRef lWnd As Long, ByVal sCaption As String) As Boolean
'https://copyprogramming.com/howto/how-to-locate-the-window-using-findwindow-function-in-windowapi-using-vba
Dim lhWndP As Long
Dim sStr As String
GetHandleFromPartialCaption = False
lhWndP = FindWindow(vbNullString, vbNullString) 'PARENT WINDOW
Do While lhWndP <> 0
sStr = String(GetWindowTextLength(lhWndP) + 1, Chr$(0))
GetWindowText lhWndP, sStr, Len(sStr)
sStr = Left$(sStr, Len(sStr) - 1)
If InStr(1, sStr, sCaption) > 0 Then
GetHandleFromPartialCaption = True
lWnd = lhWndP
Debug.Print sStr
Exit Do
End If
lhWndP = GetWindow(lhWndP, GW_HWNDNEXT)
Loop
End Function
Function WndFromPID(ByVal Xpid As LongPtr) As LongPtr
'https://stackoverflow.com/questions/7807150/how-can-i-get-a-handle-to-a-window-of-executed-process-in-vba
Dim Nhwnd As LongPtr, Npid As Long, Nthread_id As Long
' Get the first window handle.
Nhwnd = FindWindow(vbNullString, vbNullString) ' NOT (ByVal 0&, ByVal 0& ) !
' Loop until we find the target or we run out of windows
Do While Nhwnd <> 0
' See if this window has a parent. If not, it is a top-level window
If GetParent(Nhwnd) = 0 Then
' This is a top-level window. See if it has the target instance handle
Nthread_id = GetWindowThreadProcessId(Nhwnd, Npid)
If Npid = Xpid Then
WndFromPID = Nhwnd ' This is the target
Exit Do
End If
End If
Nhwnd = GetWindow(Nhwnd, 2) ' Examine the next window [2 = GW_HWNDNEXT]
Loop
End Function