Sign in to follow this  
Followers 0
odklizec

Problem with SendMessage conversion from "C" to AutoIt

14 posts in this topic

Hi guys,

I need to convert relative simple C SendMesage code to AutoIt. But from some strange reasons, I cannot make it to work <_< Maybe I don't see something obvious or it's just that I'm not very experienced with C to AutoIt conversion :) So can someone experienced please look at this?

I want this convert to AutoIt:

SendMessage(hWnd, WM_RUNSCRIPTCODE, RUNSCRIPTCODE_LOUD, LPARAM(LPCTSTR(str)));

I tried the below autoit translation, which I believe should be OK. It creates the structure, gets the structure pointer, but unfortunately, it does not pass the correct result to the last SendMessage parameter. And I know the target application is OK. If the "SendMessage" is done from C code, it works OK.

Global Const $WM_RUNSCRIPTCODE = 0x08C7
$hwnd = WinGetHandle("Welcome!")
$stringToSend="test$='OK'"

$slen = stringlen($stringToSend) + 1
MsgBox(0,"string len",$slen)

$str = "char[" & $slen & "]"
$struct= DllStructCreate($str); make a structure
if @error Then
    MsgBox(0,"","Error in DllStructCreate " & @error);
    exit
endif

DllStructSetData($struct,1,$stringToSend)
$pointer = DllStructGetPtr($struct,1); get the pointer to the string
if @error Then
    MsgBox(0,"","Error in DllStructGetPtr " & @error);
    exit
endif

MsgBox(0,"DllStruct","Struct Size: " & DllStructGetSize($struct) & @CRLF & _
        "Struct pointer: " & DllStructGetPtr($struct) & @CRLF & _
        "Data: " & DllStructGetData($struct,1) & @CRLF)


$result = DllCall("user32.dll","int","SendMessage","hWnd",$hwnd ,"int",$WM_RUNSCRIPTCODE,"int","1","ptr",$pointer)

Does anyone have any idea what's wrong here? Thank you in advance!

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

Well the obvious question...

What is WM_RUNSCRIPTCODE?

(That and where's the C source of this function)

(You also know there's a _SendMessage() function in AutoIt right? (Same API call... but just thought I'd tell you))

Edited by SmOke_N

Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.

Share this post


Link to post
Share on other sites

#3 ·  Posted (edited)

Thanks for reply and suggestion regarding the _SendMessage() Yes, I already tried also this UDF function, unfortunately, with the same result.

I'm almost sure that the problem is in the last "pointer" part. I just don't know what's wrong here?

The WM_RUNSCRIPTCODE is just a message expected in the target application. Basically, it should allow to run the applications' own script from 3rd party apps. The last parameter should be the pointer to a string of the script's source code.

Here is the full description from SDK:

A C++ example might be (using values from the MMBPlugInSDK.h header file):

::SendMessage(hWnd, WM_RUNSCRIPTCODE, RUNSCRIPTCODE_LOUD, LPARAM(LPCTSTR(str)));

Where:

- hWnd is a handle to the application's window obtained via the SetParentWindow

callback of the plugin (check the plugin overviews published in this folder, too)

- WM_RUNSCRIPTCODE is a symbolic constant, a number of the passed message

- RUNSCRIPTCODE_LOUD (also RUNSCRIPTCODE_QUIET) are symbolic constants for showing

(not showing) error messages during processing the script code, if there are any

- str is a pointer to a string of the script's source code

The values of those symbolic constants are:

- WM_RUNSCRIPTCODE stands for 08C7 hexadecimally

- RUNSCRIPTCODE_QUIET stands for 0

- RUNSCRIPTCODE_LOUD stands for 1

Edited by odklizec

Share this post


Link to post
Share on other sites

Thanks for reply and suggestion regarding the _SendMessage() Yes, I already tried also this UDF function, unfortunately, with the same result.

I'm almost sure that the problem is in the last "pointer" part. I just don't know what's wrong here?

The WM_RUNSCRIPTCODE is just a message expected in the target application. Basically, it should allow to run the applications' own script from 3rd party apps. The last parameter should be the pointer to a string of the script's source code.

Here is the full description from SDK:

What happens when you send it as bytes rather than char?

Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.

Share this post


Link to post
Share on other sites

What happens when you send it as bytes rather than char?

Unfortunately, still the same. DllStructGetData returns the value from structure in bytes but the host application seems not getting it.

Here is my actual code:

#include <Misc.au3>

Global Const $WM_RUNSCRIPTCODE = 0x08C7
Global Const $RUNSCRIPTCODE_LOUD = 1
Global Const $RUNSCRIPTCODE_QUIET = 0

$hwnd = WinGetHandle("Welcome!")
$stringToSend="test$='OK'"

$slen = stringlen($stringToSend) + 1
;MsgBox(0,"string len",$slen)

$str = "char[" & $slen & "]"
$struct= DllStructCreate($str);make a struct for the string so we can get a pointer to it
if @error Then
    MsgBox(0,"","Error in DllStructCreate " & @error);
    exit
endif

DllStructSetData($struct,1,$stringToSend)
$pointer = DllStructGetPtr($struct,1);the pointer to the string
if @error Then
    MsgBox(0,"","Error in DllStructGetPtr " & @error);
    exit
endif

MsgBox(0,"DllStruct","Struct Size: " & DllStructGetSize($struct) & @CRLF & _
        "Struct pointer: " & DllStructGetPtr($struct,1) & @CRLF & _
        "Data: " & DllStructGetData($struct,1) & @CRLF)

$result = DllCall("user32.dll","int","SendMessage","hWnd",$hwnd ,"int",$WM_RUNSCRIPTCODE,"int",$RUNSCRIPTCODE_LOUD,"ptr",$pointer)
;$result = _SendMessage($hwnd, $WM_RUNSCRIPTCODE, $RUNSCRIPTCODE_LOUD, $pointer)
If @error Then
        MsgBox(0,"_ToggleMonitor", "_SendMessage Error: " & @error)
        Exit
    EndIf

Thanks for help!

Share this post


Link to post
Share on other sites

#7 ·  Posted (edited)

Thanks for tip Zedna! I forgot to mention that I already tried both ways. Still the same. If I wouldn't know for sure that this mechanism works fine from C++ code, I would say there is something wrong in the host application. But it works in C++ based apps? <_<

Here is a part of code, which I know work OK...

case WM_RUNSCRIPTCODE:
        if(m_Process && !m_Ignore)
        {
            HWND hWnd = theApp.GetPlayerHWnd();
            if(hWnd)
            {
                CString str;
                GetWindowText(str);
                ::SendMessage(hWnd, WM_RUNSCRIPTCODE, RUNSCRIPTCODE_LOUD, LPARAM(LPCTSTR(str)));
                theApp.StoreCommand(str);
                SetCurSel(-1);
            }
        }

Instead of

$pointer = DllStructGetPtr($struct,1); get the pointer to the string

try

$pointer = DllStructGetPtr($struct); get the pointer to the string
Edited by odklizec

Share this post


Link to post
Share on other sites

Try this

DllCall("user32.dll","int","SendMessage","hWnd",$hwnd ,"int",$WM_RUNSCRIPTCODE,"int","1","str",$stringToSend)
Yes, this was my initial code, before I realized there must be a pointer to string in last parameter. However, I made a small progress but it's very odd <_<

If I use this code in AutoIt 3.2.0.0 it definitely sends something to the target application...

$result = DllCall("user32.dll","int","SendMessage","hWnd",$hwnd ,"int",$WM_RUNSCRIPTCODE,"int","1","str",$pointer)

It's wrong code because of str instead of ptr, but it sends something to the target application, which immeditaly invoke the "Unknown Function" error message. It means that the application receives the string from AutoIt. But when the target application trying to run the received string (via RUNSCRIPTCODE), it fails (from obvious reason).

Of course, I also tried to replace the $pointer with $stringToSend, but it does not pass the string to application. And replacing str with ptr and leaving the $pointer where it is now does not help either.

Now the strange part is, that the same (Unknown Function error) does not happen if the above "wrong" script is compiled with actual 3.2.8.1 or 3.2.9.4 beta! Any idea why is it like that? Why the wrong code partially works with 3.2.0.0 and does not help with the latest versions?

Share this post


Link to post
Share on other sites

#10 ·  Posted (edited)

Look at Auto3Library's TreeView_GetText()

There must be used alocating memory for string buffer in target application's memory area.

Maybe you must do your call this way too.

Func _TreeView_GetText($hWnd, $hNode)
  Local $iItem, $tItem, $pItem, $pBuffer, $tBuffer, $pMemory, $tMemMap, $pText

  $tBuffer = DllStructCreate("char Text[4096]")
  $pBuffer = DllStructGetPtr($tBuffer)
  $tItem   = DllStructCreate($tagTVITEMEX)
  $pItem   = DllStructGetPtr($tItem)
  DllStructSetData($tItem, "Mask", $TVIF_TEXT)
  DllStructSetData($tItem, "hItem", $hNode)
  DllStructSetData($tItem, "TextMax", 4096)
  if _Lib_InProcess($hWnd, $ghTVLastWnd) then
    DllStructSetData($tItem, "Text", $pBuffer)
    _API_SendMessage($hWnd, $TVM_GETITEM, 0, $pItem)
  else
    $iItem   = DllStructGetSize($tItem)
    $pMemory = _Mem_CtrlInit($hWnd, $iItem + 4096, $tMemMap)
    $pText   = $pMemory + $iItem
    DllStructSetData($tItem, "Text", $pText)
    _Mem_CtrlWrite($tMemMap, $pItem, $pMemory, $iItem)
    _API_SendMessage($hWnd, $TVM_GETITEM, 0, $pMemory)
    _Mem_CtrlRead($tMemMap, $pText, $pBuffer, 4096)
    _Mem_CtrlFree($tMemMap)
  endif
  Return DllStructGetData($tBuffer, "Text")
EndFunc
Edited by Zedna

Share this post


Link to post
Share on other sites

Thanks Zedna. That sound reasonable. I will try that, although I don't know anything about allocating memory in target application. But yesterday I knew nothing about structures. New day..new challenge <_<

If I understand the TreeView_GetText() code right, all I need is to use/change this code to write in target application memory?

$iItem   = DllStructGetSize($tItem)
    $pMemory = _Mem_CtrlInit($hWnd, $iItem + 4096, $tMemMap)
    $pText   = $pMemory + $iItem
    DllStructSetData($tItem, "Text", $pText)
    _Mem_CtrlWrite($tMemMap, $pItem, $pMemory, $iItem)
    _API_SendMessage($hWnd, $TVM_GETITEM, 0, $pMemory)
    _Mem_CtrlRead($tMemMap, $pText, $pBuffer, 4096)
    _Mem_CtrlFree($tMemMap)

Share this post


Link to post
Share on other sites

I'm happy to report that I got it working!!! Big thanks Zedna for your suggestions! The last one helped a lot and it now works as expected! Once again thank you and SmOke_N for your tips!

Share this post


Link to post
Share on other sites

I'm happy to report that I got it working!!! Big thanks Zedna for your suggestions! The last one helped a lot and it now works as expected! Once again thank you and SmOke_N for your tips!

OK.

So post your working code (or code snipet) so other AutoIters can learn something.

BTW: Your nick name sounds like Czech. Are you from Czech? I am.

Share this post


Link to post
Share on other sites

Oh, yes here is the code, which sends the string from AutoIt to target app..

#include <A3LMemory.au3>

Global Const $WM_RUNSCRIPTCODE = 0x08C7
Global Const $RUNSCRIPTCODE_LOUD = 1
Global Const $RUNSCRIPTCODE_QUIET = 0
Local $iItem, $pMemory, $tMemMap, $pText

$hwnd = WinGetHandle("Welcome!")
$stringToSend='Message("","OK")' 

$slen = stringlen($stringToSend) +1
$str = "char[" & $slen & "]"
$struct= DllStructCreate($str); make a struct for the string so we can get a pointer to it
if @error Then
    MsgBox(0,"","Error in DllStructCreate " & @error)
    exit
endif

; this is the critical part responsible for writing the internal pointer of string in structure to target application memory
$iItem   = DllStructGetSize($struct); get structure size
$pMemory = _Mem_CtrlInit($hwnd, $iItem, $tMemMap); init memory and return pointer to initialized memory in target app
DllStructSetData($struct, 1, $stringToSend); set string to structure
$pointer = DllStructGetPtr($struct,1); pointer to string in structure
_Mem_CtrlWrite($tMemMap, $pointer, $pMemory, $iItem); write string pointer to application memory
    
; SendMesage to target application (pointer in target application memory) 
$result = DllCall("user32.dll","int","SendMessage","hWnd",$hwnd ,"int",$WM_RUNSCRIPTCODE,"int",$RUNSCRIPTCODE_LOUD,"ptr",$pMemory)
If @error Then
        MsgBox(0,"_ToggleMonitor", "_SendMessage Error: " & @error)
        Exit
    EndIf

_Mem_CtrlFree($tMemMap);release the memory

In the end, it was easier than I thought. But after reading your last comment I was about giving it up <_<

Yes, I'm Czech too, but currently living in Slovakia.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0