Solved

Bitmaps, from handles (HBitmap, Hbmp)

Posted on 2004-04-14
6
1,338 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

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

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

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

Often, when implementing a feature, you won't know how certain events should be handled at the point where they occur and you'd rather defer to the user of your function or class. For example, a XML parser will extract a tag from the source code, wh…
IntroductionThis article is the second in a three part article series on the Visual Studio 2008 Debugger.  It provides tips in setting and using breakpoints. If not familiar with this debugger, you can find a basic introduction in the EE article loc…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.

730 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