fredag den 6. marts 2015

Sending a WM_COPYDATA from C++ to Python

The following code shows how to send a WM_DATACOPY message from C++ and how to recede it in Python.

The code is based on the code in these posts: http://stackoverflow.com/questions/2451103/use-wm-copydata-to-send-data-between-processes, and http://stackoverflow.com/questions/5249903/receiving-wm-copydata-in-python.

Sending in C++:

1
2
3
4
5
6
7
LPTSTR lpszString = L"Not ready";
COPYDATASTRUCT cds;
cds.dwData = 1;
size_t count_t = sizeof(TCHAR) * (_tcslen(lpszString) + 1);
cds.cbData = static_cast<DWORD>(count_t);
cds.lpData = lpszString;
SendMessage(hwnd, WM_COPYDATA, (WPARAM)hwnd, (LPARAM)(LPVOID)&cds);


Recieving in Python:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import win32api
import win32process

import struct
import win32con
import win32gui
import array
import win32ui
from ctypes import *


class ACOPYDATASTRUCT(Structure):
    _fields_ = [
        ('dwData', c_ulong),
        ('cbData', c_ulong),
        ('lpData', c_void_p)
    ]
PCOPYDATASTRUCT = POINTER(ACOPYDATASTRUCT)


class Listener:

    def __init__(self):
        message_map = {
            win32con.WM_COPYDATA: self.OnCopyData
        }
        wc = win32gui.WNDCLASS()
        wc.lpfnWndProc = message_map
        wc.lpszClassName = 'MyWindowClass'
        hinst = wc.hInstance = win32api.GetModuleHandle(None)
        classAtom = win32gui.RegisterClass(wc)
        self.hwnd = win32gui.CreateWindow (
            classAtom,
            "win32gui test",
            0,
            0, 
            0,
            win32con.CW_USEDEFAULT, 
            win32con.CW_USEDEFAULT,
            0, 
            0,
            hinst, 
            None
        )
        print self.hwnd

    def OnCopyData(self, hwnd, msg, wparam, lparam):
        print hwnd
        print msg
        print wparam
        print lparam
        pCDS = cast(lparam, PCOPYDATASTRUCT)
        print pCDS.contents.dwData
        print pCDS.contents.cbData
        status = wstring_at(pCDS.contents.lpData)
        print status
        win32gui.PostQuitMessage(0)
        return 1

l = Listener()

win32gui.PumpMessages()


As an additional bonus one can send the hwnd of the receiving python program to the C++ program using a WM_COPYDATA message:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class COPYDATATYPE(Structure):
    _fields_ = [("cmd",   c_ulong),
                ("hwnd",  c_ulong)]

class COPYDATASTRUCT(Structure):
    _fields_ = [("dwData", c_ulong),
                ("cbData", c_ulong),
                ("lpData", POINTER(COPYDATATYPE))]

cpyData = COPYDATATYPE(1, l.hwnd)
cds = COPYDATASTRUCT(c_ulong(8888),
                     c_ulong(sizeof(cpyData)),
                     pointer(cpyData))

# try to send a message
win32api.SendMessage(target_hwnd,
                     win32con.WM_COPYDATA,
                     0,
                     addressof(cds))


See also my previous post here: http://blog.fagidiot.dk/2015/03/send-wmcopydata-from-python-partial.html

torsdag den 5. marts 2015

Formatting code on this blog

I use http://hilite.me if you have alternative suggestions then don't hesitate to leave a comment.

Send WM_COPYDATA from Python - Partial Solution

I wanted to send a WM_COPYDATA message from a Python application to a Windows application.

Here is the code both Python and C++ which I got work with the only caveat that the string in szData is not received correctly. This example is a very tiny modification of the code posted here: https://groups.google.com/forum/#!topic/comp.lang.python/z3QS7Ic80mU


Python code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import struct
import win32con
import win32gui
import array

class COPYDATATYPE(Structure):
    _fields_ = [("nNum",   c_ulong),
                ("szData", c_char_p)]

class COPYDATASTRUCT(Structure):
    _fields_ = [("dwData", c_ulong),
                ("cbData", c_ulong),
                ("lpData", POINTER(COPYDATATYPE))]

cpyData = COPYDATATYPE(17, '123456789')
cds = COPYDATASTRUCT(c_ulong(1),
                     c_ulong(sizeof(cpyData)),
                     pointer(cpyData))

# try to send a message
win32api.SendMessage(hwnd,
                     win32con.WM_COPYDATA,
                     0,
                     addressof(cds))

C++ code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
 case WM_COPYDATA:
    pCDS = (COPYDATASTRUCT*)lParam;

    if (pCDS->dwData == 1)
    {
      struct COPYDATATYPE
      {
        long nNum;
        char * szData;
      };

      int count = pCDS->cbData;
      PVOID data = pCDS->lpData;
      if (data != NULL)
      {
        COPYDATATYPE *cp = (COPYDATATYPE *)data;
        int i = cp->nNum;
        // The following line does not work.
        const char* message = cp->szData;
      }
    }
    break;

I also found use in reading this: http://www.qsl.net/dl4yhf/yhf_comm/yhf_comm_info.htm#find_window_handle

Get the list of hwnds for a given pid

This Python code shows how to get the list of hwnds from a given pid on Windows.

We use win32gui to enumerate all windows, get their pid to check and then append the hwnd to the list.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def get_hwnds_for_pid (pid):
  def callback (hwnd, hwnds):
    _, found_pid = win32process.GetWindowThreadProcessId (hwnd)
    if found_pid == pid:
        hwnds.append (hwnd)
    return True
    
  hwnds = []
  win32gui.EnumWindows (callback, hwnds)
  return hwnds

hwnds = get_hwnds_for_pid(3196)
print hwnds