Creating image file from pointer to HGLOBAL
-
In an app that is used to capture images, the information used to store the image data is referenced by a pointer to HGLOBAL. That is then used to re-create the image so it can be written to disk with a call to GlobalFree. I have two problems: I need to attach a file header so I can write the image to a file. The image data is of native type DWORD so I cant use the .NET classes without massaging the image data. What I have done thus far: BITMAPINFOHEADER* imgHeader = (BITMAPINFOHEADER*)GlobalLock(gPtrImage); array<BYTE>^ imgArray = gcnew array<BYTE>(imgHeader->biSizeImage); if (!imgHeader) { GlobalFree(gPtrImage); return; } BITMAPFILEHEADER* bfh = new BITMAPFILEHEADER(); bfh->bfType = 0x4d42; //BM bfh->bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + imgHeader->biSize + imgHeader->biSizeImage); bfh->bfReserved1 = 0; bfh->bfReserved2 = 0; bfh->bfOffBits = (DWORD) (bfh->bfSize - imgHeader->biSizeImage); LPDWORD Pixels = (LPDWORD)((LPBYTE)imgHeader + sizeof(BITMAPINFOHEADER)); Marshal::Copy(static_cast<IntPtr>(bfh), imgArray, 0, sizeof(BITMAPFILEHEADER)); Marshal::Copy(static_cast<IntPtr>(Pixels), imgArray, (sizeof(BITMAPFILEHEADER) + 1), imgHeader->biSizeImage); GlobalUnlock(gPtrImage); System::Environment::SpecialFolder saveFolder = System::Environment::SpecialFolder::MyDocuments; SaveFileDialog^ sfd = gcnew SaveFileDialog(); sfd->InitialDirectory = System::Environment::GetFolderPath(saveFolder); sfd->Filter = "Device Independent Bitmaps |*.bmp|All files|*.*"; if (sfd->ShowDialog() == System::Windows::Forms::DialogResult::OK) { System::IO::Stream^ imgFileStream = sfd->OpenFile(); if (imgFileStream != nullptr) { imgFileStream->Write(imgArray, 0, imgArray->Length); imgFileStream->Close(); } } GlobalFree(gPtrImage); gPtrImage = NULL; The file size is a lot larger than I would expect for a 640*480 32 bit BMP and it can't be displayed. The image is fine when written to a picturebox, the data is then unlocked but NOT freed. As a test, I save the image of the picturebox i.e PictureBox->Image and that is fine. The data needs to to be handed off to another application that requires native code so I need to re-create the image from the pointer to HGLOBAL and that's where it falls down What might be wrong with this? Thanks for any help
-
In an app that is used to capture images, the information used to store the image data is referenced by a pointer to HGLOBAL. That is then used to re-create the image so it can be written to disk with a call to GlobalFree. I have two problems: I need to attach a file header so I can write the image to a file. The image data is of native type DWORD so I cant use the .NET classes without massaging the image data. What I have done thus far: BITMAPINFOHEADER* imgHeader = (BITMAPINFOHEADER*)GlobalLock(gPtrImage); array<BYTE>^ imgArray = gcnew array<BYTE>(imgHeader->biSizeImage); if (!imgHeader) { GlobalFree(gPtrImage); return; } BITMAPFILEHEADER* bfh = new BITMAPFILEHEADER(); bfh->bfType = 0x4d42; //BM bfh->bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + imgHeader->biSize + imgHeader->biSizeImage); bfh->bfReserved1 = 0; bfh->bfReserved2 = 0; bfh->bfOffBits = (DWORD) (bfh->bfSize - imgHeader->biSizeImage); LPDWORD Pixels = (LPDWORD)((LPBYTE)imgHeader + sizeof(BITMAPINFOHEADER)); Marshal::Copy(static_cast<IntPtr>(bfh), imgArray, 0, sizeof(BITMAPFILEHEADER)); Marshal::Copy(static_cast<IntPtr>(Pixels), imgArray, (sizeof(BITMAPFILEHEADER) + 1), imgHeader->biSizeImage); GlobalUnlock(gPtrImage); System::Environment::SpecialFolder saveFolder = System::Environment::SpecialFolder::MyDocuments; SaveFileDialog^ sfd = gcnew SaveFileDialog(); sfd->InitialDirectory = System::Environment::GetFolderPath(saveFolder); sfd->Filter = "Device Independent Bitmaps |*.bmp|All files|*.*"; if (sfd->ShowDialog() == System::Windows::Forms::DialogResult::OK) { System::IO::Stream^ imgFileStream = sfd->OpenFile(); if (imgFileStream != nullptr) { imgFileStream->Write(imgArray, 0, imgArray->Length); imgFileStream->Close(); } } GlobalFree(gPtrImage); gPtrImage = NULL; The file size is a lot larger than I would expect for a 640*480 32 bit BMP and it can't be displayed. The image is fine when written to a picturebox, the data is then unlocked but NOT freed. As a test, I save the image of the picturebox i.e PictureBox->Image and that is fine. The data needs to to be handed off to another application that requires native code so I need to re-create the image from the pointer to HGLOBAL and that's where it falls down What might be wrong with this? Thanks for any help
BITMAPINFOHEADER.biSizeImage is allowed to be 0... I would personally feel better about checking for that unless you know for sure it's never going to be 0...maybe something like
if (imgHeader->biSizeImage == 0)
imgHeader->biSizeImage = (((imgHeader->biWidth * (long)imgHeader->biBitCount + 31L) & (~31L)) / 8L) * abs(imgHeader->biHeight);Al_S wrote:
array<BYTE>^ imgArray = gcnew array<BYTE>(imgHeader->biSizeImage);
Your array isn't big enough. What about the size of the BITMAPFILEHEADER and the BITMAPINFOHEADER?
Al_S wrote:
Marshal::Copy(static_cast(Pixels), imgArray, (sizeof(BITMAPFILEHEADER) + 1), imgHeader->biSizeImage);
Array indexes are 0-based... That should be Marshal::Copy(static_cast(Pixels), imgArray, sizeof(BITMAPFILEHEADER), imgHeader->biSizeImage); Also I'm not sure about those static_casts... why not just use IntPtr(bfh)/IntPtr(Pixels)
Al_S wrote:
The file size is a lot larger than I would expect for a 640*480 32 bit BMP
Should be a little over 1MB - just how big is it? You're the one calculating the size - what do you see in the debugger?
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
BITMAPINFOHEADER.biSizeImage is allowed to be 0... I would personally feel better about checking for that unless you know for sure it's never going to be 0...maybe something like
if (imgHeader->biSizeImage == 0)
imgHeader->biSizeImage = (((imgHeader->biWidth * (long)imgHeader->biBitCount + 31L) & (~31L)) / 8L) * abs(imgHeader->biHeight);Al_S wrote:
array<BYTE>^ imgArray = gcnew array<BYTE>(imgHeader->biSizeImage);
Your array isn't big enough. What about the size of the BITMAPFILEHEADER and the BITMAPINFOHEADER?
Al_S wrote:
Marshal::Copy(static_cast(Pixels), imgArray, (sizeof(BITMAPFILEHEADER) + 1), imgHeader->biSizeImage);
Array indexes are 0-based... That should be Marshal::Copy(static_cast(Pixels), imgArray, sizeof(BITMAPFILEHEADER), imgHeader->biSizeImage); Also I'm not sure about those static_casts... why not just use IntPtr(bfh)/IntPtr(Pixels)
Al_S wrote:
The file size is a lot larger than I would expect for a 640*480 32 bit BMP
Should be a little over 1MB - just how big is it? You're the one calculating the size - what do you see in the debugger?
Mark Salsbery Microsoft MVP - Visual C++ :java:
Hi Mark. Since the original image is from a global pointer to HGLOBAL, which was previously created and displayed, I am aware of all the fields of the BITMAPINFOHEADER. They are all set from grabbing an image in other code. I am trying to re-create the image after handing off the pointer to HGLOBAL elsewhere With that: biSizeImage = 0x12c000 (about 1.2M) biBitCount = 0x20 ... ... I saw that the array size was goofy so I fixed that - I think. array^ imgArray = gcnew array(imgHeader->biSizeImage + sizeof(BITMAPFILEHEADER)); I also fixed copying to the array: Marshal::Copy(static_cast<IntPtr>(bfh), imgArray, 0, sizeof(BITMAPFILEHEADER)); Marshal::Copy(static_cast<IntPtr>(Pixels), imgArray, sizeof(BITMAPFILEHEADER), imgHeader->biSizeImage); All this washes out to a correct file size. When viewed in a hex editor, the BITMAPFILEHEADER appears where it belongs correctly. The rest of the data is different though. The resultant image can not be viewed X| As far as the use of the static_cast, I don't have any problems with that and I think I've used it properly. Thanks for responding.
-
Hi Mark. Since the original image is from a global pointer to HGLOBAL, which was previously created and displayed, I am aware of all the fields of the BITMAPINFOHEADER. They are all set from grabbing an image in other code. I am trying to re-create the image after handing off the pointer to HGLOBAL elsewhere With that: biSizeImage = 0x12c000 (about 1.2M) biBitCount = 0x20 ... ... I saw that the array size was goofy so I fixed that - I think. array^ imgArray = gcnew array(imgHeader->biSizeImage + sizeof(BITMAPFILEHEADER)); I also fixed copying to the array: Marshal::Copy(static_cast<IntPtr>(bfh), imgArray, 0, sizeof(BITMAPFILEHEADER)); Marshal::Copy(static_cast<IntPtr>(Pixels), imgArray, sizeof(BITMAPFILEHEADER), imgHeader->biSizeImage); All this washes out to a correct file size. When viewed in a hex editor, the BITMAPFILEHEADER appears where it belongs correctly. The rest of the data is different though. The resultant image can not be viewed X| As far as the use of the static_cast, I don't have any problems with that and I think I've used it properly. Thanks for responding.
alleyes wrote:
I saw that the array size was goofy so I fixed that - I think. array^ imgArray = gcnew array<BYTE>(imgHeader->biSizeImage + sizeof(BITMAPFILEHEADER));
Still not big enough :) array<BYTE>^ imgArray = gcnew array<BYTE>(imgHeader->biSizeImage + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER));
alleyes wrote:
Marshal::Copy(static_cast<IntPtr>(Pixels), imgArray, sizeof(BITMAPFILEHEADER), imgHeader->biSizeImage);
You're not writing the BITMAPINFOHEADER to the output array... I missed that one the first time, sorry :) Marshal::Copy(static_cast<IntPtr>(imgHeader), imgArray, sizeof(BITMAPFILEHEADER), sizeof(BITMAPINFOHEADER) + imgHeader->biSizeImage);
Mark Salsbery Microsoft MVP - Visual C++ :java:
modified on Tuesday, January 12, 2010 4:47 PM
-
alleyes wrote:
I saw that the array size was goofy so I fixed that - I think. array^ imgArray = gcnew array<BYTE>(imgHeader->biSizeImage + sizeof(BITMAPFILEHEADER));
Still not big enough :) array<BYTE>^ imgArray = gcnew array<BYTE>(imgHeader->biSizeImage + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER));
alleyes wrote:
Marshal::Copy(static_cast<IntPtr>(Pixels), imgArray, sizeof(BITMAPFILEHEADER), imgHeader->biSizeImage);
You're not writing the BITMAPINFOHEADER to the output array... I missed that one the first time, sorry :) Marshal::Copy(static_cast<IntPtr>(imgHeader), imgArray, sizeof(BITMAPFILEHEADER), sizeof(BITMAPINFOHEADER) + imgHeader->biSizeImage);
Mark Salsbery Microsoft MVP - Visual C++ :java:
modified on Tuesday, January 12, 2010 4:47 PM
-
alleyes wrote:
I saw that the array size was goofy so I fixed that - I think. array^ imgArray = gcnew array<BYTE>(imgHeader->biSizeImage + sizeof(BITMAPFILEHEADER));
Still not big enough :) array<BYTE>^ imgArray = gcnew array<BYTE>(imgHeader->biSizeImage + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER));
alleyes wrote:
Marshal::Copy(static_cast<IntPtr>(Pixels), imgArray, sizeof(BITMAPFILEHEADER), imgHeader->biSizeImage);
You're not writing the BITMAPINFOHEADER to the output array... I missed that one the first time, sorry :) Marshal::Copy(static_cast<IntPtr>(imgHeader), imgArray, sizeof(BITMAPFILEHEADER), sizeof(BITMAPINFOHEADER) + imgHeader->biSizeImage);
Mark Salsbery Microsoft MVP - Visual C++ :java:
modified on Tuesday, January 12, 2010 4:47 PM
-
Hi Mark, Is there a way to do the reverse of this? That is, one HGLOBAL to store different images, where it normally is one HGLOBAL relating to one image. Thanks for the valuable help!
Hi Developers. i am going to wrap lib(static library ) file in c++. one function return HGLOBAL DIB. how can i access Byte Array or Bitmap Image from this and pass this Byte Array or bitmap to My C# Project. please help me. thanks in advance. Here is function that return HGLOBAL DIB Type.
HGLOBAL m_hDib;
int retval = XRaySensorIsImageAvailable();if ( 0 < retval ) { if (m\_hDib ) ::GlobalFree(m\_hDib); m\_hDib = XRaySensorGetDIB(); }