Indeed, here it's not needed , I just took a send_copydata function where it was needed ...Is that actually necessary?
New WM_COPYData Examples
Moderators: Hacker, petermad, Stefan2, white
- franck8244
- Power Member
- Posts: 704
- Joined: 2003-03-06, 17:37 UTC
- Location: Geneva...
- Balderstrom
- Power Member
- Posts: 2148
- Joined: 2005-10-11, 10:10 UTC
There's a request I believe for retrieving the selected file list via the new WM_COPYData mechanism.
And now that it's implemented, a few things I've asked in the past could be done:
ActiveTab#, ActiveTabName
And with the 64bit compile using lazarus, most of the Window Panels/Controls are all classed Window#
---- It would be great to be able to query the HWND of a given control by name.
E.g. command char might be "G" and implemented similiar to how TC can accept an EM_UserCommand or CD-path by name --- but instead return the HWND of the given control.
There are a number of things that are not possible or very difficult with the lazarus compile since all controls except the FileLists are classed Window#.
And now that it's implemented, a few things I've asked in the past could be done:
ActiveTab#, ActiveTabName
And with the 64bit compile using lazarus, most of the Window Panels/Controls are all classed Window#
---- It would be great to be able to query the HWND of a given control by name.
E.g. command char might be "G" and implemented similiar to how TC can accept an EM_UserCommand or CD-path by name --- but instead return the HWND of the given control.
There are a number of things that are not possible or very difficult with the lazarus compile since all controls except the FileLists are classed Window#.
Example for AutoIt:
Code: Select all
#Include <GUIConstantsEx.au3>
#include <SendMessage.au3>
Opt('GUIOnEventMode', 1)
Global $hGUI
_Main()
Func _Main()
Local $iBtn
$hGUI = GUICreate('Test', 150, 50)
GUISetOnEvent($GUI_EVENT_CLOSE, 'Event_GUIClose')
$iBtn = GUICtrlCreateButton('Click Here', 10, 10, 100, 30)
GUICtrlSetOnEvent(-1, 'handle_button_press')
; This registers the function to call when we receive a WM_COPYDATA (0x004A) back from TC:
GUIRegisterMsg(0x004A, 'Our_Func_for_WM_COPYDATA')
GUISetState()
While 1
Sleep(500)
WEnd
EndFunc ;==>_Main
Func _Msg2TC($prmCommand = "A")
$hTC = WinGetHandle("[CLASS:TTOTAL_CMD]")
$dwData = Asc("G") + 256 * Asc("A")
$lpDataLen = StringLen($prmCommand)
$lpData = DllStructCreate("char[" & $lpDataLen + 1 & "]")
DllStructSetData($lpData, 1, $prmCommand)
$lpDataPtr = DllStructGetPtr($lpData)
$structCOPYDATA = DllStructCreate("dword;dword;ptr")
DllStructSetData($structCOPYDATA, 1, $dwData)
DllStructSetData($structCOPYDATA, 2, $lpDataLen)
DllStructSetData($structCOPYDATA, 3, $lpDataPtr)
$ptrCOPYDATA = DllStructGetPtr($structCOPYDATA)
$arrReturn = _SendMessage($hTC, 0x004A, $hGUI, $ptrCOPYDATA)
EndFunc
Func Our_Func_for_WM_COPYDATA($hWnd, $iMsg, $wParam, $lParam)
$returnCOPYDATA = DllStructCreate("dword;dword;ptr", $lParam)
$returnDWData = DllStructGetData($returnCOPYDATA, 1)
$returnLPDataLen = DllStructGetData($returnCOPYDATA, 2)
$returnLPDataPtr = DllStructGetData($returnCOPYDATA, 3)
$returnLPData = DllStructCreate("char[" & $returnLPDataLen + 1 & "]", $returnLPDataPtr)
$retValue = DllStructGetData($returnLPData, 1)
; Avoid MsgBox in real life at this point (see AutoIt Help 'GUIRegisterMsg')
MsgBox(0,"TC Reply", $retValue)
EndFunc
Func handle_button_press()
_Msg2TC()
EndFunc
Func Event_GUIClose()
Exit
EndFunc
I attemp to write a autohotkey script in order to send a em_xx cmd to TC, I see the sample code here and did a test, unfortunately it didn't work, anybody can tell me what is the problem in my code?
PS: I am using autohotkey_L 1.1.08.01 x64, and Total commander 8.01 rc5 X64
and here is my em_cmd
PS: I am using autohotkey_L 1.1.08.01 x64, and Total commander 8.01 rc5 X64
Code: Select all
#NoTrayIcon
#SingleInstance force
SetBatchLines -1
SendMode, Input
TC_SendWMCopyData("EM", "em_test", "", "ahk_class TTOTAL_CMD")
return
TC_SendWMCopyData( cmdType, byRef cmd, byRef addParams="", aWin="A" )
{
Critical
VarSetCapacity( CopyDataStruct, A_PtrSize * 3 )
if( A_IsUnicode )
{
VarSetCapacity( cmdA, StrPut(cmd, "cp0"))
Loop, % StrLen(cmd)
NumPut( Asc(SubStr(cmd, A_Index, 1)), cmdA, A_Index - 1, "Char")
}
NumPut( Asc(SubStr(cmdType,1,1)) + 256 * Asc(SubStr(cmdType,2,1)), CopyDataStruct )
NumPut( StrLen(cmd) + (cmdType="CD" ? 5 : 1), CopyDataStruct, A_PtrSize )
NumPut((A_IsUnicode ? &cmdA : &cmd), CopyDataStruct, A_PtrSize * 2)
Loop, % (cmdType=="CD" ? 2 : 0)
NumPut( Asc(SubStr(addParams, A_Index, 1)), (A_IsUnicode ? cmdA : cmd), (StrLen(cmd) + A_Index), "Char" )
SendMessage, 0x4A,, &CopyDataStruct,, ahk_id %aWin%
return
}
Code: Select all
[em_test]
button=
cmd=notepad
- Balderstrom
- Power Member
- Posts: 2148
- Joined: 2005-10-11, 10:10 UTC
That looks like the current version of my code, I think yer not calling it correctly.
Note parameter #2: cmd, is a byRef - it has to be a variable.
So you call it like this:
But I use a higher-level function to call into TC_SendWMCopyData
e.g.
So you would do:
Which keeps global variables out of the namespace as well.
EDIT: That wont work, in my actual TC_EMC, I also call:
TC_Activate()
Wherein, wID is assigned to it's ID# (ahk_id).
Now you don't necessarily have to do all that. But the WMCopyData function is expecting a ID# so minimally you need to do
wID:=WinExist("ahk_class TTOTAL_CMD")
or
wID:=WinActive("ahk_class TTOTAL_CMD")
So long as it's the only instance of TC running, it would be fine.
It's probably a slight bug that the default variable for wID is ="A" in TC_SendWMCopyData() as that wont work with the SendMessage --- I just never use it like that and didn't really notice --- I always call it with caller functions, like TC_EMC(), TC_CMD() and TC_CD()
TC_CD() is a neat piece of code, but it pretty much requires a LIB.ahk as it uses a fair number of defined functions, not really pasteable here.
Code: Select all
TC_SendWMCopyData("EM", "em_test", "", "ahk_class TTOTAL_CMD")
return
TC_SendWMCopyData( cmdType, byRef cmd, byRef addParams="", aWin="A" )
So you call it like this:
Code: Select all
TC_SendWMCopyData("EM", gCmd:="em_test", gNul:="", "ahk_class TTOTAL_CMD")
e.g.
Code: Select all
TC_EMC( cmd, wID="ahk_class TTOTAL_CMD")
{
TC_SendWMCopyData( "EM", cmd, params:="", wID )
return
}
Code: Select all
TC_EMC("em_test")
EDIT: That wont work, in my actual TC_EMC, I also call:
TC_Activate()
Code: Select all
TC_EMC( cmd, wID="ahk_class TTOTAL_CMD", activateWin=FALSE, showMsg=FALSE )
{
TC_Activate( wID, activateWin, showMsg, cmd )
TC_SendWMCopyData( "EM", cmd, params:="", wID )
return
}
Code: Select all
TC_Activate( byRef wID, activateWin=TRUE, showMsg=TRUE, cmd="" )
{
wID:=QueryWinID(wID, TRUE)
; MsgBox, %A_ThisFunc% :: wID: %wID%
if(!activateWin ) ;&& WinExist("ahk_id " wID))
return FALSE
if( showMsg )
MsgBox,,%A_ThisFunc%, % "Activating TC" ( cmd ? ", for command: " cmd "`n" : "`n"), 1
MsgBox, %A_ThisFunc%, Activating TC
WinActivate, ahk_id %wID%
return TRUE
}
QueryWinID( aWin="A", canExist=FALSE, winText="", notTitle="", notText="" )
{
; MsgBox,, %A_ThisFunc%, aWin: %aWin%, canExist: %canExist%
if( !(retVal:=WinActiveA( aWin, winText, notTitle, notText )) )
retVal:=( !canExist ? 0 : WinExistA( aWin, winText, notTitle, notText ))
return retVal
}
WinActiveA( aWin="", winText="", notTitle="", notText="" )
{
return WinActive( (aWin+0 ? "ahk_id " aWin : aWin), winText, notTitle, notText )
}
WinExistA( aWin="", winText="", notTitle="", notText="" )
{
return WinExist( (aWin+0 ? "ahk_id " aWin : aWin), winText, notTitle, notText )
}
wID:=WinExist("ahk_class TTOTAL_CMD")
or
wID:=WinActive("ahk_class TTOTAL_CMD")
So long as it's the only instance of TC running, it would be fine.
It's probably a slight bug that the default variable for wID is ="A" in TC_SendWMCopyData() as that wont work with the SendMessage --- I just never use it like that and didn't really notice --- I always call it with caller functions, like TC_EMC(), TC_CMD() and TC_CD()
Code: Select all
TC_CMD( cmd, wID="ahk_class TTOTAL_CMD", activateWin=FALSE, post=TRUE, showMsg=FALSE )
{
TC_Activate( wID, activateWin, showMsg, cmd )
if( !post )
SendMessage, 0x433, TC_@( cmd ), 0x0,, ahk_id %wID%
else
PostMessage, 0x433, TC_@( cmd ), 0x0,, ahk_id %wID%
return TRUE
}
TC_@( cm_cmd="" )
{
static
static init := 0
global TC__TotalCmd@INC
local iCmd, iCmd1, iCmd2, iCmd3, iTmp, retVal, tmpVar
if( !init && init := 1 && !ErrorLevel:="")
{
local aDrive, aFile, aPath
if(!(aPath:=TC__TotalCMD@INC))
{
EnvGet, aPath, COMMANDER_PATH
; MsgBox, TC__TotalCmd@INC: %TC__TotalCmd@INC%
if( !aPath )
{
EnvGet, aDrive, SystemDrive
if(!FileExist(aPath:=aDrive "\TotalCmd"))
if(!FileExist(aPath:=ProgramFiles "\TotalCMD"))
aPath:=ProgramFiles
FileSelectFile, TC__TotalCmd@INC,, %aPath%,Location of TotalCMD.inc file?,*.inc
}
else
TC__TotalCmd@INC:=aPath "\TotalCMD.inc"
}
; MsgBox, TC__TotalCmd@INC: %TC__TotalCmd@INC%
; MsgBox, aPath: %aPath%
SplitPath( TC__TotalCmd@INC, aFile )
; MsgBox, aFile: %aFile%`nErrorLevel: %ErrorLevel%
onErrorExit((ErrorLevel || !RegExMatch( aFile, "i)^TotalCMD.inc$")) && !(init:=0), "ERROR: Invalid TotalCMD.inc File.")
FileRead, TcmdINC, %TC__TotalCmd@INC%
Loop, Parse, TcmdINC, `n, `r
{
if( !regExMatch( A_LoopField, "^cm_([a-zA-Z]+)([0-9]+)?=([0-9]+);", iCmd ) )
continue
if( iCmd3 > 6000 && iCmd3 <= 20000)
continue
if( iCmd3 > 5000 && iCmd3 <= 5500)
continue
else
if( iCmd3 > 2060 && iCmd3 <= 2120 ) ; cm_GotoDriveA ...
continue
else
if( iCmd3 >= 701 && iCmd3 <= 900 ) ; cm_UserMenu1 ... cm_UserMenu300
continue
else
if( iCmd3 >= 271 && iCmd3 <= 299 ) ; cm_SrcCustomView1 - 29
continue
else
if( iCmd3 >= 171 && iCmd3 <= 199 ) ; cm_RightCustomView1 - 29
continue
else
if( iCmd3 >= 71 && iCmd3 <= 99 ) ; cm_LeftCustomView1 - 29
continue
tmpVar = cm_%iCmd1%%iCmd2%
StringUpper, tmpVar, tmpVar
%tmpVar% := iCmd3
}
if( !cm_cmd )
return
}
Sleep, 50
if( !cm_cmd )
return
; if( )
onErrorExit( !inStr( cm_cmd, "cm_"), "UnSupported: Input must be a `cm_` string." cm_cmd ": " %cm_cmd%, A_ThisFunc )
StringUpper, cm_cmd, cm_cmd
if( retVal := %cm_cmd% )
return retVal
if( RegExMatch( cm_cmd, "^CM_(LEFT|RIGHT|SRC)(CUSTOMVIEW)(\d+)$", iCmd ) )
{
onErrorExit( ( iCmd3 < 1 || iCmd3 > 29 ), "Illegal value for: " cm_cmd, A_ThisFunc )
local LEFT := 0, RIGHT := 100, SRC := 200
return % ( %iCmd1% + iCmd3 + 70 )
}
if( RegExMatch( cm_cmd, "^(CM_)(USERMENU)(\d+)$", iCmd ) )
{
onErrorExit( ( iCmd3 < 1 || iCmd3 > 200 ), "Illegal value for: " cm_cmd, A_ThisFunc )
return ( iCmd3 + 700 )
}
if( RegExMatch( cm_cmd, "CM_(LEFT|RIGHT|SRC|TRG)(ACTIVATETAB|SORTBYCOL)(\d+)", iCmd ) )
{
onErrorExit( ( iCmd3 < 1 || iCmd3 > 99 ), "Illegal value for: " cm_cmd , A_ThisFunc )
local SRC := 0, TRG := 100, LEFT := 200, RIGHT := 300
local ACTIVATETAB := 5000, SORTBYCOL := 6000
return % ( %iCmd1% + %iCmd2% + iCmd3 )
}
if( RegExMatch( cm_cmd, "(CM_)(GOTODRIVE)([A-Z])", iCmd ) )
{
return ( ASC(iCmd3) + 2000 -4 )
}
;onErrorExit( TRUE, "Variable: " cm_cmd " doesn't exist!", A_ThisFunc )
}
onErrorExit( checkEval, errMsg="", fn="", delay=3 )
{
if( !checkEval )
return 0
if((!ErrorLevel || ErrorLevel:=1) && !errMsg)
Exit
RegExMatch(fn, "^([^:]+)(::([^ ].*))?$", rTmp )
msg := ( rTmp1 ? rTmp1 "(" rTmp3 "): " : "" ) errMsg
MsgBox,, % A_ScriptName " :: " rTmp1, % msg, % delay
Exit
}
SplitPath( fullPath, byRef aFile="", byRef aDir="", byRef aExt="", byRef aDrive="", byRef aNameOnly="" )
{
fullPath.=((inStr(FileExist(fullPath), "D") && SubStr(fullPath, 0) <> "\") ? "\" : "")
SplitPath, fullPath, aFile, aDir, aExt, aNameOnly, aDrive
aDir.=(aDir && (aDir==aDrive)) ? "\" : ""
return
}
thanks Balderstrom, my code can work now,
just as you said, the problem is on the last param of TC_SendWMCopyData, a winExist("ahk_class TTOTAL_CMD") or WinActive("ahk_class TTOTAL_CMD") should be necessary.
I have another two questions:
1. the input parameter of "CD" is so complex, src" `r"target" ", do we MUST write the parameter like this?
2. it seems the "CD" and "EM" are both internal message format defined by TC, but I didn't find the detail information for these message types, can you tell you where can I get the detail description?
just as you said, the problem is on the last param of TC_SendWMCopyData, a winExist("ahk_class TTOTAL_CMD") or WinActive("ahk_class TTOTAL_CMD") should be necessary.
I have another two questions:
1. the input parameter of "CD" is so complex, src" `r"target" ", do we MUST write the parameter like this?
2. it seems the "CD" and "EM" are both internal message format defined by TC, but I didn't find the detail information for these message types, can you tell you where can I get the detail description?
- Balderstrom
- Power Member
- Posts: 2148
- Joined: 2005-10-11, 10:10 UTC
Mr.Ghisler used half of a carriage return "`r" of an "`r`n" to separate two possible variables, as a SendMessage can only pass a single parameter. Likewise, a file path can't contain a partial carriage return.
Thus, SRC_Path "`r" TRG_Path
Since it is possible to only change a single panel's path, e.g.
examplePath:="`r" TRG_Path ; Only change Target Panel
examplePath:=SRC_Path "`r" ; Only change Source Panel
examplePath:=SRC_Path "`r" TRG_Path ; Change both Panels.
Thus, SRC_Path "`r" TRG_Path
Since it is possible to only change a single panel's path, e.g.
examplePath:="`r" TRG_Path ; Only change Target Panel
examplePath:=SRC_Path "`r" ; Only change Source Panel
examplePath:=SRC_Path "`r" TRG_Path ; Change both Panels.
*BLINK* TC9 Added WM_COPYDATA and WM_USER queries for scripting.
- Balderstrom
- Power Member
- Posts: 2148
- Joined: 2005-10-11, 10:10 UTC
Code: Select all
WinActiveA( aWin="", winText="", notTitle="", notText="" )
{
return WinActive( (aWin+0 ? "ahk_id " aWin : aWin), winText, notTitle, notText )
}
It allows you to pass around either a "string" for aWin (e.g. ahk_class TTOTAL_CMD), or a winID.
aWin+0 will be an empty-string if it IS a string and you add 0 to it.
aWin+0 will remain a number if it was a number and you add 0 to it.
Thus we have,
Code: Select all
if( aWin+0 ) ; if a number
return WinActive("ahk_id " aWin, winText, notTitle, notText)
else
return WinActive(aWin, winText, notTitle, notText)
Thus it becomes cleaner to write functions that rely on each other when you can do:
SomeFunction(winID, TRUE)
And within SomeFunction() we validate the winID by using WinActiveA() or WinExistA() instead of the vanilla AHK functions WinActive/WinExist. If you use the default AHK functions, then you need to always know what it is you are passing around --- and in a scripting language that mutates strings to numbers and vica versa - it is beneficial to have functions that can handle both as well.
The complete syntax is in fact :fallmq wrote:thanks Balderstrom, my code can work now,
just as you said, the problem is on the last param of TC_SendWMCopyData, a winExist("ahk_class TTOTAL_CMD") or WinActive("ahk_class TTOTAL_CMD") should be necessary.
I have another two questions:
1. the input parameter of "CD" is so complex, src" `r"target" ", do we MUST write the parameter like this?
2. it seems the "CD" and "EM" are both internal message format defined by TC, but I didn't find the detail information for these message types, can you tell you where can I get the detail description?
<Left>\r<Right>\0
<Source>\r<Target>\0S
<Left>\r<Right>\0T open in new Tab
If you only need to open in current source tab you can define a user command em_cd with command cd AND %A As parameter;
After, you can use your em_cd like sending em_cd D:\MyFolder it will also allows you to add filter like em_cd *.ini *.reg *.key
-- edited --
We already add such discussion on em_cmd and specific CD command in 2006 ! http://ghisler.ch/board/viewtopic.php?p=104086#104086 and http://www.ghisler.ch/board/viewtopic.php?p=105246
When i use:ZoSTeR wrote:Example for AutoIt:
Code: Select all
#Include <GUIConstantsEx.au3> #include <SendMessage.au3> Opt('GUIOnEventMode', 1) Global $hGUI _Main() Func _Main() Local $iBtn $hGUI = GUICreate('Test', 150, 50) GUISetOnEvent($GUI_EVENT_CLOSE, 'Event_GUIClose') $iBtn = GUICtrlCreateButton('Click Here', 10, 10, 100, 30) GUICtrlSetOnEvent(-1, 'handle_button_press') ; This registers the function to call when we receive a WM_COPYDATA (0x004A) back from TC: GUIRegisterMsg(0x004A, 'Our_Func_for_WM_COPYDATA') GUISetState() While 1 Sleep(500) WEnd EndFunc ;==>_Main Func _Msg2TC($prmCommand = "A") $hTC = WinGetHandle("[CLASS:TTOTAL_CMD]") $dwData = Asc("G") + 256 * Asc("A") $lpDataLen = StringLen($prmCommand) $lpData = DllStructCreate("char[" & $lpDataLen + 1 & "]") DllStructSetData($lpData, 1, $prmCommand) $lpDataPtr = DllStructGetPtr($lpData) $structCOPYDATA = DllStructCreate("dword;dword;ptr") DllStructSetData($structCOPYDATA, 1, $dwData) DllStructSetData($structCOPYDATA, 2, $lpDataLen) DllStructSetData($structCOPYDATA, 3, $lpDataPtr) $ptrCOPYDATA = DllStructGetPtr($structCOPYDATA) $arrReturn = _SendMessage($hTC, 0x004A, $hGUI, $ptrCOPYDATA) EndFunc Func Our_Func_for_WM_COPYDATA($hWnd, $iMsg, $wParam, $lParam) $returnCOPYDATA = DllStructCreate("dword;dword;ptr", $lParam) $returnDWData = DllStructGetData($returnCOPYDATA, 1) $returnLPDataLen = DllStructGetData($returnCOPYDATA, 2) $returnLPDataPtr = DllStructGetData($returnCOPYDATA, 3) $returnLPData = DllStructCreate("char[" & $returnLPDataLen + 1 & "]", $returnLPDataPtr) $retValue = DllStructGetData($returnLPData, 1) ; Avoid MsgBox in real life at this point (see AutoIt Help 'GUIRegisterMsg') MsgBox(0,"TC Reply", $retValue) EndFunc Func handle_button_press() _Msg2TC() EndFunc Func Event_GUIClose() Exit EndFunc
Code: Select all
$dwData = Asc("G") + 256 * Asc("A")
Instead, when i use:
Code: Select all
$dwData = Asc("G") + 256 * Asc("W")
Why?
A year too late but better than never 
You have to change the "char" in
to "wchar"

You have to change the "char" in
Code: Select all
$returnLPData = DllStructCreate("char[" & $returnLPDataLen + 1 & "]", $returnLPDataPtr)
Code: Select all
$returnLPData = DllStructCreate("wchar[" & $returnLPDataLen + 1 & "]", $returnLPDataPtr)