Problema con BitBlt y C++/CLI

Iniciado por SARGE553413, 17 Octubre 2014, 21:42 PM

0 Miembros y 1 Visitante están viendo este tema.

SARGE553413

Hola a todos.

Tengo que usar la función BitBlt() en código C++/CLI. Lo he intentado de muchas formas y no consigo que funcione. Me da el error código 6 (invalid handle).
He leído bastantes posts etc. por internet, y para empezar veo que nadie que use esta función tiene que hacer un casting de IntPtr a HDC. Adjunto el código (comentado en inglés)


/*
1 - Get a bmp image from hard drive( i have it).

2 - Copy it to byte array, or get its internal array, i have tested both ways (method1 and method2 in comments).

3 - Prepare a PicutreBox and obtain from it my context device.

4 - Use BitBlt function to copy only first 50 lines of the initial bitmap.

5 - Display it in a windows form.
*/

#include "stdafx.h"
#include <Windows.h>
#include "MyForm.h"
#include "string.h"

using namespace System;
using namespace System::Runtime::InteropServices;
using namespace System::Runtime::CompilerServices;
using namespace System::IO;
using namespace System::Windows::Forms;
using namespace System::Drawing;
using namespace System::Drawing::Imaging;

using namespace ConsoleApplication1;

[DllImport("gdi32.dll", CallingConvention = CallingConvention::StdCall, SetLastError = true)]
extern bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight,
IntPtr hdcSrc, int nXSrc, int nYSrc, long dwRop);

int main(array<System::String ^> ^args){
Graphics ^gph = nullptr;
try{
//Get a test image and it's parameters
Bitmap ^image = gcnew Bitmap("./img/Simple_tux0.bmp");
int iWidth = image->Width;
int iHeigth = image->Height;
PixelFormat pxF = image->PixelFormat;


//Prepare a PictureBo, whici will be given as parameter to Windows Form.
PictureBox ^pb = gcnew PictureBox();
pb->SizeMode = PictureBoxSizeMode::StretchImage;
pb->Size = Drawing::Size(iWidth + 10, iHeigth + 10);
pb->Location = Drawing::Point(3, 3);

//Get bitmap
IntPtr prueba = image->GetHbitmap();

//Create my device context, where i will paint my lines.
gph = pb->CreateGraphics();
IntPtr deviceContext = gph->GetHdc();


//Paint only 50 lines of the source Bitmap.
BOOL b = BitBlt((HDC)deviceContext.ToPointer(), 0, 0, iWidth, 50,
(HDC)prueba.ToPointer(), 0, 0, SRCCOPY);

//Get the error:
unsigned long err = GetLastError();
Console::WriteLine("error: {0}", err); //error 6 : Invalid Handle

//Create windows form with the PictureBox to display my 50 lines.
MyForm ^f = gcnew MyForm(pb);
Application::Run(f);
Application::Exit();
}
finally{
if (gph != nullptr){
gph->ReleaseHdc();
delete gph;
}

}
return 0;
}


He intentado usar también esto:


[DllImport("coredll.dll", EntryPoint = "CreateCompatibleDC")]
extern IntPtr CreateCompatibleDC(IntPtr hdc);

[DllImport("coredll.dll", EntryPoint = "GetDC")]
extern IntPtr GetDC(IntPtr hwnd);

int main(){
//...
//...
IntPtr dc = GetDC(deviceContext);
IntPtr compDc = CreateCompatibleDC(dc);

//Paint only 50 lines of the source Bitmap.
BOOL b = BitBlt((HDC)compDc.ToPointer(), 0, 0, iWidth, 50,
(HDC)prueba.ToPointer(), 0, 0, SRCCOPY);


La primera pregunta que me surge es por qué yo tengo que hacer esto:

(HDC)deviceContext.ToPointer()

Y en todos los post que leo nadie lo hace.

Saludos, gracias.

kub0x

Muy buenas SARGE553413,

¿los posts que has revisado por internet son de C++/CLI? IntPtr es una clase del Framework, lo cual no pertenece a la WinAPI y por lo tanto no se puede castear a HDC, por lo cual debes de optener un pointer, a void (void*) supongo, y luego castear a HDC que realmente es lo que estás haciendo.

Aun así podrías hacer override al evento WM_PAINT del PictureBox, dentro de ahí haces la llamada a la siguiente función, la cual dibuja tu imagen:

Código (cpp) [Seleccionar]
void FromImageImage( PaintEventArgs^ e )
   {
      // Create image.
      Image^ imageFile = Image::FromFile( "tuimagen.jpg" );

      // Create graphics object for alteration.
      Graphics^ newGraphics = Graphics::FromImage( imageFile );

      // Alter image.
      newGraphics->FillRectangle( gcnew SolidBrush( Color::Black ), 100, 50, 100, 100 );

      // Draw image to screen.
      e->Graphics->DrawImage( imageFile, PointF(0.0F,0.0F) );

      // Dispose of graphics object.
      delete newGraphics;
   }


Saludos!
Viejos siempre viejos,
Ellos tienen el poder,
Y la juventud,
¡En el ataúd! Criaturas Al poder.

Visita mi perfil en ResearchGate


SARGE553413

#2
Hola de nuevo, respecto al IntPtr, tiene un método toPointer() que devuelve void*, y HDC es un void* realmente.
De todas formas ya he solucionado el error, el problema es que ahora no veo por pantalla lo que dibujo.
Adjunto el nuevo código en el que BitBlt() da error 0 (todo ok), por algún motivo el PictureBox no se actualiza bien:

Código (cpp) [Seleccionar]

[DllImport("gdi32.dll", CallingConvention = CallingConvention::StdCall, SetLastError = true)]
extern bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight,
IntPtr hdcSrc, int nXSrc, int nYSrc, long dwRop);

int main(array<System::String ^> ^args){
Graphics ^gph = nullptr;
try{
//Get a test image and it's parameters
Bitmap ^image = gcnew Bitmap("./img/Simple_tux0.bmp");
int iWidth = image->Width;
int iHeigth = image->Height;
PixelFormat pxF = image->PixelFormat;

//Prepare a PictureBox, which will be given as parameter to Windows Form.
//It has an image that's my "draw area".
PictureBox ^pb = gcnew PictureBox();
pb->SizeMode = PictureBoxSizeMode::Normal;
pb->Size = Drawing::Size(iWidth, iHeigth);
pb->Location = Drawing::Point(0, 0);
Bitmap ^drawArea = gcnew Bitmap(pb->Width, pb->Height);
pb->Image = drawArea;

//Get bitmap
IntPtr prueba = image->GetHbitmap();

//Create my device context, where i will paint my lines.
gph = Graphics::FromImage(drawArea);
IntPtr deviceContext = gph->GetHdc();


//Paint only 50 lines of the source Bitmap.
BOOL b = BitBlt((HDC)deviceContext.ToPointer(), 0, 0, iWidth, 50,
(HDC)prueba.ToPointer(), 0, 0, SRCCOPY);

//Get the error:
unsigned long err = GetLastError();
Console::WriteLine("error: {0}", err); //error 6 : Invalid Handle

//Refresh
pb->Invalidate();
pb->Refresh();

//Create windows form with the PictureBox to display my 50 lines.
MyForm ^f = gcnew MyForm(pb);
Application::Run(f);
Application::Exit();
}
finally{
if (gph != nullptr){
gph->ReleaseHdc();
delete gph;
}

}
return 0;
}


¿Qué hago mal?

Saludos, gracias.

kub0x

Cita de: SARGE553413 en 19 Octubre 2014, 16:00 PM
Hola de nuevo, respecto al IntPtr, tiene un método toPointer() que devuelve void*, y HDC es un void* realmente.

Exactamente igual que lo que te dije arriba. Si no encuentras posts por la red que usen toPointer() es que no es C++/CLI, ya que HDC es unmanaged e IntPtr es managed.

Por lo demás el code está bien, quizá te falte el añadir el PictureBox al formulario, espero sea eso, pues error 0 indica que todo fue bien.

Código (cpp) [Seleccionar]

f->Controls->Add(pb);


Aun así el otro código que te dí es funcional, haciendo override al WM_PAINT.

Saludos!
Viejos siempre viejos,
Ellos tienen el poder,
Y la juventud,
¡En el ataúd! Criaturas Al poder.

Visita mi perfil en ResearchGate