I am trying to read from Python the WM_COPYDATA
message some applications (I'm trying with Spotify) send to WindowsLiveMessenger to update the "What I'm listening to..." phrase.
From what I have been able to find, WM_COPYDATA
messages come in a COPYDATASTRUCT
with the following structure:
dwData
in our case 0x547 so that it access the listening now feature cbData
with the length of the string received lpData
with a pointer to the string itself, may include Unicode charactersThe string should have the following format: \0Music\0status\0format\0song\0artist\0album\0
as stated by ListeningNowTracker
What we receive in a WM_COPYDATA
event is a pointer for lParam
that contains the COPYDATASTRUCT
.
I started tinkering with pywin32 functions and I remembered that they do not accept Unicode characters from past experience, then I switched to ctypes. Despite this being an almost new world in Python for me, I tried with POINTER()
and all I got was unknown objects for me or access violations.
I think that the code should create a COPYDATASTRUCT
:
class CopyDataStruct(Structure):
_fields_ = [('dwData', c_int),
('cbData', c_int),
('lpData', c_void_p)]
Then make the lParam
be a pointer to that structure, get the string pointer from lpData
and finally get the string with ctypes.string_at(lpData,cbData)
.
Any tips?
UPDATE 1
The WM_COPYDATA
event is received by a hidden window built with win32gui
just for this purpose. The copydata event is connected to a function called OnCopyData
and this is its header:def OnCopyData(self, hwnd, msg, wparam, lparam):
The values the function delivers are correct as compared with the ones from the Spy++ messages log.
UPDATE 2
This should be close to what I want, but gives a NULL pointer error.
class CopyDataStruct(ctypes.Structure):
_fields_ = [('dwData', c_int),
('cbData', c_int),
('lpData', c_wchar_p)]
PCOPYDATASTRUCT = ctypes.POINTER(CopyDataStruct)
pCDS = ctypes.cast(lparam, PCOPYDATASTRUCT)
print ctypes.wstring_at(pCDS.contents.lpData)
I wrote the following trivial win32gui app:
import win32con, win32api, win32gui, ctypes, ctypes.wintypes
class COPYDATASTRUCT(ctypes.Structure):
_fields_ = [
('dwData', ctypes.wintypes.LPARAM),
('cbData', ctypes.wintypes.DWORD),
('lpData', ctypes.c_void_p)
]
PCOPYDATASTRUCT = ctypes.POINTER(COPYDATASTRUCT)
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 = ctypes.cast(lparam, PCOPYDATASTRUCT)
print pCDS.contents.dwData
print pCDS.contents.cbData
print ctypes.wstring_at(pCDS.contents.lpData)
return 1
l = Listener()
win32gui.PumpMessages()
I then sent the window a WM_COPYDATA
message from another app (written in Delphi):
Text := 'greetings!';
CopyData.cbData := (Length(Text)+1)*StringElementSize(Text);
CopyData.lpData := PWideChar(Text);
SendMessage(hwnd, WM_COPYDATA, Handle, NativeInt(@CopyData));
The output was:
461584
461584
74
658190
2620592
42
22
greetings!
So it seems that it works trivially, pretty much as you coded it.
The only thing that I can think of is that the text in Spotify's COPYDATASTRUCT
is not null-terminated. You should be able to check that quite easily by reading out the data. Make use of the cbData
member.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With