Saturday, January 31, 2009

How Do I Create a Window With Visual Styles?

The following code is a bare-bones application that loads a window with a button that creates a message box when pressed. It will enable visual styles on machines that support them. In order to get this to build in Visual Studio, create a new project, select "Win32 Console Application", then set the application type to "Windows Application" and check "Empty Project". In the project properties window, change Character Set to "Not Set" to disable unicode. The parts dealing with visual styles are highlighted in blue.


#pragma comment(lib, "comctl32.lib") // for visual styles
#pragma comment(linker, "/manifestdependency:\"type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' \
language='*'\"")

#include <windows.h>
#include <commctrl.h>
#define ID_OK 100

LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_OK:
if(HIWORD(wParam) == BN_CLICKED)
MessageBox(hwnd, "OK Button Pressed",
"Notification", MB_OK);
break;
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszArgument, int nCmdShow)
{
MSG msg;
HWND hwnd;
WNDCLASSEX wcx = {0};

InitCommonControls(); // enable visual styles

wcx.cbSize = sizeof(wcx);
wcx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.style = CS_HREDRAW | CS_VREDRAW;
wcx.lpszClassName = "MyWindowClass";
wcx.lpfnWndProc = MainWndProc;
wcx.hInstance = hInstance;
RegisterClassEx(&wcx);

hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, "MyWindowClass",
"My Window Title", WS_VISIBLE | WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 300, 200, NULL, NULL,
hInstance, NULL);

CreateWindow("BUTTON", "OK", WS_CHILD | WS_VISIBLE |
BS_PUSHBUTTON, 190, 120, 75, 25, hwnd, (HMENU)ID_OK,
hInstance, NULL);

while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return 0;
}

Saturday, May 24, 2008

How can I insert location dependent information in my debugging messages?

Visual Studio defines the symbols __LINE__ and __FILE__ and __FUNCTION__ so you can access the line number, source file, and function where they are inserted.

Why is my message box invisible until I press the ALT key?

This means that there is a bug in your program. One possibility is that you may be returning prematurely from your WM_PAINT or WM_SYSCOMMAND message handlers.

How do I wait for a process to terminate?

Suppose you want to execute a command line utility and wait for it to finish before continuing. Then you can construct the command line including the executable path at the beginning and call the following function.
int ExecAndWait(char *cmdline, char *workingdir)
{
STARTUPINFO startupinfo = {0};
PROCESS_INFORMATION procinfo = {0};

startupinfo.cb = sizeof(startupinfo);
if(CreateProcess(NULL, cmdline, NULL, NULL, FALSE,
CREATE_NO_WINDOW, NULL, workingdir,
&startupinfo, &procinfo) == 0)
return 0;

WaitForSingleObject(procinfo.hProcess, INFINITE);
return 1;
}

How do I find the path of the temp directory?

There is a simple function called GetTempPath().
DWORD WINAPI GetTempPath(DWORD nBufferLength, LPTSTR lpBuffer);
If you need to obtain a unique filename, you can use GetTempFileName().

Sunday, April 13, 2008

Why does ShowWindow cause a flicker?

When you call
ShowWindow(hwnd, SW_SHOW);
the WM_ERASEBKGND message is sent to the window procedure. If you are using your own custom paint routine, you probably don't need this. It will fill in your window with gray and then your paint routine will paint over it, but the gray fill appears briefly as a flicker. To prevent this, just override the WM_ERASEBKGND message handler.
case WM_ERASEBKGND:
return FALSE;

Friday, April 4, 2008

Why does my application fail when run on computers that don't have Visual Studio installed?

If you get an error message like "The application failed to initialize properly (0xc0150002)" when you try to run your application on a computer that doesn't have Visual Studio installed, it is probably because the proper C runtime DLLs are missing on that computer. This is one of the trickier aspects of using Microsoft's development tools because you need to have access to the same version of the DLLs that you linked with. So if you link dynamically to the C runtime library, you will have to either include the C runtime DLLs with your installer or provide a way for users to obtain the propert DLLs. To me this seems like a bad idea for most programs because it is rare that a program will use all that many functions in the runtime library. So for many cases it is probably more efficient to statically link the C runtime library using /MT on the command line instead of /MTD. This will usually yield binaries that are smaller than the combined size of the dynamically linked binary and the DLL file. Static linking will ensure that your program will run even without access to any DLLs.