Solved

Bitmaps, from handles (HBitmap, Hbmp)

Posted on 2004-04-14
6
1,333 Views
Last Modified: 2013-12-03
Aright, I need sample code for this, as I am through beating my head against the wall, and not getting it.

Background: I'm injecting a Paint Hook dll into a process, to grab the window before it is scaled.

What I need:
Working, unmanaged, C++ code that can create a Bitmap. I do not care what kind, because it is going into C# land, I can manipulate it from there.

Here a portion of the code :

HBITMAP hBmp = NULL;

    {
        HDC hDC = GetDC(hWnd);
        hBmp = CreateCompatibleBitmap(hDC, rect.right - rect.left, rect.bottom - rect.top);
        ReleaseDC(hWnd, hDC);
    }
      hOld = SelectObject(hDCMem, hBmp);

I need a bitmap created from hBmp. And this code is going into a DLL.

I want headers, prototypes, whatever, give it all to me as long as it works.
0
Comment
Question by:rossryan
  • 3
  • 2
6 Comments
 
LVL 14

Expert Comment

by:Daniel Junges
ID: 10822193


HDC hDC = GetDC(hWnd);

CBitmap hBmp;
BOOL result = hBmp.CreateCompatibleBitmap(hDC, rect.right - rect.left, rect.bottom - rect.top);

0
 
LVL 48

Accepted Solution

by:
AlexFM earned 500 total points
ID: 10823515
Hi rossryan, I have some code which shows how we can create bitmap in unmanaged code and pass it to managed code. Unmanaged Dll creates bitmap, fills it with some image, and copies bitmap bits to pointer supplied by caller (C# application). Having this sample you can continue your work, changing pointer supplied by caller to memory-mapped file.

Create unmanaged C++ Win32 Dll BMPServer. Visual Studio creates two files in this project: BMPServer.h and BMPServer.cpp. Paste the following code to BMPServer.h (overwriting existing code):

#ifdef BMPSERVER_EXPORTS
#define BMPSERVER_API __declspec(dllexport)
#else
#define BMPSERVER_API __declspec(dllimport)
#endif

extern "C" BMPSERVER_API void CreateBMP(BYTE* pBits, int nWidth, int nHeight);

Paste the following code to BMPServer.cpp (overwriting existing code):

#include "stdafx.h"
#include "BMPServer.h"
BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                               )
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
      break;
    }
    return TRUE;
}


BMPSERVER_API void CreateBMP(BYTE* pBits, int nWidth, int nHeight)
{
    LPBITMAPINFO lpbi;

    lpbi = (LPBITMAPINFO) new BYTE[sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD))];
    lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    lpbi->bmiHeader.biWidth = nWidth;
    lpbi->bmiHeader.biHeight = nHeight;
    lpbi->bmiHeader.biPlanes = 1;
    lpbi->bmiHeader.biBitCount = 32; //24;
    lpbi->bmiHeader.biCompression = BI_RGB;
    lpbi->bmiHeader.biSizeImage = nWidth * nHeight; //    WIDTHBYTES((DWORD)nWidth * 8) * nHeight;
    lpbi->bmiHeader.biXPelsPerMeter = 0;
    lpbi->bmiHeader.biYPelsPerMeter = 0;
    lpbi->bmiHeader.biClrUsed = 0;
    lpbi->bmiHeader.biClrImportant = 0;

    BYTE* pTmp;

    HBITMAP hBitmap = CreateDIBSection(
        NULL,
        lpbi,
        DIB_RGB_COLORS,
        (void **)&pTmp,
        NULL,
        0 );

    delete[] lpbi;

    HDC hDC = CreateCompatibleDC(NULL);
    SelectObject(hDC, hBitmap);

    HPEN pen = CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
    HBRUSH brush = CreateSolidBrush(RGB(255, 255, 255));

    SelectObject(hDC, pen);
    SelectObject(hDC, brush);

    Rectangle(hDC, 0, 0, nWidth, nHeight);
    TextOut(hDC, 20, 20, "Hello", (int)strlen("Hello"));


    DWORD ImageSize = 4 * nWidth * nHeight;

    GetBitmapBits(hBitmap, ImageSize, pBits);


    DeleteObject(pen);
    DeleteObject(brush);

    DeleteObject(hBitmap);
    DeleteDC(hDC);
}

Create Windows C# application BMPClient and paste the following code to Form1.cs:

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;
using System.IO;
using System.Drawing.Imaging;


namespace BMPClient
{
    /// <summary>
    /// Summary description for Form1.
    /// </summary>
    public class Form1 : System.Windows.Forms.Form
    {
        private System.Windows.Forms.PictureBox pictureBox1;
        private System.Windows.Forms.Button button1;
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.Container components = null;

        public Form1()
        {
            //
            // Required for Windows Form Designer support
            //
            InitializeComponent();

            //
            // TODO: Add any constructor code after InitializeComponent call
            //
        }

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if (components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }

        #region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.pictureBox1 = new System.Windows.Forms.PictureBox();
            this.button1 = new System.Windows.Forms.Button();
            this.SuspendLayout();
            //
            // pictureBox1
            //
            this.pictureBox1.Location = new System.Drawing.Point(16, 24);
            this.pictureBox1.Name = "pictureBox1";
            this.pictureBox1.Size = new System.Drawing.Size(232, 136);
            this.pictureBox1.TabIndex = 0;
            this.pictureBox1.TabStop = false;
            //
            // button1
            //
            this.button1.Location = new System.Drawing.Point(72, 200);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(136, 32);
            this.button1.TabIndex = 1;
            this.button1.Text = "Run";
            this.button1.Click += new System.EventHandler(this.button1_Click);
            //
            // Form1
            //
            this.AutoScaleBaseSize = new System.Drawing.Size(6, 15);
            this.ClientSize = new System.Drawing.Size(264, 245);
            this.Controls.Add(this.button1);
            this.Controls.Add(this.pictureBox1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);

        }
        #endregion

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.Run(new Form1());
        }

        [DllImport ("BMPServer.dll")]
        public static extern void CreateBMP(IntPtr pBits, int nWidth, int nHeight);

        [DllImport ("Gdi32.dll")]
        public static extern Int32 SetBitmapBits(IntPtr hBmp, Int32 cbBytes, IntPtr lpBits);

        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);


        private void button1_Click(object sender, System.EventArgs e)
        {
            int nWidth = 200;
            int nHeight = 100;

            IntPtr pBitmapBits = Marshal.AllocHGlobal(nWidth*nHeight*4);

            CreateBMP(pBitmapBits, nWidth, nHeight);

            Bitmap bmp = new Bitmap(nWidth, nHeight, PixelFormat.Format32bppRgb);

            IntPtr hBitmap = bmp.GetHbitmap();

            SetBitmapBits(hBitmap, nWidth*nHeight*4, pBitmapBits);

            Bitmap bmpResult = Bitmap.FromHbitmap(hBitmap);

            DeleteObject(hBitmap);

            pictureBox1.Image = bmpResult;

            Marshal.FreeHGlobal(pBitmapBits);
        }
    }
}

Build both projects. Ensure that BMPServer.dll is available at runtime from BMPClient.exe and run C# client. Press the button and see result.
0
 

Author Comment

by:rossryan
ID: 10828783
Hmm, DIB. Very nice. On a previous discussion, things were getting out of hand (first we start up GDI+, then make the image, then shut it down...pain).

I'm going to merge this code with what I have after work.
0
Announcing the Most Valuable Experts of 2016

MVEs are more concerned with the satisfaction of those they help than with the considerable points they can earn. They are the types of people you feel privileged to call colleagues. Join us in honoring this amazing group of Experts.

 

Author Comment

by:rossryan
ID: 10829187
//CPP
HWND hWnd = NULL;
HBITMAP hBmp = NULL;
HGDIOBJ hOld = NULL;
HDC hDCMem = NULL;
HANDLE hMap = NULL;
SapphireData* SapphireData;
HANDLE hSapphireData = NULL;
void* SapphireDataMap = NULL;
CPaintHook hook;




void HookWindow2(HWND hWnd)
{
      
      hMap = CreateFileMapping(INVALID_HANDLE_VALUE,    // current file handle
    NULL,                              // default security
    PAGE_READWRITE,                    // read/write permission
    0,                                 // max. object size
    0,                                 // size of hFile
    "SapphireFSwap");            // name of mapping object


      if (hMap != NULL && GetLastError() == ERROR_ALREADY_EXISTS)
      {
    CloseHandle(hMap);
    hMap = NULL;
      }

      
      if (hMap != NULL)
      {
    CloseHandle(hMap);
    hMap = NULL;
      }
      SapphireDataMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, "SapphireFSwap");

      if (SapphireDataMap != NULL)
      {
      hSapphireData = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
      }
   
      if (hSapphireData != NULL)
      {
            SapphireData = (struct SapphireData*)hSapphireData;
      }
      

      

      
   

    hook.SubClass(hWnd);


    hDCMem = CreateCompatibleDC(NULL);

    RECT rect;

    GetWindowRect(hWnd, & rect);

    hBmp = NULL;

    {
        HDC hDC = GetDC(hWnd);
        hBmp = CreateCompatibleBitmap(hDC, rect.right - rect.left, rect.bottom - rect.top);
        ReleaseDC(hWnd, hDC);
    }
      hOld = SelectObject(hDCMem, hBmp);

}

void UpdateWindow2()
{  
      GdiplusStartupInput gdiplusStartupInput;
      ULONG_PTR gdiplusToken;
      GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
      SendMessage(hWnd, WM_PRINT, (WPARAM) hDCMem, PRF_CHILDREN | PRF_CLIENT | PRF_ERASEBKGND | PRF_NONCLIENT | PRF_OWNED);
      SapphireData->SapphireSwap = Bitmap::FromHBITMAP(hBmp);
      //delete image;
    GdiplusShutdown(gdiplusToken);
    return 0;

}

void DestroyWindow2()
{

      
 

      SelectObject(hDCMem, hOld);
    DeleteObject(hDCMem);

      /*
    OpenClipboard(hWnd);
 
    EmptyClipboard();
    SetClipboardData(CF_BITMAP, hBmp);
    CloseClipboard();
      */

      if (hSapphireData)
      {
    UnmapViewOfFile(hSapphireData);
    SapphireData = NULL;
      }
      CloseHandle(SapphireDataMap);

}

//H
struct SapphireData
{
CBitmap SapphireSwap;
};





void CaptureWindow(HWND hWnd);
void HookWindow2(HWND hWnd);
void UpdateWindow2();
void DestroyWindow2();



Hmm. Aright, how do I redefine the above struct to carry the bits (or a full bitmap) into shared memory?


CreateBMP(BYTE* pBits, int nWidth, int nHeight)

Hmm. I have a handle to a bitmap (hBmp), not the bits, so, I'll probably have to lock memory to get them. Width and Height I can grab from getClientRect.
0
 

Author Comment

by:rossryan
ID: 10829461
I've got one other question for you Alex (not code related). I've told you about Sapphire, do you think it's pointless?

I mean, I've studied MS Task Gallery, Sun's Project Looking Glass, 3DOsX, 3DTop, 3DWM, LiteStep...the list goes on. I've looked at their interfaces, compared what I think are the best parts, considered whether they are relevant or a dead end.

What do you think?
0
 
LVL 48

Expert Comment

by:AlexFM
ID: 10830466
I don't know nothing about Sapphire, so I cannot help here.

About your code:

hBmp = CreateCompatibleBitmap(hDC, rect.right - rect.left, rect.bottom - rect.top);

Replace this with lines:

    LPBITMAPINFO lpbi;

    ...

    delete[] lpbi;

By this way you have DIB instead of DDB. Lines from my code:

    HDC hDC = CreateCompatibleDC(NULL);

    ...

    TextOut(hDC, 20, 20, "Hello", (int)strlen("Hello"));

are used to fill bitmap, you have your own code for this. Line:

   GetBitmapBits(hBitmap, ImageSize, pBits);

write bitmap bits to buffer supplied by called. Instead of this write bitmap bits to memory mapped file.
0

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Written by John Humphreys C++ Threading and the POSIX Library This article will cover the basic information that you need to know in order to make use of the POSIX threading library available for C and C++ on UNIX and most Linux systems.   [s…
C++ Properties One feature missing from standard C++ that you will find in many other Object Oriented Programming languages is something called a Property (http://www.experts-exchange.com/Programming/Languages/CPP/A_3912-Object-Properties-in-C.ht…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

766 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question