I'm trying to write a python script that will interface with my copy of stickies. I'm having trouble with how Python interacts with the WM_COPYDATA struct, and unfortunately I haven't been able to find many examples online.
Using the code:
import struct
import win32con
import win32gui
import struct, array
int_buffer = array.array("L", [0])
char_buffer = array.array('b', 'do new sticky')
int_buffer_address = int_buffer.buffer_info()[0]
char_buffer_address, char_buffer_size = char_buffer.buffer_info
copy_struct = struct.pack("pLp",
int_buffer_address,
char_buffer_size, char_buffer_address)
hwnd = win32gui.FindWindow("ZhornSoftwareStickiesMain", None)
win32gui.SendMessage(w, WM_COPYDATA, hwnd, copy_struct)
I get the following error:
C:\Users\%userprofile%\Desktop>python sender.py
Traceback (most recent call last):
File "sender.py", line 7, in <module>
char_buffer = array.array('b', 'do new sticky')
TypeError: an integer is required
I can't seem to figure out why I'm getting such an error. Any ideas?
Edit: Some partially working code
import struct
import win32con
import win32gui
import struct, array
int_buffer = array.array("L", [0])
char_buffer = array.array('b', b"do manage open")
int_buffer_address = int_buffer.buffer_info()[0]
# Add () to buffer_info to call it.
char_buffer_address, char_buffer_size = char_buffer.buffer_info()
# Need P type for the addresses.
copy_struct = struct.pack("PLP",int_buffer_address,char_buffer_size, char_buffer_address)
hwnd = win32gui.FindWindow(None, "ZhornSoftwareStickiesMain")
win32gui.SendMessage(hwnd, win32con.WM_COPYDATA, None, copy_struct)
In Python 3, strings are Unicode. Try using a byte string:
>>> import array
>>> array.array('b',b'do new sticky')
array('b', [100, 111, 32, 110, 101, 119, 32, 115, 116, 105, 99, 107, 121])
There are a few other changes as well:
import struct, array
int_buffer = array.array("L", [0])
char_buffer = array.array('b', b'do new sticky')
int_buffer_address = int_buffer.buffer_info()[0]
# Add () to buffer_info to call it.
char_buffer_address, char_buffer_size = char_buffer.buffer_info()
# Need P type for the addresses.
copy_struct = struct.pack("PLP",int_buffer_address,char_buffer_size, char_buffer_address)
I didn't install the software, but I think the next lines should be:
hwnd = win32gui.FindWindow(None, "ZhornSoftwareStickiesMain")
win32gui.SendMessage(hwnd, win32con.WM_COPYDATA, None, copy_struct)
I installed the software. Initially I couldn't get any commands to work, but the trick that wasn't clear from the API documentation is that each string must start with api
:
import struct
import win32con
import win32gui
import array
char_buffer = array.array('B', b'api do new sticky hello, world')
char_buffer_address, char_buffer_size = char_buffer.buffer_info()
copy_struct = struct.pack("PLP", 12345, char_buffer_size, char_buffer_address)
hwnd = win32gui.FindWindow(None, "ZhornSoftwareStickiesMain")
win32gui.SendMessage(hwnd, win32con.WM_COPYDATA, None, copy_struct)
This is much easier to do with ctypes. This code has been tested with a simple Delphi application that receives and displays the string that is sent via WM_COPYDATA
.
import win32con
import ctypes
import ctypes.wintypes
FindWindow = ctypes.windll.user32.FindWindowW
SendMessage = ctypes.windll.user32.SendMessageW
class COPYDATASTRUCT(ctypes.Structure):
_fields_ = [
('dwData', ctypes.wintypes.LPARAM),
('cbData', ctypes.wintypes.DWORD),
('lpData', ctypes.c_wchar_p)
#formally lpData is c_void_p, but we do it this way for convenience
]
hwnd = FindWindow('TheNameOfMyWindowClass', None)
cds = COPYDATASTRUCT()
cds.dwData = 0
str = 'boo'
cds.cbData = ctypes.sizeof(ctypes.create_unicode_buffer(str))
cds.lpData = ctypes.c_wchar_p(str)
SendMessage(hwnd, win32con.WM_COPYDATA, 0, ctypes.byref(cds))
This assumes that the recipient expects a UTF-16 encoded payload. If the recipient is expecting ANSI encoded payload then you need to use this variant.
import win32con
import ctypes
import ctypes.wintypes
FindWindow = ctypes.windll.user32.FindWindowW
SendMessage = ctypes.windll.user32.SendMessageW
class COPYDATASTRUCT(ctypes.Structure):
_fields_ = [
('dwData', ctypes.wintypes.LPARAM),
('cbData', ctypes.wintypes.DWORD),
('lpData', ctypes.c_char_p)
#formally lpData is c_void_p, but we do it this way for convenience
]
hwnd = FindWindow('TheNameOfMyWindowClass', None)
cds = COPYDATASTRUCT()
cds.dwData = 0
str = b'boo'
cds.cbData = ctypes.sizeof(ctypes.create_string_buffer(str))
cds.lpData = ctypes.c_char_p(str)
SendMessage(hwnd, win32con.WM_COPYDATA, 0, ctypes.byref(cds))
Now, looking at the Stickies link that you posted, you need to use the ANSI variant. If you want to receive notification, you'll need to pass a window handle in you SendMessage call instead of 0 as above.
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