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.
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:
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!
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:
[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.
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.
f->Controls->Add(pb);
Aun así el otro código que te dí es funcional, haciendo override al WM_PAINT.
Saludos!