如何将GDIPlus截图生成的std::vector<BYTE>字节数据通过Dart FFI传递至Flutter
Got it, let's tackle how to pass that std::vector<BYTE> from your GDI+ screenshot code to Flutter via Dart FFI. There are a few solid approaches here—let's break them down step by step:
方案1:Pass Pointer + Length (Most Common)
Dart FFI doesn't have a direct equivalent to std::vector, but it can natively handle raw pointers and numeric values. We'll expose the vector's underlying data pointer and its size to Dart, then convert it to a Uint8List (Flutter's preferred byte type).
Step 1: Modify Your C++ Code to Export Data
First, wrap your screenshot/PNG conversion logic into exportable functions, and keep the vector data alive while Dart reads it:
#include <iostream> #include <fstream> #include <vector> #include <Windows.h> #include <gdiplus.h> // Reuse your existing save_png_memory function bool save_png_memory(HBITMAP hbitmap, std::vector<BYTE>& data) { Gdiplus::Bitmap bmp(hbitmap, nullptr); IStream* istream = nullptr; if (CreateStreamOnHGlobal(NULL, TRUE, &istream) != 0) return false; CLSID clsid_png; if (CLSIDFromString(L"{557cf406-1a04-11d3-9a73-0000f81ef32e}", &clsid_png)!=0) return false; Gdiplus::Status status = bmp.Save(istream, &clsid_png); if (status != Gdiplus::Status::Ok) return false; HGLOBAL hg = NULL; if (GetHGlobalFromStream(istream, &hg) != S_OK) return 0; int bufsize = GlobalSize(hg); data.resize(bufsize); LPVOID pimage = GlobalLock(hg); if (!pimage) return false; memcpy(&data[0], pimage, bufsize); GlobalUnlock(hg); istream->Release(); return true; } // Static vector to keep data alive until Dart finishes reading it static std::vector<BYTE> s_png_data; extern "C" __declspec(dllexport) const BYTE* get_screenshot_png(size_t* out_size) { // Initialize GDI+ once (move this to DllMain if using a DLL) static ULONG_PTR gdiplus_token; static bool gdiplus_initialized = false; if (!gdiplus_initialized) { Gdiplus::GdiplusStartupInput tmp; Gdiplus::GdiplusStartup(&gdiplus_token, &tmp, NULL); gdiplus_initialized = true; } // Take screenshot (reuse your existing logic) RECT rc; GetClientRect(GetDesktopWindow(), &rc); auto hdc = GetDC(0); auto memdc = CreateCompatibleDC(hdc); auto hbitmap = CreateCompatibleBitmap(hdc, rc.right, rc.bottom); auto oldbmp = SelectObject(memdc, hbitmap); BitBlt(memdc, 0, 0, rc.right, rc.bottom, hdc, 0, 0, SRCCOPY); SelectObject(memdc, oldbmp); DeleteDC(memdc); ReleaseDC(0, hdc); // Generate PNG data s_png_data.clear(); bool success = save_png_memory(hbitmap, s_png_data); DeleteObject(hbitmap); // Return data and size to Dart if (success) { *out_size = s_png_data.size(); return s_png_data.data(); } else { *out_size = 0; return nullptr; } } // Export a function to free the static data (prevent memory leaks) extern "C" __declspec(dllexport) void free_screenshot_png() { s_png_data.clear(); }
Step 2: Dart FFI Code to Receive Data
In Flutter, use the ffi package to call these functions and convert the raw pointer to a Uint8List:
import 'dart:ffi'; import 'dart:typed_data'; import 'package:ffi/ffi.dart'; // Define function signatures matching C++ exports typedef GetScreenshotPngFunc = Pointer<Uint8> Function(Pointer<Size> outSize); typedef GetScreenshotPng = Pointer<Uint8> Function(Pointer<Size> outSize); typedef FreeScreenshotPngFunc = Void Function(); typedef FreeScreenshotPng = void Function(); void loadScreenshot() { // Load your compiled DLL (adjust path for your project) final dylib = DynamicLibrary.open('screenshot.dll'); // Look up exported functions final getPng = dylib.lookupFunction<GetScreenshotPngFunc, GetScreenshotPng>('get_screenshot_png'); final freePng = dylib.lookupFunction<FreeScreenshotPngFunc, FreeScreenshotPng>('free_screenshot_png'); // Call to get PNG data final sizePtr = calloc<Size>(); final dataPtr = getPng(sizePtr); final pngSize = sizePtr.value.toInt(); Uint8List? pngBytes; if (dataPtr != nullptr && pngSize > 0) { // Convert raw pointer to Uint8List (Flutter-friendly byte buffer) pngBytes = dataPtr.asTypedList(pngSize); // Use the bytes in Flutter, e.g., display as image: // Image.memory(pngBytes!) } // Clean up resources calloc.free(sizePtr); freePng(); }
方案2: Use a Struct to Encapsulate Data
If you prefer a more structured approach, wrap the pointer and size in a C++ struct, then mirror it in Dart:
C++ Side
typedef struct { const BYTE* data; size_t size; } PngBuffer; extern "C" __declspec(dllexport) PngBuffer get_screenshot_png_struct() { // Same screenshot/PNG logic as above, populate s_png_data PngBuffer result = {nullptr, 0}; if (!s_png_data.empty()) { result.data = s_png_data.data(); result.size = s_png_data.size(); } return result; }
Dart Side
// Define struct matching C++ layout class PngBuffer extends Struct { external Pointer<Uint8> data; @Int64() external int size; } typedef GetPngStructFunc = PngBuffer Function(); typedef GetPngStruct = PngBuffer Function(); // Usage final getPngStruct = dylib.lookupFunction<GetPngStructFunc, GetPngStruct>('get_screenshot_png_struct'); final buffer = getPngStruct(); final pngBytes = buffer.data.asTypedList(buffer.size);
方案3: Pre-Allocate Dart Memory (No Static Storage)
If you want Dart to manage the memory entirely, first query the required buffer size, then allocate memory in Dart and have C++ write directly to it:
C++ Side
extern "C" __declspec(dllexport) bool write_png_to_buffer(BYTE* buffer, size_t buffer_size, size_t* out_written) { std::vector<BYTE> png_data; // Same screenshot/PNG logic to populate png_data if (png_data.size() > buffer_size) { *out_written = png_data.size(); // Tell Dart how much space is needed return false; } memcpy(buffer, png_data.data(), png_data.size()); *out_written = png_data.size(); return true; }
Dart Side
typedef WritePngFunc = Bool Function(Pointer<Uint8> buffer, Size bufferSize, Pointer<Size> outWritten); typedef WritePng = bool Function(Pointer<Uint8> buffer, int bufferSize, Pointer<Size> outWritten); // First get required buffer size final outWrittenPtr = calloc<Size>(); write_png_to_buffer(nullptr, 0, outWrittenPtr); final requiredSize = outWrittenPtr.value.toInt(); // Allocate memory in Dart final buffer = calloc<Uint8>(requiredSize); final success = write_png_to_buffer(buffer, requiredSize, outWrittenPtr); if (success) { final pngBytes = buffer.asTypedList(outWrittenPtr.value.toInt()); // Use the bytes } // Clean up calloc.free(buffer); calloc.free(outWrittenPtr);
Key Notes
- GDI+ Initialization: Move GDI+ startup/shutdown to
DllMainif building a DLL, to avoid reinitializing it on every function call. - Memory Safety: Always free allocated memory (either via the
free_screenshot_pngfunction or Dart'scalloc.free). - Thread Safety: If calling FFI from Flutter isolates, ensure your C++ code is thread-safe (GDI+ is generally thread-safe if each thread has its own startup token).
内容的提问来源于stack exchange,提问作者Kumah Andrews

