So close: Correctly retrieving string from WM_COPYDATA

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
Verdlin
Posts: 63
Joined: 04 Oct 2013, 08:55
Contact:

So close: Correctly retrieving string from WM_COPYDATA

19 Oct 2013, 08:10

Edit: The program is posted in a latter post.

I have a small Unicode, C++ program I've compiled. It is designed to send strings to an AHK script. If I need to post it, let me know, and I will.

My problem is, when I try to retrieve the string out of the COPYDATA struct, I get a junk string. Example output:
Image

Would someone be so kind as to assist? Thanks to those already who have helped me, thus far.

Code: Select all

OnMessage(WM_COPYDATA:=74, "OnWMCopyData")
Run, C:\Test.exe %A_ScriptHwnd%
return

OnWMCopyData(wParam, lParam, msg, hWnd)
{
	VarSetCapacity(sData, 255)
	sData := NumGet(lParam + 2 *A_PtrSize)
	Msgbox % StrGet(sData) "`n" sData
	return
}
Last edited by Verdlin on 26 Oct 2013, 11:18, edited 1 time in total.
Zelio
Posts: 278
Joined: 30 Sep 2013, 00:45
Location: France

Re: Retrieving non-junk string from WM_COPYDATA

19 Oct 2013, 08:30

Did you try StrGet(sdata, "cp0") for ansi or StrGet(sdata, "cp1200") for unicode ? The problem is to know what your C++ apps do exactly... (in general way, chinese char is a string format problem)
Also Init your buffer with zero for automaticly finish the string, VarSetCapacity(sData, 255, 0) else you will need a lenght parameter for StrGet().
But it seems, it is not an obligation, they are an example in the manual, at the end of OnMessage() page :

Code: Select all

Receive_WM_COPYDATA(wParam, lParam)
{
    StringAddress := NumGet(lParam + 2*A_PtrSize)  ; Retrieves the CopyDataStruct's lpData member.
    CopyOfData := StrGet(StringAddress)  ; Copy the string out of the structure.
    ; Show it with ToolTip vs. MsgBox so we can return in a timely fashion:
    ToolTip %A_ScriptName%`nReceived the following string:`n%CopyOfData%
    return true  ; Returning 1 (true) is the traditional way to acknowledge this message.
}
I looked at your other thread for your C++ code, I am not an expert but depending on the project settings, LPCTSTR would be mapped to either LPCSTR (ANSI) or LPCWSTR (Unicode), sizeof(TCHAR) too. Also will depends if you use autohotkeyA32.exe or autohotkeyU32.exe (default is unicode, the common autohotkey.exe, AHK 1.1 32bit).
Don't ask me how, but I am curious to know how change those settings...
Verdlin
Posts: 63
Joined: 04 Oct 2013, 08:55
Contact:

Re: Retrieving non-junk string from WM_COPYDATA

20 Oct 2013, 14:19

Zelio wrote:Did you try StrGet(sdata, "cp0") for ansi or StrGet(sdata, "cp1200") for unicode ? The problem is to know what your C++ apps do exactly... (in general way, chinese char is a string format problem)
Also Init your buffer with zero for automaticly finish the string, VarSetCapacity(sData, 255, 0) else you will need a lenght parameter for StrGet().
But it seems, it is not an obligation, they are an example in the manual, at the end of OnMessage() page :

Code: Select all

Receive_WM_COPYDATA(wParam, lParam)
{
    StringAddress := NumGet(lParam + 2*A_PtrSize)  ; Retrieves the CopyDataStruct's lpData member.
    CopyOfData := StrGet(StringAddress)  ; Copy the string out of the structure.
    ; Show it with ToolTip vs. MsgBox so we can return in a timely fashion:
    ToolTip %A_ScriptName%`nReceived the following string:`n%CopyOfData%
    return true  ; Returning 1 (true) is the traditional way to acknowledge this message.
}
I looked at your other thread for your C++ code, I am not an expert but depending on the project settings, LPCTSTR would be mapped to either LPCSTR (ANSI) or LPCWSTR (Unicode), sizeof(TCHAR) too. Also will depends if you use autohotkeyA32.exe or autohotkeyU32.exe (default is unicode, the common autohotkey.exe, AHK 1.1 32bit).
Don't ask me how, but I am curious to know how change those settings...
Thanks a lot for the help, Zelio! I have tried StrGet with, no parms, cp0, and cp1200. I also tried switching the VS project to use multibyte character set. In every case, I'm still getting a Chinese string. I've also tested on both AHK Unicode and AHK ANSI. Although the characters are different between the two builts, neither of them are the right string.

I read about Unicde vs Ansi, and I think you are right about the mapping. I am also curious how to change those settings, but I don't know how.

I think you are right about the key being in the VS project. Beyond switching from Unicode character set to Multibyte, I am at a loss for what to do. Any other thoughts?
Zelio
Posts: 278
Joined: 30 Sep 2013, 00:45
Location: France

Re: Retrieving non-junk string from WM_COPYDATA

20 Oct 2013, 18:01

If StrGet() with cp0 or cp1200 don't work then I don't know, maybe reread http://msdn.microsoft.com/en-us/library ... 49011.aspx and http://msdn.microsoft.com/en-us/library ... 49010.aspx to be sure you create use and get WM_COPYDATA without mistakes.
Perhaps post your current c++ code with the current ahk script too, an other member could help you (out of my knowledge)...
Verdlin
Posts: 63
Joined: 04 Oct 2013, 08:55
Contact:

Re: Retrieving non-junk string from WM_COPYDATA

21 Oct 2013, 18:09

Okay, after a bit of work, I discovered quite a few errors in my console program. AFAICT, I have a working Unicode project...except that it does not work. I also cannot find in error on my AHK script. The problem still seems likely to be a project setting...maybe not?

At first, I did my best to match the project settings for AHK. That did not make a difference. Next, I looked at how AHK handled the Post/SendMessage commands; they use _T macro for string, so now I am using that, too.

This annoyance is just a small bottleneck in a rather fun project I am working on. *Any* help at all is greatly appreciated because it will save me enormous amounts of time with researching this issue.

VS 2010 Project can be downloaded here: https://www.dropbox.com/s/pgj2jg2vngzr2 ... essage.zip

Code: Select all

OnMessage(WM_COPYDATA:=74, "OnWMCopyData")
Run, C:\Users\%A_UserName%\Documents\Visual Studio 2010\Projects\SendMessage\Debug\SendMessage.exe %A_ScriptHwnd%
return

OnWMCopyData(wParam, lParam)
{
	StringAddress := NumGet(lParam * A_PtrSize)  ; Retrieves the CopyDataStruct's lpData member.

	CopyOfData := StrGet(StringAddress)
	CopyOfData2 := StrGet(StringAddress, "cp0")
	CopyOfData3 := StrGet(StringAddress, "cp1200")
	CopyOfData4 := StrGet(StringAddress, "UTF-8")
	CopyOfData5 := StrGet(StringAddress, "UTF-16")
	Msgbox %CopyOfData%`n%CopyOfData2%`n%CopyOfData3%`n%CopyOfData4%`n%CopyOfData5%
	return true
}

Code: Select all

// SendMessage.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <iostream>
#include "Windows.h"

using namespace std;

int _httoi(const TCHAR *value)
{
  struct CHexMap
  {
    TCHAR chr;
    int value;
  };
  const int HexMapL = 16;
  CHexMap HexMap[HexMapL] =
  {
    {'0', 0}, {'1', 1},
    {'2', 2}, {'3', 3},
    {'4', 4}, {'5', 5},
    {'6', 6}, {'7', 7},
    {'8', 8}, {'9', 9},
    {'A', 10}, {'B', 11},
    {'C', 12}, {'D', 13},
    {'E', 14}, {'F', 15}
  };
  TCHAR *mstr = _tcsupr(_tcsdup(value));
  TCHAR *s = mstr;
  int result = 0;
  if (*s == '0' && *(s + 1) == 'X') s += 2;
  bool firsttime = true;
  while (*s != '\0')
  {
    bool found = false;
    for (int i = 0; i < HexMapL; i++)
    {
      if (*s == HexMap[i].chr)
      {
        if (!firsttime) result <<= 4;
        result |= HexMap[i].value;
        found = true;
        break;
      }
    }
    if (!found) break;
    s++;
    firsttime = false;
  }
  free(mstr);
  return result;
}

int _tmain(int argc, _TCHAR* argv[])
{
  if (argv[1])
  {
	  int iHwnd = _httoi(argv[1]);
	  HWND hWnd = (HWND)iHwnd;

	  cout << "char:" << argv[1] << "|int:" << iHwnd << "|hWnd:" << hWnd << endl;

	  LPCTSTR lpszString = (LPCTSTR)_T("Hello AHK");
	
	  COPYDATASTRUCT cds;
	  cds.dwData = 1; // arbitrary
	  cds.cbData = sizeof(TCHAR) * (_tcslen(lpszString) + 1);
	  cds.lpData = &lpszString;
	
	  cout << SendMessage(hWnd, WM_COPYDATA, (WPARAM) hWnd, (LPARAM) (LPVOID)&cds) << endl;
	  cout << GetLastError() << endl;
	  // Must be syncronous to use PostMessage
	  //cout << PostMessage(hWnd, WM_COPYDATA, (WPARAM) hWnd, (LPARAM) (LPVOID)&cds) << endl;
	  //cout << GetLastError() << endl;
  }

  cout << "Press Enter to quit..." << endl;
  cin.get();

  return 0;
}
Last edited by Verdlin on 26 Oct 2013, 11:15, edited 1 time in total.
User avatar
MilesAhead
Posts: 232
Joined: 03 Oct 2013, 09:44

Re: So close: Correctly retrieving string from WM_COPYDATA

26 Oct 2013, 13:02

It may not be your code that is broken. There's lots of legacy code in Windows that only works with ANSI. I had a similar issue with WM_COPYDATA and a message-only window. Unfortunately I no longer have the C++ source. But I'm sure the "fix" was to treat the string passed as ANSI in all respects by both processes. Some of the innards will just never be fixed. It would break too much OS code.
"My plan is to ghostwrite my biography. Then hire another writer to put his
name on it and take the blame."

- MilesAhead
Verdlin
Posts: 63
Joined: 04 Oct 2013, 08:55
Contact:

Re: So close: Correctly retrieving string from WM_COPYDATA

05 Jan 2014, 16:25

*Sigh* Thanks, all. Sometimes the solution is starting you right in the face.

As it turns out, I was translating the string incorrectly.

I was using this:
StringAddress := NumGet(lParam * A_PtrSize)
Instead of this:
StringAddress := NumGet(lParam + 2 * A_PtrSize)

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Chunjee, Hansielein, Lpanatt and 332 guests