使用ctypes调用第三方库ReadFromBlob函数后缓冲区为空问题求助
ReadFromBlob via ctypes Let's break down what's going wrong in your code and how to fix it. The core issue is misunderstanding how the output parameters (buffer and size) work in the C function—these are meant to be populated by the function itself, not pre-initialized with empty data.
Key Problems in Your Original Code
- You initialized
bas an emptybytesobject and tried to pass its pointer to the function. But the C function expects a pointer to a pointer (const void **) where it can write the address of the allocated/found buffer. Your empty buffer doesn't give the function anywhere to store the actual data address. - You didn't explicitly define the function's argument types for ctypes, which can lead to silent type mismatches (ctypes uses default types that might not match the C function's signature).
Step-by-Step Fix
1. Explicitly Define the Function Signature
First, tell ctypes exactly what the ReadFromBlob function expects. This prevents type errors and makes the code safer:
from ctypes import * # Load the library lib = cdll.LoadLibrary(path_to_lib) # Define the function's argument types and return type # Assuming the function returns an int (0 for success, non-zero for error; adjust if docs say otherwise) lib.ReadFromBlob.argtypes = [ c_void_p, # PVOID blob c_int, # INT blob_size c_char_p, # PCSTR section (C string) c_char_p, # PCSTR key (C string) POINTER(c_void_p), # const void **buffer (pointer to void pointer) POINTER(c_uint) # UINT *size (pointer to unsigned int) ] lib.ReadFromBlob.restype = c_int
2. Correctly Handle Input and Output Parameters
When reading the blob data, you don't need to cast it to a pointer-to-array—c_char_p(data) works fine as a void* for the blob argument. For the output parameters:
- Initialize a
c_void_p()variable to hold the address of the buffer the function will return. - Initialize a
c_uint(0)variable to hold the size of that buffer. - Pass pointers to both variables so the function can write to them.
# Read the blob file with open(filepath, 'rb') as file: data = file.read() # Prepare input parameters blob = c_char_p(data) blob_size = c_int(len(data)) # Prepare output parameters (will be populated by the function) output_buffer = c_void_p() output_size = c_uint(0) # Call the function result = lib.ReadFromBlob( blob, blob_size, b"MainImage", # Use bytes for C strings b"Image", byref(output_buffer), # Pointer to the void pointer byref(output_size) # Pointer to the size variable ) # Check if the call succeeded (adjust error check based on library docs) if result != 0: raise RuntimeError(f"ReadFromBlob failed with error code {result}")
3. Extract the Data from the Returned Buffer
Now that the function has populated output_buffer and output_size, you can convert the buffer to a Python bytes object:
# Convert the C buffer to Python bytes if output_buffer.value and output_size.value > 0: # Create a ctypes array from the buffer address buffer_array = cast(output_buffer, POINTER(c_char * output_size.value)) extracted_data = buffer_array.contents.raw else: extracted_data = b"" print(f"Extracted data size: {len(extracted_data)} bytes")
4. Important: Clean Up Allocated Memory
Critical Note: If the ReadFromBlob function allocates memory for the buffer (most C functions do unless stated otherwise), you need to find and call the corresponding cleanup function from the library (e.g., FreeBlobBuffer or similar). Failing to do this will cause memory leaks. Check the library's documentation for the correct way to free the buffer.
Full Corrected Code
from ctypes import * def read_from_blob(path_to_lib, filepath): # Load and configure the library lib = cdll.LoadLibrary(path_to_lib) lib.ReadFromBlob.argtypes = [c_void_p, c_int, c_char_p, c_char_p, POINTER(c_void_p), POINTER(c_uint)] lib.ReadFromBlob.restype = c_int # Read blob data with open(filepath, 'rb') as file: data = file.read() # Prepare parameters blob = c_char_p(data) blob_size = c_int(len(data)) output_buffer = c_void_p() output_size = c_uint(0) # Execute function result = lib.ReadFromBlob(blob, blob_size, b"MainImage", b"Image", byref(output_buffer), byref(output_size)) if result != 0: raise RuntimeError(f"Failed to read blob: error code {result}") # Extract data extracted_data = b"" if output_buffer.value and output_size.value > 0: buffer_array = cast(output_buffer, POINTER(c_char * output_size.value)) extracted_data = buffer_array.contents.raw # TODO: Add cleanup here if library requires freeing the buffer # Example: lib.FreeBlobBuffer(output_buffer) return extracted_data # Usage extracted = read_from_blob("path/to/your/library.dll", "path/to/your/blob.file") print(f"Extracted {len(extracted)} bytes of data")
Why This Works
- By passing pointers to uninitialized
c_void_pandc_uintvariables, we give the C function space to write the address and size of the actual data buffer it finds. - Explicitly defining
argtypesensures ctypes converts Python types correctly to C types, avoiding silent failures. - Casting the returned buffer address to a pointer-to-array lets us access the raw bytes in Python.
内容的提问来源于stack exchange,提问作者daria

