Screenshot code gives unexpected error on call to GetDIBits - c

I'm using some C code to take a screenshot of a window, translated directly from a PowerBuilder sample that works perfectly. Here's the part up to where there's a problem:
extern "C" __declspec(dllexport) BOOL __stdcall WindowScreenShot(const wchar_t* fileName, unsigned long x, unsigned long y,
unsigned long width, unsigned long height)
{
HWND ll_hWnd;
HDC ll_hdc, ll_hdcMem;
HBITMAP ll_hBitmap;
HANDLE hDib = NULL, hFile = NULL;
char* lpBitmap = NULL;
BOOL lb_result, lb_ok = FALSE;
BITMAPINFO lstr_Info;
BITMAPFILEHEADER lstr_Header;
int li_pixels;
DWORD dwBmpSize, dwBytesWritten;
// get handle to windows background
ll_hWnd = GetDesktopWindow();
// Get the device context of window and allocate memory
ll_hdc = GetDC(ll_hWnd);
ll_hdcMem = CreateCompatibleDC(ll_hdc);
ll_hBitmap = CreateCompatibleBitmap(ll_hdc, width, height);
if (ll_hBitmap != 0)
{
// Select an object into the specified device context
SelectObject(ll_hdcMem, ll_hBitmap);
// Copy the bitmap from the source to the destination
lb_result = BitBlt(ll_hdcMem, 0, 0, width, height, ll_hdc, x, y, SRCCOPY);
lstr_Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
// Get the bitmapinfo (THIS LINE IS FAILING CURRENTLY)
if (GetDIBits(ll_hdcMem, ll_hBitmap, 0, height, NULL, &lstr_Info, DIB_RGB_COLORS) > 0)
{ ...
That last call to GetDIBits always fails with the only documented error that it can give, ERROR_INVALID_PARAMETER. Since this code is basically absolutely identical to the PowerBuilder code, in terms of the structures used and Windows APIs called, I just have no idea how to resolve it. I've also read the API docs carefully and it all looks like it should work.
Any bright ideas? Thanks.

Paul Ogilvie's answer resolved it - I was using uninitialized structures because they were automatic variables. As soon as I fixed that the code worked.

Related

Simple C Program that creates 2 X11 windows

I want to create 2 windows in linux that I'll later draw in from a separate thread. I currently have a non-deterministic bug where the second window that I create sometimes doesn't get created (no errors though).
Here is the code.
static void create_x_window(Display *display, Window *win, int width, int height)
{
int screen_num = DefaultScreen(display);
unsigned long background = WhitePixel(display, screen_num);
unsigned long border = BlackPixel(display, screen_num);
*win = XCreateSimpleWindow(display, DefaultRootWindow(display), /* display, parent */
0,0, /* x, y */
width, height, /* width, height */
2, border, /* border width & colour */
background); /* background colour */
XSelectInput(display, *win, ButtonPressMask|StructureNotifyMask);
XMapWindow(display, *win);
}
int main(void) {
XInitThreads(); // prevent threaded XIO errors
local_display = XOpenDisplay(":0.0");
Window self_win, remote_win;
XEvent self_event, remote_event;
create_x_window(local_display, &remote_win, 640,480);
// this line flushes buffer and blocks so that the window doesn't crash for a reason i dont know yet
XNextEvent(local_display, &remote_event);
create_x_window(local_display, &self_win, 320, 240);
// this line flushes buffer and blocks so that the window doesn't crash for a reason i dont know yet
XNextEvent(local_display, &self_event);
while (1) {
}
return 0;
}
I don't really care for capturing input in the windows, but I found a tutorial that had XSelectInput and XNextEvent (in an event loop) and I was having trouble making this work without either.
It's not a bug, it's a feature. You left out the event loop.
Although you cleverly called XNextEvent twice, the X protocol is asynchronous so the server may still be setting up the actual window while you call XNextEvent, so there is nothing to do.
Tutorial here.

Void to HBITMAP conversion Issue

I keep getting an error at line 123:
123 C:\Dev-Cpp\Window_main_2.c invalid conversion from `void*' to `HBITMAP__*'
I don't know what to make of this and its driving me crazy.
void DrawBitmap(HDC hdcDest, char *filename, int x, int y)
{
HBITMAP image;
BITMAP bm;
HDC hdcMem;
// This is the line that brings about the issue (just ask me if more code is required because
// there is a lot more. Essentially this whole function points to a file and I call this
// function in another function that will compile a windows screen filled with the following
// image path. But I cant get this HBITMAP to agree with the image datatype. Please let me
// know if more info is required and thank you.) The line is below.
image = LoadImage(0, "C:\\Users\\Lillian\\Pictures\\c.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
GetObject(image, sizeof(BITMAP), &bm);
hdcMem = CreateCompatibleDC(global_hdc);
SelectObject(hdcMem, image);
BitBlt(
global_hdc,
x,
y,
bm.bmWidth,
bm.bmHeight,
hdcMem,
0,
0,
SRCCOPY);
DeleteDC(hdcMem);
DeleteObject((HBITMAP)image);
}
LoadImage() returns a HANDLE. Your need a cast when assigning the result to your variable:
image = (HBITMAP) LoadImage(0, "C:\\Users\\Lillian\\Pictures\\c.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
Also, before you DeleteDC(), you need to select the original HBITMAP back into hdcMem - you need to save it when you call SelectObject() earlier.

Using GDI+ in C - gdiPlusStartup function returning 2

I am attempting to use GDI+ in my C application to take a screenshot and save it as JPEG. I am using GDI+ to convert the BMP to JPEG but apparently when calling the GdiplusStartup function, the return code is 2(invalid parameter) instead of 0:
int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
//if(GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL) != 0)
// printf("GDI NOT WORKING\n");
printf("%d",GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL));
HDC hdc = GetDC(NULL); // get the desktop device context
HDC hDest = CreateCompatibleDC(hdc); // create a device context to use yourself
// get the height and width of the screen
int height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
int width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
// create a bitmap
HBITMAP hbDesktop = CreateCompatibleBitmap( hdc, width, height);
// use the previously created device context with the bitmap
SelectObject(hDest, hbDesktop);
// copy from the desktop device context to the bitmap device context
// call this once per 'frame'
BitBlt(hDest, 0,0, width, height, hdc, 0, 0, SRCCOPY);
// after the recording is done, release the desktop context you got..
ReleaseDC(NULL, hdc);
// ..and delete the context you created
DeleteDC(hDest);
SaveJpeg(hbDesktop,"a.jpeg",100);
GdiplusShutdown(gdiplusToken);
return 0;
}
I am trying to figure out why the GdiplusStartup function is not working.
Any thoughts?
Initialize gdiplusStartupInput variable with the following values: GdiplusVersion = 1, DebugEventCallback = NULL, SuppressBackgroundThread = FALSE, SuppressExternalCodecs = FALSE
According to MSDN article GdiplusStartup function http://msdn.microsoft.com/en-us/library/windows/desktop/ms534077%28v=vs.85%29.aspx
GdiplusStartupInput structure has default constructor which initializes the structure with these values. Since you call the function from C, constructor is not working and structure remains uninitialized. Provide your own initialization code to solve the problem.
// As Global
ULONG_PTR gdiplusToken;
// In top of main
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&programInfo.gdiplusToken, &gdiplusStartupInput, NULL);
works for me.

How could I draw a RGB HBITMAP and display it in a STATIC WINDOW using C?

HBITMAP DisplayMap(HDC hThisDC){
HDC hDC=CreateCompatibleDC(hThisDC);
BITMAPINFOHEADER bi;
bi.biSize=sizeof(BITMAPINFOHEADER);
bi.biWidth=670;
bi.biHeight=540;
bi.biPlanes=1;
bi.biBitCount=24;
bi.biCompression=BI_RGB;
int lineSize=((((bi.biWidth*bi.biBitCount)+31)&~31)>>3);
bi.biSizeImage=lineSize*bi.biHeight;
unsigned char* data=(unsigned char*)malloc(bi.biSizeImage);
int off=0;
for(int y=0;y<bi.biHeight;y++){
for(int x=0;x<bi.biWidth;x++){
data[off+3*x]=255;
data[off+3*x+1]=0;
data[off+3*x+2]=127;
}
off+=lineSize;
}
HBITMAP hBitmap=CreateDIBitmap(hDC,&bi,CBM_INIT,data,(BITMAPINFO*)&bi,DIB_RGB_COLORS);
SetDIBits(hDC,hBitmap,0,bi.biHeight,data,(BITMAPINFO*)&bi,DIB_RGB_COLORS);
return hBitmap;
}
//
HBITMAP hBitmap=DisplayMap(GetDC(hwnd));
if(hBitmap==NULL||true){
char* str=(char*)malloc(15);
sprintf(str,"ERROR: %d!",GetLastError());
MessageBox(NULL,str,"ALERT",MB_OK|MB_ICONEXCLAMATION);
}
ShowWindow (hwnd, nFunsterStil);
SendMessage(hFrame, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap);
//
If I use "(HBITMAP)LoadImage()" to display it, it shows no problem. But as I'm using my function "DisplayMap()" which is supposed to create a HBITMAP out of nothing it doesn't work. The script shows no erros and the "hBitmap" return a valid HBITMAP (not NULL), but don't matter the color I choose to fill the bitmap, it keeps displaying the same full-black image.
I searched for a solution in Google through many sources and I wasn't able to figure out the problem. I thank for any help, has been a long time I don't work with c.The idea is to create and display an image sequence which shall be generate just in time without loading anything from files.
Why do you GetDC from hwnd, but STM_SETIMAGE to hFrame. Try GetDC from hFrame.

WinAPI get mouse cursor icon

I want to get the cursor icon in Windows.
I think language I use isn't very important here, so I will just write pseudo code with WinAPI functions I'm trying to use:
c = CURSORINFO.new(20, 1, 1, POINT.new(1,1));
GetCursorInfo(c); #provides correctly filled structure with hCursor
DrawIcon(GetWindowDC(GetForegroundWindow()), 1, 1, c.hCursor);
So this part works fine, it draws current cursor on active window.
But that's not what I want. I want to get an array of pixels, so I should draw it in memory.
I'm trying to do it like this:
hdc = CreateCompatibleDC(GetDC(0)); #returns non-zero int
canvas = CreateCompatibleBitmap(hdc, 256, 256); #returns non-zero int too
c = CURSORINFO.new(20, 1, 1, POINT.new(1,1));
GetCursorInfo(c);
DrawIcon(hdc, 1, 1, c.hCursor); #returns 1
GetPixel(hdc, 1, 1); #returns -1
Why doesn't GetPixel() return COLORREF? What am I missing?
I'm not very experienced with WinAPI, so I'm probably doing some stupid mistake.
You have to select the bitmap you create into the device context. If not, the GetPixel function will return CLR_INVALID (0xFFFFFFFF):
A bitmap must be selected within the device context, otherwise, CLR_INVALID is returned on all pixels.
Also, the pseudo-code you've shown is leaking objects badly. Whenever you call GetDC, you must call ReleaseDC when you're finished using it. And whenever you create a GDI object, you must destroy it when you're finished using it.
Finally, you appear to be assuming that the coordinates for the point of origin—that is, the upper left point—are (1, 1). They are actually (0, 0).
Here's the code I would write (error checking omitted for brevity):
// Get your device contexts.
HDC hdcScreen = GetDC(NULL);
HDC hdcMem = CreateCompatibleDC(hdcScreen);
// Create the bitmap to use as a canvas.
HBITMAP hbmCanvas = CreateCompatibleBitmap(hdcScreen, 256, 256);
// Select the bitmap into the device context.
HGDIOBJ hbmOld = SelectObject(hdcMem, hbmCanvas);
// Get information about the global cursor.
CURSORINFO ci;
ci.cbSize = sizeof(ci);
GetCursorInfo(&ci);
// Draw the cursor into the canvas.
DrawIcon(hdcMem, 0, 0, ci.hCursor);
// Get the color of the pixel you're interested in.
COLORREF clr = GetPixel(hdcMem, 0, 0);
// Clean up after yourself.
SelectObject(hdcMem, hbmOld);
DeleteObject(hbmCanvas);
DeleteDC(hdcMem);
ReleaseDC(hdcScreen);
But one final caveat—the DrawIcon function will probably not work as you expect. It is limited to drawing an icon or cursor at the default size. On most systems, that will be 32x32. From the documentation:
DrawIcon draws the icon or cursor using the width and height specified by the system metric values for icons; for more information, see GetSystemMetrics.
Instead, you probably want to use the DrawIconEx function. The following code will draw the cursor at the actual size of the resource:
DrawIconEx(hdcMem, 0, 0, ci.hCursor, 0, 0, 0, NULL, DI_NORMAL);

Resources