Getting Win32 API to draw a square on form

1

Yes - I know this is not exciting - but my colleague can get the same psuedocode to work in a C program, it show a black square with some green bands - but in C# it only draws a black square - see below:

enter image description here

If you copy the code below in the partial class of Form1 (a fresh default Windows Application - you will see my problem. I have tried a million different things - but I guess it how I have stipulated the Win32 calls or something - if anyone can help where I have gone wrong - I would be very grateful.

The code below looks very long but I have decided to put it all in so it is easy to just copy/paste into the partial class for a standard form - so don't let it scare you!

 public partial class Form1 : Form
    {
        [DllImport("gdi32.dll", SetLastError = true)]
        static extern IntPtr CreateDIBitmap([In] IntPtr hdc, [In] ref BITMAPINFOHEADER lpbmih, uint fdwInit, byte[] lpbInit, [In] ref BITMAPINFO lpbmi, uint fuUsage);

        [DllImport("gdi32.dll", SetLastError = true)]
        static extern int SetDIBits(IntPtr hdc, IntPtr hbmp, uint uStartScan, uint
           cScanLines, byte[] lpvBits, [In] ref BITMAPINFO lpbmi, uint fuColorUse);

        [DllImport("gdi32.dll", SetLastError = true)]
        static extern IntPtr CreateCompatibleDC(IntPtr hdc);

        [DllImport("gdi32.dll", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
        static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);

        enum TernaryRasterOperations : uint
        {
            /// <summary>dest = source</summary>
            SRCCOPY = 0x00CC0020,
            /// <summary>dest = source OR dest</summary>
            SRCPAINT = 0x00EE0086,
            /// <summary>dest = source AND dest</summary>
            SRCAND = 0x008800C6,
            /// <summary>dest = source XOR dest</summary>
            SRCINVERT = 0x00660046,
            /// <summary>dest = source AND (NOT dest)</summary>
            SRCERASE = 0x00440328,
            /// <summary>dest = (NOT source)</summary>
            NOTSRCCOPY = 0x00330008,
            /// <summary>dest = (NOT src) AND (NOT dest)</summary>
            NOTSRCERASE = 0x001100A6,
            /// <summary>dest = (source AND pattern)</summary>
            MERGECOPY = 0x00C000CA,
            /// <summary>dest = (NOT source) OR dest</summary>
            MERGEPAINT = 0x00BB0226,
            /// <summary>dest = pattern</summary>
            PATCOPY = 0x00F00021,
            /// <summary>dest = DPSnoo</summary>
            PATPAINT = 0x00FB0A09,
            /// <summary>dest = pattern XOR dest</summary>
            PATINVERT = 0x005A0049,
            /// <summary>dest = (NOT dest)</summary>
            DSTINVERT = 0x00550009,
            /// <summary>dest = BLACK</summary>
            BLACKNESS = 0x00000042,
            /// <summary>dest = WHITE</summary>
            WHITENESS = 0x00FF0062,
            /// <summary>
            /// Capture window as seen on screen.  This includes layered windows
            /// such as WPF windows with AllowsTransparency="true"
            /// </summary>
            CAPTUREBLT = 0x40000000
        }

        [DllImport("gdi32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);

        [StructLayout(LayoutKind.Sequential)]
        public struct BITMAPINFOHEADER
        {
            public uint biSize;
            public int biWidth;
            public int biHeight;
            public ushort biPlanes;
            public ushort biBitCount;
            public uint biCompression;
            public uint biSizeImage;
            public int biXPelsPerMeter;
            public int biYPelsPerMeter;
            public uint biClrUsed;
            public uint biClrImportant;

            public void Init()
            {
                biSize = (uint)Marshal.SizeOf(this);
            }
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct RGBQUAD
        {
            public byte rgbBlue;
            public byte rgbGreen;
            public byte rgbRed;
            public byte rgbReserved;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct BITMAPINFO
        {
            public BITMAPINFOHEADER bmiHeader;
            public RGBQUAD bmiColors;
        }   

        [DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr GetDC(IntPtr hWnd);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);

        private System.IntPtr m_Bitmap;
        private BITMAPINFOHEADER m_Bmh;
        private BITMAPINFO m_Bmi = new BITMAPINFO();

        public Form1()
        {
            m_Bmh.Init();
            m_Bmh.biPlanes = 1;
            m_Bmh.biBitCount = 24;
            m_Bmh.biCompression = 0;
            m_Bmh.biHeight = 100;
            m_Bmh.biWidth = 100;

            m_Bitmap = (IntPtr)0;

            InitializeComponent();
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {   
            int errorNumber = Marshal.GetLastWin32Error();

            byte[] testGraphicArray = new byte[300];
            for (int i = 0; i < m_Bmh.biWidth; i++)
            {
                testGraphicArray[i * 3 + 0] = Convert.ToByte(i);
                testGraphicArray[i * 3 + 1] = Convert.ToByte(255 - i);
                testGraphicArray[i * 3 + 2] = Convert.ToByte(i);
            }

            IntPtr winPtr = GetDC(this.Handle);

            errorNumber = Marshal.GetLastWin32Error();

            //Make the bitmap
            if (m_Bitmap == (IntPtr)0)
                m_Bitmap = CreateDIBitmap(winPtr, ref m_Bmh, (uint)0L, testGraphicArray, ref m_Bmi, (uint)0L);

            errorNumber = Marshal.GetLastWin32Error();

            int retValue;
            //Set data to bitmap
            for (int i = 0; i < 100; i++)
            {
                retValue = SetDIBits((System.IntPtr)winPtr, m_Bitmap, (uint)i, 1, testGraphicArray, ref m_Bmi, (uint)0L);
            }

            errorNumber = Marshal.GetLastWin32Error();

            // Draw the bitmap
            if (m_Bitmap != (IntPtr)0)
            {
                IntPtr hMemDC;
                IntPtr Old;

                hMemDC = CreateCompatibleDC((System.IntPtr)winPtr);

                Old = SelectObject(hMemDC, m_Bitmap);//Select out what was in DC

                bool success = BitBlt((System.IntPtr)winPtr, 10, 10, m_Bmh.biWidth, m_Bmh.biHeight, hMemDC, 0, 0, TernaryRasterOperations.SRCCOPY);
                errorNumber = Marshal.GetLastWin32Error();
                SelectObject(hMemDC, Old);//Put back in the previous stuff back into DC
            }

            ReleaseDC(this.Handle, winPtr);   
        }
c#
winapi
gdi
asked on Stack Overflow Jan 12, 2012 by Vidar • edited Jan 12, 2012 by Vidar

1 Answer

1

Is the double-buffering style set?

If so, then .NET arranges for e.Graphics in OnPaint to be an in-memory DC, and after the Paint routine completes, .NET will overwrite everything on-screen from that in-memory layer.

Make sure your control styles are set appropriately. I think you want AllPaintingInWmPaint turned on, and double buffering turned off.

You're missing a number of other things that are necessary for properly handling WM_PAINT using native APIs, though, such as using the device context returned by BeginPaint and later calling EndPaint. You may need to handle WM_PAINT from WndProc and not let .NET dispatch it to OnPaint and the Paint event handlers.

I hope you're doing this to learn about the Win32 GDI APIs, and not because you plan to draw on .NET Forms with them in a finished application.

answered on Stack Overflow Jan 12, 2012 by Ben Voigt

User contributions licensed under CC BY-SA 3.0