WinAPI text output in C - c

I'm fairly new to WINAPI, and I need some help doing text output. I have an array of pixels that I write to with functions and then periodically blit onto the screen using the following functions:
DWORD WINAPI tickThreadProc(HANDLE handle) {
ShowWindow( hwnd, SW_SHOW );
HDC hdc = GetDC( hwnd );
hdcMem = CreateCompatibleDC( hdc );
HBITMAP hbmOld = (HBITMAP)SelectObject( hdcMem, hbmp );
int delay = 1000 / fps;
InitPhys();
LoadIMGs();
for ( ;; ) {
onFrame( pixels );
BitBlt( hdc, gLeft, gTop, width, height, hdcMem, 0, 0, SRCCOPY );
// Wait
Sleep( delay );
// Physics
SimPhys();
}
SelectObject( hdcMem, hbmOld );
DeleteDC( hdc );
return 0;
}
void MakeSurface(HWND hwnd) {
BITMAPINFO bmi;
bmi.bmiHeader.biSize = sizeof(BITMAPINFO);
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -height; // Order pixels from top to bottom
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32; // last byte not used, 32 bit for alignment
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = 0;
bmi.bmiHeader.biXPelsPerMeter = 0;
bmi.bmiHeader.biYPelsPerMeter = 0;
bmi.bmiHeader.biClrUsed = 0;
bmi.bmiHeader.biClrImportant = 0;
bmi.bmiColors[0].rgbBlue = 0;
bmi.bmiColors[0].rgbGreen = 0;
bmi.bmiColors[0].rgbRed = 0;
bmi.bmiColors[0].rgbReserved = 0;
HDC hdc = GetDC( hwnd );
// Create DIB section to always give direct access to pixels
hbmp = CreateDIBSection( hdc, &bmi, DIB_RGB_COLORS, (void**)&pixels, NULL, 0 );
DeleteDC( hdc );
// Create a new thread to use as a timer
hTickThread = CreateThread( NULL, 0, &tickThreadProc, NULL,0, NULL );
}
This is modified off some code I found on the internet. The pixel struct has 4 ints for r, g, b, and a.
I need to do text output and loading a picture for text is impractical. Any help?

First of all, if you use GetDC to get a handle to device context, you must use ReleaseDC when you're done with it. DeleteDC is only for device contexts that you created.
To draw text to this window, you can use functions like TextOut or DrawText using that DC (before you release it).
PAINTSTRUCT is for handling WM_PAINT messages (which is the more common way to draw to a Window). It looks like you're instead trying to draw directly from another thread on a regular basis. GDI isn't very good at dealing with multiple threads, so you might have some problems with this approach. But if your BitBlts are working, then a TextOut should work as well.

Related

Why is window creation slow for many windows? Can it be faster?

For an experiment I want to create lots of small windows. I mean a lot, like a thousand or so.
The windows are small, containing some labels only (AB):
I created a hundred of them as an experiment, but their display is not instantaneous, it is visible as they are put on the screen. Why is that?
I expected for a C/C++ program to be very fast, so that I don't see the windows put on the screen at all. Is it a wrong expectation? Or should I use some kind of lighter window type (I'm no Windows-programmer, so I'm just guessing) which can be put up much faster?
Here's the relevant part of the code:
HWND parent = 0;
for (int i = 0; i < 100; ++i)
{
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_BORDER,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
if (parent == 0)
parent = hWnd;
else
SetWindowLong(hWnd, GWL_HWNDPARENT, (long)parent);
SetWindowLong(hWnd, GWL_STYLE, 0);
SetMenu(hWnd, NULL);
SetWindowPos(hWnd, HWND_TOP, 100 + (i * 20), 100, 20, 20, 0);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
}
...
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
SetBkColor(hdc, RGB(0,255,0));
TextOut(hdc, 1, 1, TEXT("AB"), strlen("AB"));
EndPaint(hWnd, &ps);
}
break;
SetWindowLong, SetMenu, SetWindowPos and ShowWindow can all be removed by giving the same information in the CreateWindowW arguments.
Then you can also remove the call to UpdateWindow.
Here its not the speed of C/C++ that matter, but the Win32 API calls which send windows messages.

WIN32 C Properly using SetWindowLongPtr and GetWindowLongPtr

Yes this is a homework assignment and I'm completely stumped.
So I've made a struct and two windows:
typedef struct thingy {
int count;
TCHAR* MSG;
COLORREF colour; };
The windows have:
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = sizeof(thingy*);
wndclass.cbWndExtra = sizeof(thingy*);
I need one window to display 0 and the next to display 1 using this struct stored in the clsextra using SetWindowLongPtr and GetWindowLongPtr/SetClassLongPtr and GetClassLongPtr
Count of course has to be initialized to 0 for the FIRST window but not for the second and I have no idea how to do this. Only one WndProc can be used to do this.
static thingy* mythingy = (thingy*)GetWindowLongPtr(hwnd, 0);
char buf[128];
int num = GetClassLongPtr(hwnd, 0);
static boolean set = false;
case WM_CREATE:
if (!set) {
mythingy = (thingy*)malloc(sizeof(thingy));
mythingy->count = 0;
mythingy->colour = RGB(0, 0, 0);
mythingy->MSG = TEXT("Hello Windows!");
set = true;
}
if (lParam != NULL) {
SetClassLongPtr(hwnd, 0, (LONG)mythingy->count);
}
mythingy->count++;
SetWindowLongPtr(hwnd, 0, (LONG)mythingy);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
DrawText(hdc, mythingy->MSG, -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
sprintf_s(buf, "%d", num);
TextOut(hdc, 0, 0, LPCWSTR(buf), 1);
EndPaint(hwnd, &ps);
return 0;
Right now both windows display 1 and I'm struggling to see why it isn't doing what I want as I can't find anything on Google about how to use these two functions or when I need to be calling them.
Window: 0x000a0528
count = 0
Add to class data
Window: 0x001f099a
count = 1
Add to class data
From the paint method I get the data and both of them are 1.
A Window Class
... is a set of attributes that the system uses as a template to create a window. Every window is a member of a window class.
Since the Windows API is exposed as a flat C interface, there is no inheritance at the language level. The phrase "is a member of" is implemented by sharing the class memory across window instances of that class. Consequently, every call to GetClassLongPtr accesses the same shared memory.
In contrast, each window can reserve cbWndExtra bytes of memory, that are attributed to the specific window instance. This memory is private to each window, and can store per-window data.
To implement your requirements you need to store the common information (current count of windows) in the window class' extra memory (cbClsExtra), and keep the per-window data (index, message, and color) in the window instance's extra memory (cbWndExtra).
Apply the following changes to your code:
// Total count of windows stored as an integer:
wndclass.cbClsExtra = sizeof(int);
In the WM_CREATE-handler, set the per-window data, increment the total count, and store it away:
case WM_CREATE:
{
int count = (int)GetClassLongPtr(hwnd, 0);
// Allocate new per-window data object:
thingy* mythingy = (thingy*)malloc(sizeof(thingy));
mythingy->count = count;
mythingy->colour = RGB(0, 0, 0);
mythingy->MSG = TEXT("Hello Windows!");
// Store the per-window data:
SetWindowLongPtr(hwnd, 0, (LONG_PTR)mythingy);
// Increment total count and store it in the class extra memory:
++count;
SetClassLongPtr(hwnd, 0, (LONG_PTR)count);
}
return DefWindowProc(hwnd, msg, wParam, lParam);
In the WM_PAINT-handler, access the per-window data:
case WM_PAINT:
{
PAINTSTRUCT ps;
hdc = BeginPaint(hwnd, &ps);
RECT rect;
GetClientRect(hwnd, &rect);
// Retrieve per-window data:
thingy* mythingy = (thingy*)GetWindowLongPtr(hwnd, 0);
DrawText(hdc, mythingy->MSG, -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
char buf[128];
sprintf_s(buf, "%d", mythingy->count);
TextOutA(hdc, 0, 0, buf, 1);
EndPaint(hwnd, &ps);
return 0;
}
Note: All error handling has been elided for brevity. Character-encoding issues have not really been addressed either (char vs. wchar_t). Likewise, resource management is missing. You'd probably want to deallocate memory in a WM_NCDESTROY-handler. The code assumes, that only windows of a single window class are created.

Create a bitmap in memory and use it

My goal is to create a bitmap in memory and use its handle as a parameter of the BM_SETIMAGE message (a message that set a button's bitmap).
The following is my code:
........
HDC hdc = GetDC(hwnd);
HDC memDC = CreateCompatibleDC(hdc);
HBITMAP hMemBmp = CreateCompatibleBitmap(hdc, 100, 100);
HBITMAP hOldBmp = (HBITMAP)SelectObject(memDC, hMemBmp);
Rectangle(memDC, 0, 0, 100, 100);
HBRUSH brush = CreateSolidBrush(RGB(0xff, 0xff, 0x00));
RECT rc;
rc.left = 0;
rc.top = 0;
rc.right = 100;
rc.bottom = 100;
FillRect(memDC, &rc, brush);
SendMessage(GetDlgItem(hDlg, IDC_SET_START_PAGE_BG), BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hMemBmp);
........
but it doesn't work, I just got black color on my button.
Questions:
Does FillRect(memDC, &rc, brush) modify the content of hMemBmp? If not, what SelectObject(memDC, hMemBmp) did for these 2 objects?
Should I use CreateBitmap instead of CreateCompatibleBitmap to do my task?
You should select the old bitmap back into the memory DC before you send the message. According to MSDN a bitmap can be selected into only a single DC at a time.

hwnd to ppm issue

I have a function which save a hwnd into a ppm file.
This function is inspired by a msdn example.
Both the msdn sample and my function work but ... I have an issue ...
But first, here is the function.
int CaptureAnImage(HWND hWnd)
{
HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
RECT rc;
BITMAPINFOHEADER bi;
DWORD dwBmpSize;
HANDLE hDIB;
char *lpbitmap;
int w, h;
FILE *f;
// Retrieve the handle to a display device context for the client
// area of the window.
hdcWindow = GetDC(hWnd);
// Create a compatible DC which is used in a BitBlt from the window DC
hdcMemDC = CreateCompatibleDC(hdcWindow);
if(!hdcMemDC) {
MessageBox(hWnd, "CreateCompatibleDC has failed","Failed", MB_OK);
goto done;
}
// Get the client area for size calculation
GetClientRect(hWnd, &rc);
w = rc.right - rc.left;
h=rc.bottom-rc.top;
// Create a compatible bitmap from the Window DC
hbmScreen = CreateCompatibleBitmap(hdcWindow, w, h);
if(!hbmScreen) {
MessageBox(hWnd, "CreateCompatibleBitmap Failed","Failed", MB_OK);
goto done;
}
// Select the compatible bitmap into the compatible memory DC.
SelectObject(hdcMemDC,hbmScreen);
// Bit block transfer into our compatible memory DC.
if(!BitBlt(hdcMemDC,
0,0,
w, h,
hdcWindow,
0,0,
SRCCOPY)) {
MessageBox(hWnd, "BitBlt has failed", "Failed", MB_OK);
goto done;
}
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = w;
bi.biHeight = h;
bi.biPlanes = 1;
bi.biBitCount = 24;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
dwBmpSize = w*bi.biBitCount*h;
// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that
// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc
// have greater overhead than HeapAlloc.
hDIB = GlobalAlloc(GHND,dwBmpSize);
lpbitmap = (char *)GlobalLock(hDIB);
// Gets the "bits" from the bitmap and copies them into a buffer
// which is pointed to by lpbitmap.
GetDIBits(hdcWindow, hbmScreen, 0,
(UINT)h,
lpbitmap,
(BITMAPINFO *)&bi, DIB_RGB_COLORS);
f = fopen("./test.ppm", "wb");
if (!f) {
fprintf(stderr, "cannot create ppm file\n");
goto done;
}
fprintf(f, "P6\n%d %d\n255\n", w, h);
fwrite((LPSTR)lpbitmap, dwBmpSize, 1, f);
fclose(f);
//Unlock and Free the DIB from the heap
GlobalUnlock(hDIB);
GlobalFree(hDIB);
//Clean up
done:
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(hWnd,hdcWindow);
return 0;
}
So here is the resulting image:
http://imageshack.us/photo/my-images/853/test2ne.jpg/
As you can see, there is a problem in the width size. Maybe because of the border of the window ?
If in the code, I change "w = rc.right - rc.left;" into "w = rc.right - rc.left - 10;", it's better. But I don't understand why I have to put "-10" and ... some pixel are missing on the right of the picture (maybe 10 pixels ?)
http://imageshack.us/photo/my-images/207/test3jq.jpg
And the last question:
is there any way to ask to GetDIBits function to put my byte in the inverted order ?
I don't wand to do a pixel by pixel copy since it will cost some cpu time. (ok, you may say that since I'm saving this file to disk, then I should not be concerned by cpu time, but my goal is not to save this picture to the disk. I'm doing it for debug purpose only)
thanks in advance for any help
Your problem is that each row of image data in a DIB must be DWORD aligned (i.e. aligned on a multiple of 4 bytes).
dwBmpSize = w*bi.biBitCount*h;
This should actually be:
dwBmpSize = ((w*bi.biBitCount+3)&~3) *h;
You will then have to account for this when writing the PPM file.
Also, the image is upside down because by default DIBs are "bottom-up" (row 0 is at the bottom). To make it "top-down" set the biHeight field to a negative value.

c - win32 api rectangle is not drawn if hdc passed to method

I have been playing with C and win32 recently and the following has me balked:
case WM_PAINT:
g_crntRect = (RECT*) malloc(sizeof(RECT));
GetWindowRect(hwnd, g_crntRect);
hpen = CreatePen(PS_SOLID, 1, RGB(255,25,5));
hdc = BeginPaint (hwnd, &ps) ;
oldPen = SelectObject(hdc, hpen);
drawRects(hwnd, hdc);
//Rectangle(hdc, 0, 0, 840, 525);
SelectObject(hdc,oldPen);
DeleteObject(hpen);
EndPaint (hwnd, &ps) ;
return 0 ;
So, if I call my own method above for drawing rectangles, it draws nothing, however the call to draw the rectangle in the WM_PAINT that I have commented above succeeds without a problem.
Here is my method:
BOOL drawRects(HWND hwnd, HDC hdc)
{
char buffer[50];
BOOL res = FALSE;
RECT tempRect = {0};
char quadStr[6] = "";
int i = 0;
quadStr[i]='*';
OutputDebugString("Going to draw");
for (i = 1; i <= 4; i++)
{
//get rect for each quadrent from the parent
OutputDebugString("inside for");
getRect(g_crntRect, &tempRect, i);
OutputDebugString("got rectr");;
res = Rectangle(hdc, tempRect.right, tempRect.top, tempRect.right, tempRect.bottom);
if (res == FALSE)
{
OutputDebugString("false");;
sprintf(buffer, "Error: %ld", GetLastError());
OutputDebugString(buffer);
}
else
{
OutputDebugString("drew");;
}
quadStr[i]='*';
printRect(quadStr, &tempRect);
}
return TRUE;
}
Looking at debug output, everything seems fine. Proper values are being passed into the Rectangle method. However, I wonder if I am not passing HDC correctly?
Any ideas?
Looks like a simple typo. In your method you have:
res = Rectangle(hdc, tempRect.right, tempRect.top, tempRect.right, tempRect.bottom);
The second parameter should be tempRect.left not tempRect.right. You're trying to draw a zero-width rectangle.
Change
BOOL drawRects(HWND hwnd, HDC* hdc)
to
BOOL drawRects(HWND hwnd, HDC hdc)
A Windows handle is actually a pointer, so there is no need to pass it by reference. But if you do, you would need to call your function as drawRects(hwnd, &hdc);
In general you don't paint outside the WM_PAINT handler. If you want your window to be updated, just call InvalidateRect on the area you want to be redrawn. That will trigger the WM_PAINT call which will then repaint the window.
Is there a reason you need to paint the window outside the paint handler?

Resources