Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. Managed C++/CLI
  4. Saving Bitmap image data to HGLOBAL [modified]

Saving Bitmap image data to HGLOBAL [modified]

Scheduled Pinned Locked Moved Managed C++/CLI
graphicscsharpwinformsdebuggingperformance
34 Posts 3 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • A alleyes 0

    Mark Salsbery wrote:

    You get a pointer to that block of memory with GlobalLock().

    That's what I meant by pointer but mis-construed it a bit. To summarize then. I can copy the bitmap data to this HGLOBAL using managed Bitmap classes? I do have the advantage of BITMAPINFOHEADER being available from the image that is to be copied from so I have most of that information which happens to be common to both the images in question If copying to the memory block is the lock still in place or do you call an GlobalUnLock first.

    M Offline
    M Offline
    Mark Salsbery
    wrote on last edited by
    #16

    alleyes wrote:

    I can copy the bitmap data to this HGLOBAL using managed Bitmap classes?

    You can't directly copy from managed to unmanaged memory. You can use Marshal::Copy though. You can also wrap the pointer in an UnmanagedMemoryStream object so you can use any managed functionality that uses a Stream object. For example, you could write a BMP file from a Bitmap to memory in one line of code instead of building the headers manually.

    alleyes wrote:

    If copying to the memory block is the lock still in place or do you call an GlobalUnLock first.

    You use the pointer while it's locked.

    Mark Salsbery Microsoft MVP - Visual C++ :java:

    A 2 Replies Last reply
    0
    • M Mark Salsbery

      alleyes wrote:

      I can copy the bitmap data to this HGLOBAL using managed Bitmap classes?

      You can't directly copy from managed to unmanaged memory. You can use Marshal::Copy though. You can also wrap the pointer in an UnmanagedMemoryStream object so you can use any managed functionality that uses a Stream object. For example, you could write a BMP file from a Bitmap to memory in one line of code instead of building the headers manually.

      alleyes wrote:

      If copying to the memory block is the lock still in place or do you call an GlobalUnLock first.

      You use the pointer while it's locked.

      Mark Salsbery Microsoft MVP - Visual C++ :java:

      A Offline
      A Offline
      alleyes 0
      wrote on last edited by
      #17

      Mark Salsbery wrote:

      You can't directly copy from managed to unmanaged memory. You can use Marshal::Copy though

      I fully intended using the Marshal class for copying.

      Mark Salsbery wrote:

      You can also wrap the pointer in an UnmanagedMemoryStream object so you can use any managed functionality that uses a Stream object.

      Now you threw me a bit. One of the things I was considering was saving the image to a managed memory stream object. Why consider unmanaged? By calling the Save method and then specifying BMP as the image type, I do get the BITMAPHEADERINFO. This is what I'm building upon without doing the Save to Stream technique:

      		System::Drawing::Rectangle imgRect = 
      			System::Drawing::Rectangle(0, 0, 
      			MyImage->Width, MyImage->Height);
      		System::Drawing::Imaging::BitmapData^ bmpData =
      			MyImage->LockBits(imgRect, 
      			System::Drawing::Imaging::ImageLockMode::ReadOnly,
      			System::Drawing::Imaging::PixelFormat::Format32bppRgb);
      		int byteCount = bmpData->Stride \* MyImage->Height;
      		array<Byte>^ bmpBytes = gcnew array<Byte>(byteCount);
      		Marshal::Copy(bmpData->Scan0, bmpBytes, 0, byteCount);
      		MyImage->UnlockBits(bmpData);
      

      Once I have the image in an array shouldn't I be copying it to the HGLOBAL? Or should the destination be the HGLOBAL and forget about the array?

      modified on Wednesday, January 20, 2010 2:42 PM

      1 Reply Last reply
      0
      • M Mark Salsbery

        alleyes wrote:

        I can copy the bitmap data to this HGLOBAL using managed Bitmap classes?

        You can't directly copy from managed to unmanaged memory. You can use Marshal::Copy though. You can also wrap the pointer in an UnmanagedMemoryStream object so you can use any managed functionality that uses a Stream object. For example, you could write a BMP file from a Bitmap to memory in one line of code instead of building the headers manually.

        alleyes wrote:

        If copying to the memory block is the lock still in place or do you call an GlobalUnLock first.

        You use the pointer while it's locked.

        Mark Salsbery Microsoft MVP - Visual C++ :java:

        A Offline
        A Offline
        alleyes 0
        wrote on last edited by
        #18

        I think I have the solution:

        MemoryStream^ ms = gcnew MemoryStream();
        MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp);
        array<Byte>^ bits = gcnew array<Byte>(MyHeader->biSizeImage);
        bits = ms->GetBuffer();
        ms->Close();
        Marshal::Copy(bits, 0, static_cast<IntPtr>(MyHGlobal), bits->Length);

        Where: MyHeader->biSizeImage is the member from the BITMAPINFOHEADER MyHGlobal is the handle to the locked memory block. I then throw an AccessViolationException at the copy call. I think that perhaps the array size may be bigger than the memory block. I appreciate your help so far - I'm close!

        M 1 Reply Last reply
        0
        • A alleyes 0

          I think I have the solution:

          MemoryStream^ ms = gcnew MemoryStream();
          MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp);
          array<Byte>^ bits = gcnew array<Byte>(MyHeader->biSizeImage);
          bits = ms->GetBuffer();
          ms->Close();
          Marshal::Copy(bits, 0, static_cast<IntPtr>(MyHGlobal), bits->Length);

          Where: MyHeader->biSizeImage is the member from the BITMAPINFOHEADER MyHGlobal is the handle to the locked memory block. I then throw an AccessViolationException at the copy call. I think that perhaps the array size may be bigger than the memory block. I appreciate your help so far - I'm close!

          M Offline
          M Offline
          Mark Salsbery
          wrote on last edited by
          #19

          I said UnmanagedMemoryStream, not MemoryStream. And you don't cast an HGLOBAL to a pointer - GlobalLock returns the pointer. And Bitmap::Save() saves everything - BITMAPFILEHEADER, BITMAPINFO, and the pixel bits. You're trying to make it harder than it is :)

          void *pMyGlobalMemory = GlobalLock(MyHGlobal);
          UnmanagedMemoryStream^ ms = gcnew UnmanagedMemoryStream(IntPtr(pMyGlobalMemory), GlobalSize(MyHGlobal));
          MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp);
          GlobalUnlock(MyHGlobal);

          Mark Salsbery Microsoft MVP - Visual C++ :java:

          A 2 Replies Last reply
          0
          • M Mark Salsbery

            I said UnmanagedMemoryStream, not MemoryStream. And you don't cast an HGLOBAL to a pointer - GlobalLock returns the pointer. And Bitmap::Save() saves everything - BITMAPFILEHEADER, BITMAPINFO, and the pixel bits. You're trying to make it harder than it is :)

            void *pMyGlobalMemory = GlobalLock(MyHGlobal);
            UnmanagedMemoryStream^ ms = gcnew UnmanagedMemoryStream(IntPtr(pMyGlobalMemory), GlobalSize(MyHGlobal));
            MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp);
            GlobalUnlock(MyHGlobal);

            Mark Salsbery Microsoft MVP - Visual C++ :java:

            A Offline
            A Offline
            alleyes 0
            wrote on last edited by
            #20

            I get an error when trying that: Error C2664: 'System::IO::UnmanagedMemoryStream::UnmanagedMemoryStream(unsigned char *,__int64)' : cannot convert parameter 1 from 'System::IntPtr' to 'unsigned char *' I didn't consider the UnmanagedMemoryStream

            Mark Salsbery wrote:

            You're trying to make it harder than it is Smile

            Yeah it seems that way :doh:

            M 1 Reply Last reply
            0
            • A alleyes 0

              I get an error when trying that: Error C2664: 'System::IO::UnmanagedMemoryStream::UnmanagedMemoryStream(unsigned char *,__int64)' : cannot convert parameter 1 from 'System::IntPtr' to 'unsigned char *' I didn't consider the UnmanagedMemoryStream

              Mark Salsbery wrote:

              You're trying to make it harder than it is Smile

              Yeah it seems that way :doh:

              M Offline
              M Offline
              Mark Salsbery
              wrote on last edited by
              #21

              Oops wrong constructor :)

              unsigned char *pMyGlobalMemory = (unsigned char *)GlobalLock(MyHGlobal);
              UnmanagedMemoryStream^ ms = gcnew UnmanagedMemoryStream(pMyGlobalMemory, GlobalSize(MyHGlobal));

              Note you're still resonsible for making sure the allocated global memory is large enough to write to. GlobalSize() will give you the allocated size, but if you don't know how many bytes will be written, you'll need to write to a growable memory stream first, check how many bytes were written, compare it to the globally allocated size, and reallocate if you need more room.

              Mark Salsbery Microsoft MVP - Visual C++ :java:

              1 Reply Last reply
              0
              • M Mark Salsbery

                I said UnmanagedMemoryStream, not MemoryStream. And you don't cast an HGLOBAL to a pointer - GlobalLock returns the pointer. And Bitmap::Save() saves everything - BITMAPFILEHEADER, BITMAPINFO, and the pixel bits. You're trying to make it harder than it is :)

                void *pMyGlobalMemory = GlobalLock(MyHGlobal);
                UnmanagedMemoryStream^ ms = gcnew UnmanagedMemoryStream(IntPtr(pMyGlobalMemory), GlobalSize(MyHGlobal));
                MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp);
                GlobalUnlock(MyHGlobal);

                Mark Salsbery Microsoft MVP - Visual C++ :java:

                A Offline
                A Offline
                alleyes 0
                wrote on last edited by
                #22

                I made a small change: void* pMyGlobal = GlobalLock(MyHGlobal); UnmanagedMemoryStream^ ms = gcnew UnmanagedMemoryStream((unsigned char*)(pMyGlobal), GlobalSize(gPtrImage)); MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp); It builds but then I get ExternalException was unhandled in GDI+

                M 1 Reply Last reply
                0
                • A alleyes 0

                  I made a small change: void* pMyGlobal = GlobalLock(MyHGlobal); UnmanagedMemoryStream^ ms = gcnew UnmanagedMemoryStream((unsigned char*)(pMyGlobal), GlobalSize(gPtrImage)); MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp); It builds but then I get ExternalException was unhandled in GDI+

                  M Offline
                  M Offline
                  Mark Salsbery
                  wrote on last edited by
                  #23

                  alleyes wrote:

                  It builds but then I get ExternalException was unhandled in GDI+

                  I can't debug that for you from here, but I suspect not enough allocated on the HGLOBAL. So maybe something like this:

                  MemoryStream^ ms = gcnew MemoryStream();
                  MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp);
                  if (GlobalSize(MyHGlobal) < ms->Length)
                  {
                      GlobalRealloc(MyHGlobal, ms->Length, 0);
                  }
                  void \*pMyGlobalMemory = GlobalLock(MyHGlobal);
                  UnmanagedMemoryStream^ ums = gcnew UnmanagedMemoryStream((unsigned char \*)pMyGlobalMemory, GlobalSize(MyHGlobal));
                  ms->WriteTo(ums);
                  GlobalUnlock(MyHGlobal);
                  

                  Mark Salsbery Microsoft MVP - Visual C++ :java:

                  A 2 Replies Last reply
                  0
                  • M Mark Salsbery

                    alleyes wrote:

                    It builds but then I get ExternalException was unhandled in GDI+

                    I can't debug that for you from here, but I suspect not enough allocated on the HGLOBAL. So maybe something like this:

                    MemoryStream^ ms = gcnew MemoryStream();
                    MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp);
                    if (GlobalSize(MyHGlobal) < ms->Length)
                    {
                        GlobalRealloc(MyHGlobal, ms->Length, 0);
                    }
                    void \*pMyGlobalMemory = GlobalLock(MyHGlobal);
                    UnmanagedMemoryStream^ ums = gcnew UnmanagedMemoryStream((unsigned char \*)pMyGlobalMemory, GlobalSize(MyHGlobal));
                    ms->WriteTo(ums);
                    GlobalUnlock(MyHGlobal);
                    

                    Mark Salsbery Microsoft MVP - Visual C++ :java:

                    A Offline
                    A Offline
                    alleyes 0
                    wrote on last edited by
                    #24

                    Mark Salsbery wrote:

                    I can't debug that for you from here

                    No of course not. I was suspecting perhaps an image already in memory was the issue and maybe that needed a dispose method called, not sure. Thanks for the re-size tip :-D

                    1 Reply Last reply
                    0
                    • M Mark Salsbery

                      alleyes wrote:

                      It builds but then I get ExternalException was unhandled in GDI+

                      I can't debug that for you from here, but I suspect not enough allocated on the HGLOBAL. So maybe something like this:

                      MemoryStream^ ms = gcnew MemoryStream();
                      MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp);
                      if (GlobalSize(MyHGlobal) < ms->Length)
                      {
                          GlobalRealloc(MyHGlobal, ms->Length, 0);
                      }
                      void \*pMyGlobalMemory = GlobalLock(MyHGlobal);
                      UnmanagedMemoryStream^ ums = gcnew UnmanagedMemoryStream((unsigned char \*)pMyGlobalMemory, GlobalSize(MyHGlobal));
                      ms->WriteTo(ums);
                      GlobalUnlock(MyHGlobal);
                      

                      Mark Salsbery Microsoft MVP - Visual C++ :java:

                      A Offline
                      A Offline
                      alleyes 0
                      wrote on last edited by
                      #25

                      NotSupportedException unhandled. Stream does not support writing? Since when? :confused:

                      M 1 Reply Last reply
                      0
                      • A alleyes 0

                        NotSupportedException unhandled. Stream does not support writing? Since when? :confused:

                        M Offline
                        M Offline
                        Mark Salsbery
                        wrote on last edited by
                        #26

                        Sorry try another constructor: ...gcnew UnmanagedMemoryStream((unsigned char *)pMyGlobalMemory, GlobalSize(MyHGlobal), GlobalSize(MyHGlobal), FileAccess.ReadWrite);

                        Mark Salsbery Microsoft MVP - Visual C++ :java:

                        A 1 Reply Last reply
                        0
                        • M Mark Salsbery

                          Sorry try another constructor: ...gcnew UnmanagedMemoryStream((unsigned char *)pMyGlobalMemory, GlobalSize(MyHGlobal), GlobalSize(MyHGlobal), FileAccess.ReadWrite);

                          Mark Salsbery Microsoft MVP - Visual C++ :java:

                          A Offline
                          A Offline
                          alleyes 0
                          wrote on last edited by
                          #27

                          Ugh! Still getting an AccessViolation Exception. That can either mean not enough memory available or is that this memory can't be written to. I see the CanWrite, CanSeek flags are TRUE. Capacity and Length are both the same - 1658934

                          modified on Thursday, January 21, 2010 9:22 AM

                          M 1 Reply Last reply
                          0
                          • A alleyes 0

                            Ugh! Still getting an AccessViolation Exception. That can either mean not enough memory available or is that this memory can't be written to. I see the CanWrite, CanSeek flags are TRUE. Capacity and Length are both the same - 1658934

                            modified on Thursday, January 21, 2010 9:22 AM

                            M Offline
                            M Offline
                            Mark Salsbery
                            wrote on last edited by
                            #28

                            Did you use FileAccess.ReadWrite? I'm only guessing here since I don't know what line the exception occurs on...

                            Mark Salsbery Microsoft MVP - Visual C++ :java:

                            A 1 Reply Last reply
                            0
                            • M Mark Salsbery

                              Did you use FileAccess.ReadWrite? I'm only guessing here since I don't know what line the exception occurs on...

                              Mark Salsbery Microsoft MVP - Visual C++ :java:

                              A Offline
                              A Offline
                              alleyes 0
                              wrote on last edited by
                              #29

                              Hi, This is what I have so far:

                              MemoryStream^ ms = gcnew MemoryStream();
                              MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp);
                              ms->Seek(0, SeekOrigin::Begin);

                              if (GlobalSize(MyGlobal) < ms->Length)
                              {
                              GlobalReAlloc(MyGlobal, (LONG)ms->Length, 0);
                              }

                              void* pMyGlobal = (UCHAR*)MyGlobal;
                              UnmanagedMemoryStream^ ums = gcnew UnmanagedMemoryStream((UCHAR*)pMyGlobal, GlobalSize(MyGlobal), GlobalSize(MyGlobal), FileAccess::ReadWrite);
                              ums->Position = 0;
                              ms->WriteTo(ums);

                              It occurs on the line: ms->WriteTo(ums); This is even without setting the Position of the streams. I did this because just before the Call to WriteTo, the position was set to Length.

                              M 1 Reply Last reply
                              0
                              • A alleyes 0

                                Hi, This is what I have so far:

                                MemoryStream^ ms = gcnew MemoryStream();
                                MyImage->Save(ms, System::Drawing::Imaging::ImageFormat::Bmp);
                                ms->Seek(0, SeekOrigin::Begin);

                                if (GlobalSize(MyGlobal) < ms->Length)
                                {
                                GlobalReAlloc(MyGlobal, (LONG)ms->Length, 0);
                                }

                                void* pMyGlobal = (UCHAR*)MyGlobal;
                                UnmanagedMemoryStream^ ums = gcnew UnmanagedMemoryStream((UCHAR*)pMyGlobal, GlobalSize(MyGlobal), GlobalSize(MyGlobal), FileAccess::ReadWrite);
                                ums->Position = 0;
                                ms->WriteTo(ums);

                                It occurs on the line: ms->WriteTo(ums); This is even without setting the Position of the streams. I did this because just before the Call to WriteTo, the position was set to Length.

                                M Offline
                                M Offline
                                Mark Salsbery
                                wrote on last edited by
                                #30

                                Where did this come from? void* pMyGlobal = (UCHAR*)MyGlobal; Casting a handle to a pointer is bad. For an HGLOBAL it's bad unless you know for sure GMEM_FIXED was used to allocate the memory. Much safer to use GlobalLock to get the pointer.

                                Mark Salsbery Microsoft MVP - Visual C++ :java:

                                A 1 Reply Last reply
                                0
                                • M Mark Salsbery

                                  Where did this come from? void* pMyGlobal = (UCHAR*)MyGlobal; Casting a handle to a pointer is bad. For an HGLOBAL it's bad unless you know for sure GMEM_FIXED was used to allocate the memory. Much safer to use GlobalLock to get the pointer.

                                  Mark Salsbery Microsoft MVP - Visual C++ :java:

                                  A Offline
                                  A Offline
                                  alleyes 0
                                  wrote on last edited by
                                  #31

                                  Sorry I threw you there. The lock is already done earlier during the creating of the original images. Didn't make sense to call another GlobalLock on it again. The call to GlobalAlloc is GlobalAlloc(GMEM_MOVEABLE, Size) You have to call GlobalLock on the existing memory again?

                                  M 2 Replies Last reply
                                  0
                                  • A alleyes 0

                                    Sorry I threw you there. The lock is already done earlier during the creating of the original images. Didn't make sense to call another GlobalLock on it again. The call to GlobalAlloc is GlobalAlloc(GMEM_MOVEABLE, Size) You have to call GlobalLock on the existing memory again?

                                    M Offline
                                    M Offline
                                    Mark Salsbery
                                    wrote on last edited by
                                    #32

                                    alleyes wrote:

                                    The lock is already done earlier during the creating of the original images. Didn't make sense to call another GlobalLock on it again. The call to GlobalAlloc is GlobalAlloc(GMEM_MOVEABLE, Size)

                                    Then you need to use the pointer returned by the GlobalLock() call.

                                    Mark Salsbery Microsoft MVP - Visual C++ :java:

                                    1 Reply Last reply
                                    0
                                    • A alleyes 0

                                      Sorry I threw you there. The lock is already done earlier during the creating of the original images. Didn't make sense to call another GlobalLock on it again. The call to GlobalAlloc is GlobalAlloc(GMEM_MOVEABLE, Size) You have to call GlobalLock on the existing memory again?

                                      M Offline
                                      M Offline
                                      Mark Salsbery
                                      wrote on last edited by
                                      #33

                                      alleyes wrote:

                                      Didn't make sense to call another GlobalLock on it again.

                                      There's nothing wrong with locking again as long as you have an equal number of unlock calls. GlobalRealloc on a locked handle can only reallocate memory in the same place so has more chance of failing, since there may not be enough room in that place. For best results the handle should be unlocked before reallocating then locked again when you need the pointer.

                                      Mark Salsbery Microsoft MVP - Visual C++ :java:

                                      A 1 Reply Last reply
                                      0
                                      • M Mark Salsbery

                                        alleyes wrote:

                                        Didn't make sense to call another GlobalLock on it again.

                                        There's nothing wrong with locking again as long as you have an equal number of unlock calls. GlobalRealloc on a locked handle can only reallocate memory in the same place so has more chance of failing, since there may not be enough room in that place. For best results the handle should be unlocked before reallocating then locked again when you need the pointer.

                                        Mark Salsbery Microsoft MVP - Visual C++ :java:

                                        A Offline
                                        A Offline
                                        alleyes 0
                                        wrote on last edited by
                                        #34

                                        Sincere thanks Mark :-D When I have the time, I think I'll do all this in managed code by Marshalling the native HGLOBAL. But for now it works well.

                                        1 Reply Last reply
                                        0
                                        Reply
                                        • Reply as topic
                                        Log in to reply
                                        • Oldest to Newest
                                        • Newest to Oldest
                                        • Most Votes


                                        • Login

                                        • Don't have an account? Register

                                        • Login or register to search.
                                        • First post
                                          Last post
                                        0
                                        • Categories
                                        • Recent
                                        • Tags
                                        • Popular
                                        • World
                                        • Users
                                        • Groups