You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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 DllMain if building a DLL, to avoid reinitializing it on every function call.
  • Memory Safety: Always free allocated memory (either via the free_screenshot_png function or Dart's calloc.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 18:02:34