0

It's been a few years since my last foray into GDI, but I don't remember having an issue like this before. I am not getting an exception, but BitBlt is returning 0 (False), checking GetLastWIN32Error shows 6. Which appears to be an invalid handle. And the destination image remains blank.

I added in calls to SelectObject as well, but that shouldn't and didn't effect the invalid handle error.

Any thoughts on what I'm missing?

void MySub()
{
    var bmpSrc = new Bitmap("c:\\temp\\test.bmp", false);
    var bmpDst= new Bitmap(1000, 1000);
    var gSrc = Graphics.FromImage(bmpSrc);
    var gDst = Graphics.FromImage(bmpDst);
    IntPtr HDCSrc = gSrc.GetHdc();
    IntPtr HDCDst = gDst.GetHdc();
    if (!BitBlt(HDCDst, 0, 0, 55, 94, HDCSrc, 0, 0, SRCCOPY))
    {
        int er = Marshal.GetLastWin32Error();
        MessageBox.Show(er.ToString());
    }
    gDst.ReleaseHdc(HDCDst);
    gSrc.ReleaseHdc(HDCSrc);
    pictureBox1.Image = iDst;
}

public static long SRCCOPY = 0x00CC0020;

[DllImport("gdi32.dll", CallingConvention = CallingConvention.ThisCall, SetLastError = true)]
public static extern bool BitBlt(
     IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, long dwRop);
ThatRickGuy
  • 402
  • 3
  • 13

3 Answers3

1

You have the wrong calling convention for Win32 APIs. use:

CallingConvention = CallingConvention.StdCall

Also the last parameter should be UInt32 or equiv. Although not authoritative, pinvoke.net is pretty useful. In this case, it defines a nice enumeration for the last parameter, in case you'll be using any other raster operations with BitBlt.

Tom Blodget
  • 20,260
  • 3
  • 39
  • 72
  • Another great point, and my response is limited by my lack of understanding of the full impact of this change, but using CallingConvention.StdCall results in an exception on the BitBlt call: A call to PInvoke function 'Form1::BitBlt' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature. If using StdCall, is there some other configuration I'm missing to ensure compatibility? – ThatRickGuy Apr 30 '13 at 18:58
  • Thanks for the pointer to pinvoke.net, that was really helpful for getting the bitblt definition correct! – ThatRickGuy May 01 '13 at 02:04
1

Finally managed to track down an exact example of what I was trying to accomplish, here it is:

        var bmpSrc = new Bitmap("c:\\temp\\test.bmp");
        var bmpDst = new Bitmap(1000, 1000);

        // Get source image  in memory
        Graphics sourceImageGraphics = Graphics.FromImage(bmpDst);
        IntPtr sourceImageHDC = sourceImageGraphics.GetHdc();
        IntPtr sourceImageCDC = CreateCompatibleDC(sourceImageHDC);
        IntPtr sourceImageHandle = bmpDst.GetHbitmap();
        SelectObject(sourceImageCDC, sourceImageHandle);

        // Get overlay image in memory
        Graphics overlayImageGraphics = Graphics.FromImage(bmpSrc);
        IntPtr overlayImageHDC = overlayImageGraphics.GetHdc();
        IntPtr overlayImageCDC = CreateCompatibleDC(overlayImageHDC);
        IntPtr overlayImageHandle = bmpSrc.GetHbitmap();
        SelectObject(overlayImageCDC, overlayImageHandle);

        for (int x = 0; x < _Iterations; x++)
                if (!BitBlt(sourceImageHDC, 0, 0, 55, 94, overlayImageCDC, 0, 0, TernaryRasterOperations.SRCCOPY))
                {
                    var er = Marshal.GetLastWin32Error();
                    MessageBox.Show(er.ToString());
                }

        // Release source Image memory.
        DeleteDC(sourceImageCDC);
        DeleteObject(sourceImageHandle);
        sourceImageGraphics.ReleaseHdc(sourceImageHDC);
        sourceImageGraphics.Dispose();

        // Release overlay Image memory.
        DeleteDC(overlayImageCDC);
        DeleteObject(overlayImageHandle);
        overlayImageGraphics.ReleaseHdc(overlayImageHDC);
        overlayImageGraphics.Dispose();

        pictureBox1.Image = bmpDst;

And over 100,000 bitblts vs 100,000 .DrawImages, bitblt is crushing .DrawImages ~8:1 on my laptop. :)

ThatRickGuy
  • 402
  • 3
  • 13
0

Ok, so this is probably a comment, but as there's code, I'm posting here:

Is there any reason why you need to p/invoke? What's wrong with:

using(var bmpSrc = new Bitmap("c:\\temp\\test.bmp", false))
using(var bmpDst = new Bitmap(1000, 1000))
using(var gDst = Graphics.FromImage(bmpDst))
{
    gDst.DrawImage(bmpSrc,0,0,55,94);
    //...
}
spender
  • 117,338
  • 33
  • 229
  • 351
  • Good question! I'm actually seeing performance issues with Graphics.Draw... for the volume of renderings and the scale of number of users we're hitting. My plan was to compare the performance between DrawImage and direct calls to BitBlt to see if it would be worthwhile to pursue. – ThatRickGuy Apr 30 '13 at 18:54