Python Memorybuffer pywin32 - python

I got some code:
def get_text(self, id):
edit_hwnd = win32gui.GetDlgItem(self.hwnd, id) # 获取窗口句柄
time.sleep(0.2)
self.edit_hwnd = edit_hwnd
length = win32api.SendMessage(
edit_hwnd, win32con.WM_GETTEXTLENGTH) + 1 # 获取窗体内容长度
buf = win32gui.PyMakeBuffer(length) # 准备buffer对象作为容器
win32gui.SendMessage(edit_hwnd, win32con.WM_GETTEXT,
length, buf) # 获取窗体内容放入容器
try:
address, length = win32gui.PyGetBufferAddressAndLen(buf) # 获取容器的内存地址
except ValueError:
print('error')
return
text = win32gui.PyGetString(address, length) # 取得字符串
buf.release()
del buf
return text
This function for get string at windows.I need to while this func to always get this value.When the value changed,i do something.But now when i done this while,my program exit with error code C000005.How can i fix it.
buf.release()
del buf
It's i added when i found this problem.It look like does't work.

The messages WM_GETTEXTLENGTH returns the length of the text in characters (excluding the terminating null character) and the maximum buffer length given to WM_GETTEXT also is based on characters (including the terminating null character).
A character in the NT-based Windows systems is encoded in a double-byte character set (DBCS), meaning two bytes per character.
The function win32gui.PyMakeBuffer(length) returns a buffer of length bytes.
So if length is the return value of WM_GETTEXTLENGTH, the reserved buffer should be length * 2 + 2 bytes long and the maximum buffer length given to WM_GETTEXT should be length + 1.

Related

Exclude escaped byte char from serial.read_until()

I'm writing code to communicate back and forth with a module over serial which returns specific byte values to indicate the start/end of its communication. The length of the data returned can vary as can all content between the start header and end footer.
In an ideal scenario, I'd be able to use the following code to receive all data from the module:
start = b'\x5a'
end = b'\x5b'
max_size = 1024
def get_from_serial(ser: serial.Serial) -> bytes:
with ser:
_ = ser.read_until(expected=start, size=max_size)
data = ser.read_until(expected=end, size=max_size)
return start + data
Unfortunately, there are circumstances where the data sent by the module includes bytes that match either the start or end byte values. In these instances, the module prepends an escape character to them:
valid_start = b'\x5a'
valid_end = b'\x5b'
escaped_start = b'\x5c\x5a'
escaped_end = b'\x5c\x5b'
A valid start/end byte can be preceded by ANY byte value other than an escape one:
good_result = b'\x5a\xff\x5c\x5b\xff\x5b'
bad_result = b'\x5a\xff\x5c\x5b' # missed b'\xff\x5b'
Is there a way to configure ser.read_until() to ignore any escaped instance of a start/end byte and only return when encountering a valid start/end byte?
There's probably a way to do this with a loop that checks if data[-2] == b'\x5c': each time ser.read_until() returns something though I feel it could get complicated if the module returns multiple instances of an escaped start/end byte scattered throughout the data.
Any thoughts or suggestions would be greatly appreciated.
Edit:
Starting to think this isn't actually possible to do from inside ser.read_until() so have added a check before returning the data.
start = b'\x5a'
end = b'\x5b'
escape = b'\x5c'
max_size = 1024
def get_from_serial(ser: serial.Serial) -> bytes:
with ser:
_ = ser.read_until(expected=start, size=max_size)
data = ser.read_until(expected=end, size=max_size)
if valid_packet(data):
return start + data
else:
raise Exception("Invalid packet")
def valid_packet(packet: bytearray) -> bool:
header = packet[:1]
footer = packet[-1:]
escape_check = packet[-2:-1]
valid_header = header == start
valid_footer = footer == end
not_escaped = escape_check != escape
return all([
valid_header,
valid_footer,
not_escaped
])

Problem with XOR : "ord() expected a character, but string of length 2 found"

This code reads bytes from two binary files and then does a byte comparison using XOR. Basically reads an odd byte from key file and then shifts that amount in text.bin file.
Even bytes in key file are bytes which must be compared with the byte in text.bin file.
Technically the way it's written it should compare the two read bytes from both bin files, but I am getting an error
output = (ord(mask)^ord(a)) # XOR
TypeError: ord() expected a character, but string of length 2 found
.
k = open ("key.bin", "rb")
t = open ("text.bin", "rb")
k.seek(0); t.seek(0); position = 0
while 1:
offset = k.read(1)
mask = k.read(2)
print(str(mask))
if not offset:
break
if not mask:
break
shift=int(ord(offset))
print(shift)
position = position + shift
t.seek(position)
a = t.read(1)
output = (ord(mask)^ord(a))
print (chr(output), end="")
k.close() ; d.close()
It happens only when mask = k.read(2) is reading from the 2nd byte.
'String of length 2' that could be some wrong hex string read instead of bytes?
It is causing that error because k.read(2) has a length of 2 and ord requires a length of 1.
Try this:
k = open ("key.bin", "rb")
t = open ("text.bin", "rb")
k.seek(0); t.seek(0); position = 0
while 1:
offset = k.read(1)
mask = bytes(chr(k.read(2)[1]),'utf8')
print(str(mask))
if not offset:
break
if not mask:
break
shift=int(ord(offset))
print(shift)
position = position + shift
t.seek(position)
a = t.read(1)
output = (ord(mask)^ord(a))
print (chr(output), end="")
k.close() ; d.close()

How to read UTF16-BE encoded bytes with length header

I want to decode a series of strings of variable length which have been encoded in UTF16-BE preceded by a two-bytes long big-endian integer indicating the half the byte-length of the following string. e.g:
Length String (encoded) Length String (encoded) ...
\x00\x05 \x00H\x00e\x00l\x00l\x00o \x00\x06 \x00W\x00o\x00r\x00l\x00d\x00! ...
All these strings and their length headers are concatenated in one big bytestring.
I have the encoded bytestring as bytes object in memory. I would like to have an iterable function which would yield strings until it reaches the end of the ByteString.
Not a huge improvement, but your code can be streamlined a bit.
def decode_strings(byte_string: ByteString) -> Generator[str]:
with io.BytesIO(byte_string) as stream:
while (s := stream.read(2)):
length = int.from_bytes(s, byteorder="big")
yield bytes.decode(stream.read(length), encoding="utf_16_be")
Currently I do it like this, but somehow I'm imagining Raymond Hettinger's "There must be a better way!".
import io
import functools
from typing import ByteString
from typing import Iterable
# Decoders
int_BE = functools.partial(int.from_bytes, byteorder="big")
utf16_BE = functools.partial(bytes.decode, encoding="utf_16_be")
encoded_strings = b"\x00\x05\x00H\x00e\x00l\x00l\x00o\x00\x06\x00W\x00o\x00r\x00l\x00d\x00!"
header_length = 2
def decode_strings(byte_string: ByteString) -> Iterable[str]:
stream = io.BytesIO(byte_string)
while True:
length = int_BE(stream.read(header_length))
if length:
text = utf16_BE(stream.read(length * 2))
yield text
else:
break
stream.close()
if __name__ == "__main__":
for text in decode_strings(encoded_strings):
print(text)
Thanks for any suggestions.

Python String Prefix by 4 Byte Length

I'm trying to write a server in Python to communicate with a pre-existing client whose message packets are ASCII strings, but prepended by four-byte unsigned integer values representative of the length of the remaining string.
I've done a receiver, but I'm sure there's a a more pythonic way. Until I find it, I haven't done the sender. I can easily calculate the message length, convert it to bytes and transmit the message.The bit I'm struggling with is creating an integer which is an array of four bytes.
Let me clarify: If my string is 260 characters in length, I wish to prepend a big-endian four byte integer representation of 260. So, I don't want the ASCII string "0260" in front of the string, rather, I want four (non-ASCII) bytes representative of 0x00000104.
My code to receive the length prepended string from the client looks like this:
sizeBytes = 4 # size of the integer representing the string length
# receive big-endian 4 byte integer from client
data = conn.recv(sizeBytes)
if not data:
break
dLen = 0
for i in range(sizeBytes):
dLen = dLen + pow(2,i) * data[sizeBytes-i-1]
data = str(conn.recv(dLen),'UTF-8')
I could simply do the reverse. I'm new to Python and feel that what I've done is probably longhand!!
1) Is there a better way of receiving and decoding the length?
2) What's the "sister" method to encode the length for transmission?
Thanks.
The struct module is helpful here
for writing:
import struct
msg = 'some message containing 260 ascii characters'
length = len(msg)
encoded_length = struct.pack('>I', length)
encoded_length will be a string of 4 bytes with value '\x00\x00\x01\x04'
for reading:
length = struct.unpack('>I', received_msg[:4])[0]
An example using asyncio:
import asyncio
import struct
def send_message(writer, message):
data = message.encode()
size = struct.pack('>L', len(data))
writer.write(size + data)
async def receive_message(reader):
data = await reader.readexactly(4)
size = struct.unpack('>L', data)[0]
data = await reader.readexactly(size)
return data.decode()
The complete code is here

Read string up to a certain size in Python

I have a string stored in a variable. Is there a way to read a string up to a certain size e.g. File objects have f.read(size) which can read up to a certain size?
Check out this post for finding object sizes in python.
If you are wanting to read the string from the start until a certain size MAX is reached, then return that new (possibly shorter string) you might want to try something like this:
import sys
MAX = 176 #bytes
totalSize = 0
newString = ""
s = "MyStringLength"
for c in s:
totalSize = totalSize + sys.getsizeof(c)
if totalSize <= MAX:
newString = newString + str(c)
elif totalSize > MAX:
#string that is slightly larger or the same size as MAX
print newString
break
This prints 'MyString' which is less than (or equal to) 176 Bytes.
Hope this helps.
message = 'a long string which contains a lot of valuable information.'
bite = 10
while message:
# bite off a chunk of the string
chunk = message[:bite]
# set message to be the remaining portion
message = message[bite:]
do_something_with(chunk)

Categories

Resources