首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

C# 呼叫 C++ DLL 返回 CBitmap* 並在 PictureBox 顯示:P/Invoke 簽名、記憶體管理與像素拷貝完整實作

     編輯:程式設計綜合問答

C++ DLL 介面假設


// MyCppLib.h
#ifdef MYCPPLIB_EXPORTS
#define MYCPPLIB_API __declspec(dllexport)
#else
#define MYCPPLIB_API __declspec(dllimport)
#endif

// 簡化結構:僅包含 HBITMAP 與寬高
typedef struct {
    HBITMAP hBitmap;
    int     width;
    int     height;
} CBitmapWrapper;

extern "C" MYCPPLIB_API CBitmapWrapper* UserGetBitmap(HWND hwnd, int w, int h);
extern "C" MYCPPLIB_API void FreeBitmapWrapper(CBitmapWrapper* p);

C# P/Invoke 簽名


[StructLayout(LayoutKind.Sequential)]
public struct CBitmapWrapper {
    public IntPtr hBitmap; // HBITMAP → IntPtr
    public int width;
    public int height;
}

public static class NativeMethods {
    [DllImport("MyCppLib.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr UserGetBitmap(IntPtr hwnd, int w, int h); // 返回結構體指標

    [DllImport("MyCppLib.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void FreeBitmapWrapper(IntPtr pWrapper);
}

核心轉換邏輯:HBITMAP → Bitmap → PictureBox


public Bitmap ConvertHBitmapToBitmap(IntPtr hBitmap, int width, int height) {
    // 1. 取得裝置上下文
    IntPtr hdcSrc = GetDC(IntPtr.Zero);
    IntPtr hdcDest = CreateCompatibleDC(hdcSrc);
    IntPtr hOld = SelectObject(hdcDest, hBitmap);

    // 2. 建立托管 Bitmap
    Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
    BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height),
                                      ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);

    // 3. BitBlt 直接拷貝像素 (最快)
    BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, SRCCOPY);
    // 注意:上述僅示意,實際需用 GetDIBits 或手動拷貝至 bmpData.Scan0
    // 這裡用 GDI+ 簡化:
    using (Graphics g = Graphics.FromImage(bmp)) {
        g.DrawImage(Image.FromHbitmap(hBitmap), 0, 0, width, height);
    }

    // 4. 清理
    bmp.UnlockBits(bmpData);
    SelectObject(hdcDest, hOld);
    DeleteDC(hdcDest);
    ReleaseDC(IntPtr.Zero, hdcSrc);

    return bmp;
}

// P/Invoke 輔助
[DllImport("gdi32.dll")] static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll")] static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
[DllImport("gdi32.dll")] static extern bool BitBlt(IntPtr hdcDest, int x, int y, int w, int h, IntPtr hdcSrc, int sx, int sy, uint rop);
[DllImport("gdi32.dll")] static extern int GetDIBits(IntPtr hdc, IntPtr hbmp, uint start, uint lines, IntPtr bits, ref BITMAPINFO bmi, uint usage);
[DllImport("user32.dll")] static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("user32.dll")] static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc);
[DllImport("gdi32.dll")] static extern bool DeleteDC(IntPtr hdc);
const uint SRCCOPY = 0x00CC0020;

完整呼叫流程


private void btnGetImage_Click(object sender, EventArgs e) {
    IntPtr pWrapper = NativeMethods.UserGetBitmap(this.Handle, 640, 480);
    if (pWrapper == IntPtr.Zero) return;

    var wrapper = Marshal.PtrToStructure(pWrapper);
    using (Bitmap bmp = ConvertHBitmapToBitmap(wrapper.hBitmap, wrapper.width, wrapper.height)) {
        pictureBox1.Image?.Dispose();
        pictureBox1.Image = bmp;
    }
    NativeMethods.FreeBitmapWrapper(pWrapper); // 釋放 C++ 端資源
}

關鍵注意點

  • 記憶體邊界:C++ 端 new CBitmapWrapper,C# 端 FreeBitmapWrapper 對應釋放,避免洩漏。
  • 位圖格式:確認 C++ 端 HBITMAP 為 32bpp ARGB,否則 GetDIBits 需正確填寫 BITMAPINFOHEADER。
  • 執行緒安全:UI 執行緒呼叫,若 C++ 耗時長需 Task.Run 異步並 Invoke 回 UI。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved