I tried looking around and found a few solutions to detect a USB drive insertion, but none that I found were actually working in C.
I wanted to ask, how do I approach this problem?
What is the idea behind the detection process (Like, how is it done) ?
Thank you in advance! :)
The example at link https://msdn.microsoft.com/en-us/library/windows/desktop/aa363432(v=vs.85).aspx, given by Weather Vane, works in plain C, as I said in comments, with simple modifications.
Below there is the working modified code.
#define UNICODE 1
#define _UNICODE 1
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
#include <dbt.h>
#pragma comment(lib, "user32.lib" )
#pragma comment(lib, "Shell32.lib" )
void Main_OnDeviceChange( HWND hwnd, WPARAM wParam, LPARAM lParam );
char FirstDriveFromMask( ULONG unitmask ); //prototype
// This GUID is for all USB serial host PnP drivers, but you can replace it
// with any valid device class guid.
GUID WceusbshGUID = { 0x25dbce51, 0x6c8f, 0x4a72,
0x8a,0x6d,0xb5,0x4c,0x2b,0x4f,0xc8,0x35 };
// For informational messages and window titles
PWSTR g_pszAppName;
// Forward declarations
void OutputMessage(HWND hOutWnd, WPARAM wParam, LPARAM lParam);
void ErrorHandler(LPTSTR lpszFunction);
//
// DoRegisterDeviceInterfaceToHwnd
//
BOOL DoRegisterDeviceInterfaceToHwnd(
IN GUID InterfaceClassGuid,
IN HWND hWnd,
OUT HDEVNOTIFY *hDeviceNotify
)
// Routine Description:
// Registers an HWND for notification of changes in the device interfaces
// for the specified interface class GUID.
// Parameters:
// InterfaceClassGuid - The interface class GUID for the device
// interfaces.
// hWnd - Window handle to receive notifications.
// hDeviceNotify - Receives the device notification handle. On failure,
// this value is NULL.
// Return Value:
// If the function succeeds, the return value is TRUE.
// If the function fails, the return value is FALSE.
// Note:
// RegisterDeviceNotification also allows a service handle be used,
// so a similar wrapper function to this one supporting that scenario
// could be made from this template.
{
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
ZeroMemory( &NotificationFilter, sizeof(NotificationFilter) );
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
NotificationFilter.dbcc_classguid = InterfaceClassGuid;
*hDeviceNotify = RegisterDeviceNotification(
hWnd, // events recipient
&NotificationFilter, // type of device
DEVICE_NOTIFY_WINDOW_HANDLE // type of recipient handle
);
if ( NULL == *hDeviceNotify )
{
ErrorHandler(TEXT("RegisterDeviceNotification"));
return FALSE;
}
return TRUE;
}
//
// MessagePump
//
void MessagePump(
HWND hWnd
)
// Routine Description:
// Simple main thread message pump.
//
// Parameters:
// hWnd - handle to the window whose messages are being dispatched
// Return Value:
// None.
{
MSG msg;
int retVal;
// Get all messages for any window that belongs to this thread,
// without any filtering. Potential optimization could be
// obtained via use of filter values if desired.
while( (retVal = GetMessage(&msg, NULL, 0, 0)) != 0 )
{
if ( retVal == -1 )
{
ErrorHandler(TEXT("GetMessage"));
break;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
//
// WinProcCallback
//
INT_PTR WINAPI WinProcCallback(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
// Routine Description:
// Simple Windows callback for handling messages.
// This is where all the work is done because the example
// is using a window to process messages. This logic would be handled
// differently if registering a service instead of a window.
// Parameters:
// hWnd - the window handle being registered for events.
// message - the message being interpreted.
// wParam and lParam - extended information provided to this
// callback by the message sender.
// For more information regarding these parameters and return value,
// see the documentation for WNDCLASSEX and CreateWindowEx.
{
LRESULT lRet = 1;
static HDEVNOTIFY hDeviceNotify;
static HWND hEditWnd;
static ULONGLONG msgCount = 0;
switch (message)
{
case WM_CREATE:
//
// This is the actual registration., In this example, registration
// should happen only once, at application startup when the window
// is created.
//
// If you were using a service, you would put this in your main code
// path as part of your service initialization.
//
if ( ! DoRegisterDeviceInterfaceToHwnd(
WceusbshGUID,
hWnd,
&hDeviceNotify) )
{
// Terminate on failure.
ErrorHandler(TEXT("DoRegisterDeviceInterfaceToHwnd"));
ExitProcess(1);
}
//
// Make the child window for output.
//
hEditWnd = CreateWindow(TEXT("EDIT"),// predefined class
NULL, // no window title
WS_CHILD | WS_VISIBLE | WS_VSCROLL |
ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL,
0, 0, 0, 0, // set size in WM_SIZE message
hWnd, // parent window
(HMENU)1, // edit control ID
(HINSTANCE) GetWindowLong(hWnd, GWL_HINSTANCE),
NULL); // pointer not needed
if ( hEditWnd == NULL )
{
// Terminate on failure.
ErrorHandler(TEXT("CreateWindow: Edit Control"));
ExitProcess(1);
}
// Add text to the window.
SendMessage(hEditWnd, WM_SETTEXT, 0,
(LPARAM)TEXT("Registered for USB device notification...\n"));
break;
case WM_SETFOCUS:
SetFocus(hEditWnd);
break;
case WM_SIZE:
// Make the edit control the size of the window's client area.
MoveWindow(hEditWnd,
0, 0, // starting x- and y-coordinates
LOWORD(lParam), // width of client area
HIWORD(lParam), // height of client area
TRUE); // repaint window
break;
case WM_DEVICECHANGE:
{
//
// This is the actual message from the interface via Windows messaging.
// This code includes some additional decoding for this particular device type
// and some common validation checks.
//
// Note that not all devices utilize these optional parameters in the same
// way. Refer to the extended information for your particular device type
// specified by your GUID.
//
PDEV_BROADCAST_DEVICEINTERFACE b = (PDEV_BROADCAST_DEVICEINTERFACE) lParam;
(void)b;
TCHAR strBuff[256];
Main_OnDeviceChange(hEditWnd, wParam, lParam);
// Output some messages to the window.
switch (wParam)
{
case DBT_DEVICEARRIVAL:
msgCount++;
StringCchPrintf(
strBuff, 256,
TEXT("Message %d: DBT_DEVICEARRIVAL\n"), msgCount);
break;
case DBT_DEVICEREMOVECOMPLETE:
msgCount++;
StringCchPrintf(
strBuff, 256,
TEXT("Message %d: DBT_DEVICEREMOVECOMPLETE\n"), msgCount);
break;
case DBT_DEVNODES_CHANGED:
msgCount++;
StringCchPrintf(
strBuff, 256,
TEXT("Message %d: DBT_DEVNODES_CHANGED\n"), msgCount);
break;
default:
msgCount++;
StringCchPrintf(
strBuff, 256,
TEXT("Message %d: WM_DEVICECHANGE message received, value %d unhandled.\n"),
msgCount, wParam);
break;
}
OutputMessage(hEditWnd, wParam, (LPARAM)strBuff);
}
break;
case WM_CLOSE:
if ( ! UnregisterDeviceNotification(hDeviceNotify) )
{
ErrorHandler(TEXT("UnregisterDeviceNotification"));
}
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
// Send all other messages on to the default windows handler.
lRet = DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return lRet;
}
#define WND_CLASS_NAME TEXT("SampleAppWindowClass")
//
// InitWindowClass
//
BOOL InitWindowClass(void)
// Routine Description:
// Simple wrapper to initialize and register a window class.
// Parameters:
// None
// Return Value:
// TRUE on success, FALSE on failure.
// Note:
// wndClass.lpfnWndProc and wndClass.lpszClassName are the
// important unique values used with CreateWindowEx and the
// Windows message pump.
{
WNDCLASSEX wndClass;
wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wndClass.hInstance = (HINSTANCE)(GetModuleHandle(0));
wndClass.lpfnWndProc = (WNDPROC)(WinProcCallback);
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hIcon = LoadIcon(0,IDI_APPLICATION);
wndClass.hbrBackground = CreateSolidBrush(RGB(192,192,192));
wndClass.hCursor = LoadCursor(0, IDC_ARROW);
wndClass.lpszClassName = WND_CLASS_NAME;
wndClass.lpszMenuName = NULL;
wndClass.hIconSm = wndClass.hIcon;
if ( ! RegisterClassEx(&wndClass) )
{
ErrorHandler(TEXT("RegisterClassEx"));
return FALSE;
}
return TRUE;
}
//
// main
//
int __stdcall _tWinMain(
HINSTANCE hInstanceExe,
HINSTANCE hInst, // should not reference this parameter
PTSTR lpstrCmdLine,
int nCmdShow
)
{
//
// To enable a console project to compile this code, set
// Project->Properties->Linker->System->Subsystem: Windows.
//
int nArgC = 0;
PWSTR* ppArgV = CommandLineToArgvW(lpstrCmdLine, &nArgC);
g_pszAppName = ppArgV[0];
if ( ! InitWindowClass() )
{
// InitWindowClass displays any errors
return -1;
}
// Main app window
HWND hWnd = CreateWindowEx(
WS_EX_CLIENTEDGE | WS_EX_APPWINDOW,
WND_CLASS_NAME,
g_pszAppName,
WS_OVERLAPPEDWINDOW, // style
CW_USEDEFAULT, 0,
640, 480,
NULL, NULL,
hInstanceExe,
NULL);
if ( hWnd == NULL )
{
ErrorHandler(TEXT("CreateWindowEx: main appwindow hWnd"));
return -1;
}
// Actually draw the window.
ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
// The message pump loops until the window is destroyed.
MessagePump(hWnd);
return 1;
}
//
// OutputMessage
//
void OutputMessage(
HWND hOutWnd,
WPARAM wParam,
LPARAM lParam
)
// Routine Description:
// Support routine.
// Send text to the output window, scrolling if necessary.
// Parameters:
// hOutWnd - Handle to the output window.
// wParam - Standard windows message code, not used.
// lParam - String message to send to the window.
// Return Value:
// None
// Note:
// This routine assumes the output window is an edit control
// with vertical scrolling enabled.
// This routine has no error checking.
{
LRESULT lResult;
LONG bufferLen;
LONG numLines;
LONG firstVis;
// Make writable and turn off redraw.
lResult = SendMessage(hOutWnd, EM_SETREADONLY, FALSE, 0L);
lResult = SendMessage(hOutWnd, WM_SETREDRAW, FALSE, 0L);
// Obtain current text length in the window.
bufferLen = SendMessage (hOutWnd, WM_GETTEXTLENGTH, 0, 0L);
numLines = SendMessage (hOutWnd, EM_GETLINECOUNT, 0, 0L);
firstVis = SendMessage (hOutWnd, EM_GETFIRSTVISIBLELINE, 0, 0L);
lResult = SendMessage (hOutWnd, EM_SETSEL, bufferLen, bufferLen);
// Write the new text.
lResult = SendMessage (hOutWnd, EM_REPLACESEL, 0, lParam);
// See whether scrolling is necessary.
if (numLines > (firstVis + 1))
{
int lineLen = 0;
int lineCount = 0;
int charPos;
// Find the last nonblank line.
numLines--;
while(!lineLen)
{
charPos = SendMessage(
hOutWnd, EM_LINEINDEX, (WPARAM)numLines, 0L);
lineLen = SendMessage(
hOutWnd, EM_LINELENGTH, charPos, 0L);
if(!lineLen)
numLines--;
}
// Prevent negative value finding min.
lineCount = numLines - firstVis;
lineCount = (lineCount >= 0) ? lineCount : 0;
// Scroll the window.
lResult = SendMessage(
hOutWnd, EM_LINESCROLL, 0, (LPARAM)lineCount);
}
// Done, make read-only and allow redraw.
lResult = SendMessage(hOutWnd, WM_SETREDRAW, TRUE, 0L);
lResult = SendMessage(hOutWnd, EM_SETREADONLY, TRUE, 0L);
}
//
// ErrorHandler
//
void ErrorHandler(
LPTSTR lpszFunction
)
// Routine Description:
// Support routine.
// Retrieve the system error message for the last-error code
// and pop a modal alert box with usable info.
// Parameters:
// lpszFunction - String containing the function name where
// the error occurred plus any other relevant data you'd
// like to appear in the output.
// Return Value:
// None
// Note:
// This routine is independent of the other windowing routines
// in this application and can be used in a regular console
// application without modification.
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
// Display the error message and exit the process.
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf)
+ lstrlen((LPCTSTR)lpszFunction)+40)
* sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, g_pszAppName, MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
}
/*+##fnc##----------------------------------------------------------------*//*!
\brief Main_OnDeviceChange
\date Created on Sun Sep 10 15:10:10 2017
\date Modified on Sun Sep 10 15:10:10 2017
\*//*-##fnc##----------------------------------------------------------------*/
void Main_OnDeviceChange(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
TCHAR szMsg[80];
switch (wParam)
{
case DBT_DEVICEARRIVAL:
// Check whether a CD or DVD was inserted into a drive.
if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
{
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
if (lpdbv->dbcv_flags & DBTF_MEDIA)
{
StringCchPrintf(szMsg, sizeof(szMsg) / sizeof(szMsg[0]), TEXT("Drive %c: Media has arrived.\n"), FirstDriveFromMask(lpdbv->dbcv_unitmask));
//MessageBox( hwnd, szMsg, TEXT("WM_DEVICECHANGE"), MB_OK );
}
else
{
StringCchPrintf(szMsg, sizeof(szMsg) / sizeof(szMsg[0]), TEXT("Assigned drive letter '%c'\n"), FirstDriveFromMask(lpdbv->dbcv_unitmask));
}
OutputMessage(hwnd, wParam, (LPARAM)szMsg);
}
break;
case DBT_DEVICEREMOVECOMPLETE:
// Check whether a CD or DVD was removed from a drive.
if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)
{
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
if (lpdbv->dbcv_flags & DBTF_MEDIA)
{
StringCchPrintf(szMsg, sizeof(szMsg) / sizeof(szMsg[0]), TEXT("Drive %c: Media was removed.\n"), FirstDriveFromMask(lpdbv->dbcv_unitmask));
//MessageBox( hwnd, szMsg, TEXT("WM_DEVICECHANGE" ), MB_OK );
}
else
{
StringCchPrintf(szMsg, sizeof(szMsg) / sizeof(szMsg[0]), TEXT("Disconnected drive '%c'\n"), FirstDriveFromMask(lpdbv->dbcv_unitmask));
}
OutputMessage(hwnd, wParam, (LPARAM)szMsg);
}
break;
default:
/*
Process other WM_DEVICECHANGE notifications for other
devices or reasons.
*/
;
}
}
/*------------------------------------------------------------------
FirstDriveFromMask( unitmask )
Description
Finds the first valid drive letter from a mask of drive letters.
The mask must be in the format bit 0 = A, bit 1 = B, bit 2 = C,
and so on. A valid drive letter is defined when the
corresponding bit is set to 1.
Returns the first drive letter that was found.
--------------------------------------------------------------------*/
char FirstDriveFromMask( ULONG unitmask )
{
char i;
for (i = 0; i < 26; ++i)
{
if (unitmask & 0x1)
break;
unitmask = unitmask >> 1;
}
return( i + 'A' );
}
Basically remove reinterpret_cast to C casts. I.e.:
wndClass.hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(0));
to
wndClass.hInstance = (HINSTANCE)(GetModuleHandle(0));
And add unused parameters name in functions definition (that is not allowed in standard C). I.e. change from:
int __stdcall _tWinMain(
HINSTANCE hInstanceExe,
HINSTANCE, // should not reference this parameter
PTSTR lpstrCmdLine,
int nCmdShow
)
to:
int __stdcall _tWinMain(
HINSTANCE hInstanceExe,
HINSTANCE hInst, // you **must define this parameter** even if it's not referenced
PTSTR lpstrCmdLine,
int nCmdShow
)
These simple modifications allow the use of almost all MS samples under plain-C compilers.
I have also added volume info.
To explain here in detail how the device notification framework works is just a waste of time. The MS documentation is complete and exhaustive, you can find all information on MSDN.
The translation I made give you the opportunity to study and test on your development environment in plain C, and this will allow to make experimentation.
Anyway the very basic essence is: the application (your program) registers itself with the notification server, that in turn, from now on until you unregister it, send all windows OS notification messages to your application. Each notification carries specific info data in specialized structures.
The full set of structure supplied with each notification (documented on MSDN) gives you indeep details on the type of change.
Related
The goal is to create a program that logs keystrokes and writes it into a text file. Currently, just tapping a key will write that key a hundred times so I'm trying to slow it down a bit.
However, using Sleep() will prevent this whole code from doing anything at all unless I use Sleep(0) (which, as I understand, means "Do not let lower priority threads run").
Code:
// Subconsole is Windows so the running app is not visible to a certain someone
int __stdcall WinMain(_In_ HINSTANCE hinstance, _In_opt_ HINSTANCE hprevinstance, _In_ LPSTR lpcmdline, _In_ int ncmdshow)
{
FILE* write;
char running = 1;
fopen_s(&write, "typelog.txt", "w");
while (running)
{
_Bool keytoggle;
char key;
// Go from A to Z and see if the key for that was pressed
for (int i = 0x41; i < 0x5A; i++)
{
// Is the highest order bit for GetAsyncKeyState a 1 (is the key down)
keytoggle = (GetAsyncKeyState(i) & (1 << 15)) != 0;
if (keytoggle)
{
key = i; // save the key that was pressed
break;
}
}
// If the key was pressed, write it, otherwise write a space
if (keytoggle)
{
if (write)
fprintf(write, "%c", key);
}
else
{
if (write)
fprintf(write, " ");
}
// Sleep for like, just one millisecond please
Sleep(1);
}
return 0;
}
I have heard that using Sleep, even for a 1ms, can be extended to 20ms because of the system timer. Is that the case? Even if it was, why would the code not be executed at all?
I've searched for an hour or so and found nothing. If you can help it'd be great.
With debugging, the problem is your txt handle is not closed when calling Sleep(1). You can use Message-Only Windows and Raw Input to achieve your goal. For example:
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, HWND_MESSAGE, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
RawInput(hWnd);
return TRUE;
}
and
#define BUFFER 512
HRESULT ShowRawInputInfo(LPARAM lParam)
{
UINT dwSize = 0;
HRESULT hResult;
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER));
LPBYTE lpb = new BYTE[dwSize];
if (lpb == NULL)
{
return 0;
}
if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize)
OutputDebugString(TEXT("GetRawInputData does not return correct size !\n"));
RAWINPUT* raw = (RAWINPUT*)lpb;
WCHAR szTempOutput[BUFFER];
if (raw->header.dwType == RIM_TYPEKEYBOARD)
{
hResult = StringCchPrintf(szTempOutput, BUFFER,
TEXT(" Kbd: make=%04x Flags:%04x Reserved:%04x ExtraInformation:%08x, msg=%04x VK=%04x \n"),
raw->data.keyboard.MakeCode,
raw->data.keyboard.Flags,
raw->data.keyboard.Reserved,
raw->data.keyboard.ExtraInformation,
raw->data.keyboard.Message,
raw->data.keyboard.VKey);
if (FAILED(hResult))
{
// TODO: write error handler
}
OutputDebugString(szTempOutput);
}
else if (raw->header.dwType == RIM_TYPEMOUSE)
{
hResult = StringCchPrintf(szTempOutput, BUFFER,
TEXT("Mouse: usFlags=%04x ulButtons=%04x usButtonFlags=%04x usButtonData=%04x ulRawButtons=%04x lLastX=%04x lLastY=%04x ulExtraInformation=%04x\r\n"),
raw->data.mouse.usFlags,
raw->data.mouse.ulButtons,
raw->data.mouse.usButtonFlags,
raw->data.mouse.usButtonData,
raw->data.mouse.ulRawButtons,
raw->data.mouse.lLastX,
raw->data.mouse.lLastY,
raw->data.mouse.ulExtraInformation);
if (FAILED(hResult))
{
// TODO: write error handler
}
OutputDebugString(szTempOutput);
}
delete[] lpb;
return 0;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
DWORD dwStyleOfStaticText = 0;
HWND hBmp2 = NULL;
WORD reason = 0;
switch (message)
{
case WM_INPUT:
{
ShowRawInputInfo(lParam);
break;
}
case WM_KEYDOWN:
WCHAR szTempOutput[512];
StringCchPrintf(szTempOutput, 512,
TEXT(" KeyValue=%04x\n"), wParam);
OutputDebugString(szTempOutput);
//PostMessage(hWnd, WM_COMMAND, KILLTHEWIN, 0);// hope it serializes message
//PostQuitMessage(0);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Edit: Or Hook
I'm having an issue with my Windows application where upon closing it from the taskbar or via hotkey it will occasionally hang. I'm wondering how to gracefully exit the following program:
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK OwnedWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static HWND mainHwnd;
static HWND ownedHwnd;
void create_windows()
{
HMODULE thisMod = GetModuleHandleA(NULL);
WNDCLASSA wc;
wc.style = CS_VREDRAW | CS_HREDRAW;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = thisMod;
wc.hIcon = 0;
wc.hCursor = 0;
wc.hbrBackground = 0;
wc.lpszMenuName = NULL;
wc.lpszClassName = "MAINWIN";
RegisterClassA(&wc);
wc.lpfnWndProc = OwnedWndProc;
wc.lpszClassName = "OWNEDWIN";
RegisterClassA(&wc);
mainHwnd = CreateWindowExA(WS_EX_TOPMOST, "MAINWIN", "MAINWIN", WS_POPUP, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), 0, 0, thisMod, NULL);
ShowWindow(mainHwnd, SW_SHOWNORMAL);
ownedHwnd = CreateWindowExA(WS_EX_LAYERED | WS_EX_TOPMOST, "OWNEDWIN", "OWNEDWIN", WS_POPUP, 0, 0, 200, 200, mainHwnd, 0, thisMod, NULL);
ShowWindow(ownedHwnd, SW_SHOWNORMAL);
}
int main(int argc, char **argv)
{
if (!RegisterHotKey(NULL, 1, MOD_NOREPEAT, VK_ESCAPE)) {
return 0;
}
create_windows();
BOOL bRet;
MSG msg;
while((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
if (bRet == -1) {
/* I'm never reached */
} else if (msg.message == WM_HOTKEY) {
UnregisterHotKey(NULL, 1);
PostMessageA(mainHwnd, WM_CLOSE, 0, 0);
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
/* Do a bit of cleanup */
return 0;
}
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static BOOL condition = FALSE;
switch (uMsg) {
case WM_CREATE:
SetTimer(hwnd, 1, 20, NULL);
return 0;
case WM_TIMER:
if (condition) {
KillTimer(hwnd, 1);
PostMessageA(ownedHwnd, WM_CLOSE, 0, 0);
} else {
/* Do processing here on both windows. The condition variable is
updated in here after the program does its thing. */
}
return 0;
case WM_CLOSE:
DestroyWindow(hwnd);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProcA(hwnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK OwnedWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
/* Letting DefWindowProcA handle everything since I don't need this window to
do anything but close afterwards. */
return DefWindowProcA(hwnd, uMsg, wParam, lParam);
}
When it does hang, it always seems to occur after the timer has already been disabled and the owned window has been closed. Never before. Whether in console mode or windows mode its always the same, always after those two things happen and I try to close the window.
With printf statements (because I'm not entirely sure how to debug this) I've noticed that when it freezes WM_CLOSE and subsequently WM_DESTROY are never reached in MainWndProc, as if it's stuck somewhere deep in GetMessage or DispatchMessage, or my message loop, I'm not doing anything fancy in this program so I have no clue. When I manage to make this happen in the debugger it ends up still running but I'm not able to pause it and step to see where where it is executing.
Strangely, though not anymore I've observed, when I would close it in console mode the window would disappear but the process would continue to run in the background until the cmd window from which I launched the program received keyboard input or closes. Conversely in windows mode the same would happen but there'd be no cmd window, instead having to end it from the task manager.
I've never had any trouble with simple Windows GUI applications where only one window is needed. It's only when there are more that I run into this problem of it never fully closing and not knowing how to gracefully exit.
Welp it turns out using WinMain, instead of main, and not specifying it as the entry point (thus the C run-time library is initialized correctly) solved it. And that's all. I still cannot fully wrap my head around it.
Before I discovered this I noticed in the debugger that when the program got out of the message loop and exited, there were a number of threads still running. What's odd is I never created any threads...
I don't know what led me to try WinMain unfortunately as I'd been wildly experimenting with different configuration at that point in console and windows mode until I arrived at the one that worked.
i am building program that ask for a file and then filter it
to what the user want (black and white, smooth and so on).
i use in opencv for the filter and windows.h for the GUI.
but for loading a picture to the Pimage in opencv - i need the image full path.
so of curse i can just give the user textbox and say to him write the all path,
but it will be really nasty programm ...
Then i remember in Photoshop and his nice file dialog that allowed
me to load file without writing the all path.
Quick search in google give me the OPENFILENAME struct,
But when i try to use the function that give me the name of the file:
if(GetOpenFileName(&ofn))
{
printf("File path: %s", szFileName)
}
It always give me this error in the console:
undefined reference to `GetOpenFileNameA#4'
collect2: ld returned 1 exit status
Sucks!
So to sum up,
I just want nice code that i will press on the "load" button,
the file dialog will open, and the full path of the file will print to the screen. this is my code so far (my and my good good friend Code Blocks):
#if defined(UNICODE) && !defined(_UNICODE)
#define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
#define UNICODE
#endif
#include <tchar.h>
#include <windows.h>
#define HEIGHT 500
#define WIDTH 500
/* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
HWND button_1;
/* Make the class name into a global variable */
TCHAR szClassName[ ] = _T("windows form");
int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nCmdShow)
{
HWND hwnd; /* This is the handle for our window */
MSG messages; /* Here messages to the application are saved */
WNDCLASSEX wincl; /* Data structure for the windowclass */
/* The Window structure */
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */
wincl.style = CS_DBLCLKS; /* Catch double-clicks */
wincl.cbSize = sizeof (WNDCLASSEX);
/* Use default icon and mouse-pointer */
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL; /* No menu */
wincl.cbClsExtra = 0; /* No extra bytes after the window class */
wincl.cbWndExtra = 0; /* structure or the window instance */
/* Use Windows's default colour as the background of the window */
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
/* Register the window class, and if it fails quit the program */
if (!RegisterClassEx (&wincl))
return 0;
/* The class is registered, let's create the program*/
hwnd = CreateWindowEx (
0, /* Extended possibilites for variation */
szClassName, /* Classname */
_T("windows form"), /* Title Text */
WS_OVERLAPPEDWINDOW, /* default window */
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
WIDTH, /* The programs width */
HEIGHT, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);
/* Make the window visible on the screen */
ShowWindow (hwnd, nCmdShow);
/* Run the message loop. It will run until GetMessage() returns 0 */
while (GetMessage (&messages, NULL, 0, 0))
{
/* Translate virtual-key messages into character messages */
TranslateMessage(&messages);
/* Send message to WindowProcedure */
DispatchMessage(&messages);
}
/* The program return-value is 0 - The value that PostQuitMessage() gave */
return messages.wParam;
}
/* This function is called by the Windows function DispatchMessage() */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) /* handle the messages */
{
case WM_CREATE:
button_1 = CreateWindow("BUTTON",
"Open file", WS_VISIBLE | WS_CHILD | WS_BORDER,
WIDTH/2, HEIGHT/2, 100, 40, hwnd, (HMENU) 1, NULL, NULL);
break;
case WM_DESTROY:
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
You simply cannot use GetOpenFileName() without defining the OPENFILENAME structure.
LPSTR filebuff = new char[256];
OPENFILENAME open = { 0 };
open.lStructSize = sizeof(OPENFILENAME);
open.hwndOwner = window; //Handle to the parent window
open.lpstrFilter = "Image Files(.jpg|.png|.bmp|.jpeg)\0*.jpg;*.png;*.bmp;*.jpeg\0\0";
open.lpstrCustomFilter = NULL;
open.lpstrFile = filebuff;
open.lpstrFile[0] = '\0';
open.nMaxFile = 256;
open.nFilterIndex = 1;
open.lpstrInitialDir = NULL;
open.lpstrTitle = "Select An Image File\0";
open.nMaxFileTitle = strlen("Select an image file\0");
open.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER;
Then use
if (GetOpenFileName(&open))
{
printf("File name:%s", open.lpstrFile);
}
The above code selects an image file, replace the lpstrFilter value to filter extensions, lpstrTitle and nMaxFileTitle() to your needs.
Here is the OPENFILENAME stuct documentation
All the properties are necessary for the dialog to show up!
I have a custom window class intended as an editor view, and since I'm handling keyboard events directly, I want to have a standard edit control on top for entering text. My problem is simple: I also want to grab focus when the user clicks on the custom window class, so I handle WM_MOUSEACTIVATE:
case WM_MOUSEACTIVATE:
SetFocus(hwnd);
return MA_ACTIVATE;
But I also want the edit control to disappear when it loses focus:
case WM_COMMAND:
if (HIWORD(wparam) == EN_KILLFOCUS) {
MessageBeep(-1);
ShowWindow(edit, SW_HIDE);
return 0;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
Except that for some reason, the WM_MOUSEACTIVATE handler fires when I click on the (visible) edit control; this causes it to lose and regain focus, but not before I hide the edit control!
The complete test program is below; if you click anywhere in the window's client area, you'll see the edit control. Click it once to give it focus, then click it again. You should hear a beep, followed by the edit control disappearing. Common Controls 6 required.
This also happens if I subclass the edit control and handle WM_KILLFOCUS there. (This was what I originally had.)
What's going on? Thanks.
// 22-23 august 2014
// edited from wineditoverlaytest 22 august 2014
// scratch Windows program by pietro gagliardi 17 april 2014
// fixed typos and added toWideString() 1 may 2014
// borrows code from the scratch GTK+ program (16-17 april 2014) and from code written 31 march 2014 and 11-12 april 2014
#define _UNICODE
#define UNICODE
#define STRICT
#define _GNU_SOURCE // needed to declare asprintf()/vasprintf()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <windows.h>
#include <commctrl.h> // needed for InitCommonControlsEx() (thanks Xeek in irc.freenode.net/#winapi for confirming)
#include <windowsx.h>
#ifdef _MSC_VER
#error sorry! the scratch windows program relies on mingw-only functionality! (specifically: asprintf())
#endif
HMODULE hInstance;
HICON hDefaultIcon;
HCURSOR hDefaultCursor;
HFONT controlfont;
void panic(char *fmt, ...);
HWND area;
HWND edit;
LRESULT CALLBACK wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
RECT r;
switch (msg) {
case WM_SIZE:
GetClientRect(hwnd, &r);
MoveWindow(area, r.left, r.top, r.right - r.left, r.bottom - r.top, TRUE);
return 0;
case WM_CLOSE:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, msg, wparam, lparam);
}
panic("oops: message %ud does not return anything; bug in wndproc()", msg);
}
LRESULT CALLBACK areawndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg) {
case WM_MOUSEACTIVATE:
SetFocus(hwnd);
return MA_ACTIVATE;
case WM_COMMAND:
if (HIWORD(wparam) == EN_KILLFOCUS) {
MessageBeep(-1);
ShowWindow(edit, SW_HIDE);
return 0;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
case WM_LBUTTONUP:
MoveWindow(edit, GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam), 100, 20, TRUE);
ShowWindow(edit, SW_SHOW);
return 0;
default:
return DefWindowProc(hwnd, msg, wparam, lparam);
}
panic("oops: message %ud does not return anything; bug in wndproc()", msg);
}
HWND makeMainWindow(void)
{
WNDCLASS cls;
HWND hwnd;
ZeroMemory(&cls, sizeof (WNDCLASS));
cls.lpszClassName = L"mainwin";
cls.lpfnWndProc = wndproc;
cls.hInstance = hInstance;
cls.hIcon = hDefaultIcon;
cls.hCursor = hDefaultCursor;
cls.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
if (RegisterClass(&cls) == 0)
panic("error registering window class");
hwnd = CreateWindowEx(0,
L"mainwin", L"Main Window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
if (hwnd == NULL)
panic("opening main window failed");
return hwnd;
}
void buildUI(HWND mainwin)
{
#define CSTYLE (WS_CHILD | WS_VISIBLE)
#define CXSTYLE (0)
#define SETFONT(hwnd) SendMessage(hwnd, WM_SETFONT, (WPARAM) controlfont, (LPARAM) TRUE);
// build GUI here; use CSTYLE and CXSTYLE in CreateWindowEx() and call SETFONT() on each new widget
WNDCLASS cls;
ZeroMemory(&cls, sizeof (WNDCLASS));
cls.lpszClassName = L"area";
cls.lpfnWndProc = areawndproc;
cls.hInstance = hInstance;
cls.hIcon = hDefaultIcon;
cls.hCursor = hDefaultCursor;
cls.hbrBackground = (HBRUSH) (COLOR_GRADIENTACTIVECAPTION + 1);
if (RegisterClass(&cls) == 0)
panic("error registering area window class");
area = CreateWindowEx(0,
L"area", L"",
WS_CHILD | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
mainwin, NULL, hInstance, NULL);
if (area == NULL)
panic("opening main window failed");
edit = CreateWindowEx(WS_EX_CLIENTEDGE,
L"edit", L"",
WS_CHILD | ES_AUTOHSCROLL | ES_LEFT | ES_NOHIDESEL | WS_TABSTOP,
0, 0, 0, 0,
area, NULL, hInstance, NULL);
if (edit == NULL)
panic("edit creation failed");
SETFONT(edit);
}
void firstShowWindow(HWND hwnd);
void initwin(void);
int main(int argc, char *argv[])
{
HWND mainwin;
MSG msg;
initwin();
mainwin = makeMainWindow();
buildUI(mainwin);
firstShowWindow(mainwin);
for (;;) {
BOOL gmret;
gmret = GetMessage(&msg, NULL, 0, 0);
if (gmret == -1)
panic("error getting message");
if (gmret == 0)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
DWORD iccFlags =
// ICC_ANIMATE_CLASS | // animation control
// ICC_BAR_CLASSES | // toolbar, statusbar, trackbar, tooltip
// ICC_COOL_CLASSES | // rebar
// ICC_DATE_CLASSES | // date and time picker
// ICC_HOTKEY_CLASS | // hot key
// ICC_INTERNET_CLASSES | // IP address entry field
// ICC_LINK_CLASS | // hyperlink
// ICC_LISTVIEW_CLASSES | // list-view, header
// ICC_NATIVEFNTCTL_CLASS | // native font
// ICC_PAGESCROLLER_CLASS | // pager
// ICC_PROGRESS_CLASS | // progress bar
ICC_STANDARD_CLASSES | // "one of the intrinsic User32 control classes"
// ICC_TAB_CLASSES | // tab, tooltip
// ICC_TREEVIEW_CLASSES | // tree-view, tooltip
// ICC_UPDOWN_CLASS | // up-down
// ICC_USEREX_CLASSES | // ComboBoxEx
// ICC_WIN95_CLASSES | // some of the above
0;
void initwin(void)
{
INITCOMMONCONTROLSEX icc;
NONCLIENTMETRICS ncm;
hInstance = GetModuleHandle(NULL);
if (hInstance == NULL)
panic("error getting hInstance");
hDefaultIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION));
if (hDefaultIcon == NULL)
panic("error getting default window class icon");
hDefaultCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
if (hDefaultCursor == NULL)
panic("error getting default window cursor");
icc.dwSize = sizeof (INITCOMMONCONTROLSEX);
icc.dwICC = iccFlags;
if (InitCommonControlsEx(&icc) == FALSE)
panic("error initializing Common Controls");
ncm.cbSize = sizeof (NONCLIENTMETRICS);
if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
sizeof (NONCLIENTMETRICS), &ncm, 0) == 0)
panic("error getting non-client metrics for getting control font");
controlfont = CreateFontIndirect(&ncm.lfMessageFont);
if (controlfont == NULL)
panic("error getting control font");
}
void panic(char *fmt, ...)
{
char *msg;
TCHAR *lerrmsg;
char *fullmsg;
va_list arg;
DWORD lasterr;
DWORD lerrsuccess;
lasterr = GetLastError();
va_start(arg, fmt);
if (vasprintf(&msg, fmt, arg) == -1) {
fprintf(stderr, "critical error: vasprintf() failed in panic() preparing panic message; fmt = \"%s\"\n", fmt);
abort();
}
// according to http://msdn.microsoft.com/en-us/library/windows/desktop/ms680582%28v=vs.85%29.aspx
lerrsuccess = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, lasterr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lerrmsg, 0, NULL);
if (lerrsuccess == 0) {
fprintf(stderr, "critical error: FormatMessage() failed in panic() preparing GetLastError() string; panic message = \"%s\", last error in panic(): %ld, last error from FormatMessage(): %ld\n", msg, lasterr, GetLastError());
abort();
}
// note to self: use %ws instead of %S (thanks jon_y in irc.oftc.net/#mingw-w64)
if (asprintf(&fullmsg, "panic: %s\nlast error: %ws\n", msg, lerrmsg) == -1) {
fprintf(stderr, "critical error: asprintf() failed in panic() preparing full report; panic message = \"%s\", last error message: \"%ws\"\n", msg, lerrmsg);
abort();
}
fprintf(stderr, "%s\n", fullmsg);
va_end(arg);
exit(1);
}
void firstShowWindow(HWND hwnd)
{
// we need to get nCmdShow
int nCmdShow;
STARTUPINFO si;
nCmdShow = SW_SHOWDEFAULT;
GetStartupInfo(&si);
if ((si.dwFlags & STARTF_USESHOWWINDOW) != 0)
nCmdShow = si.wShowWindow;
ShowWindow(hwnd, nCmdShow);
if (UpdateWindow(hwnd) == 0)
panic("UpdateWindow(hwnd) failed in first show");
}
From the docs for WM_MOUSEACTIVATE:
The parent window receives this message only if the child window
passes it to the DefWindowProc function
So I would say the edit control is doing this - it's passing WM_MOUSEACTIVATE to DefWindowProc, which then passes the message to the parent window.
If you really want to use this method, you would need to subclass the edit control to stop it passing the message through to DefWindowProc.
Or, change your approach, and use WM_LBUTTONDOWN instead.
I've sub-classed some button controls, since I'm drawing the whole UI myself (to the dialog's hdc).
This is to avoid flicker, the intention is that all drawing is done via a single memDC - preventing the staggered update of the UI.
So, I draw everything to the dialog's background, then position some buttons over the regions of the UI that should react to mouse events. So far so good. Or so I thought.
I sub-classed the buttons, using the following WndProc, expecting that Windows would do everything as per normal, except the drawing.
LRESULT CALLBACK invisibleBtnProc(HWND hwndBtn, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
long oldProc = GetWindowLong(hwndBtn, GWL_USERDATA);
switch (uMsg)
{
case WM_PAINT:
ValidateRect(hwndBtn, NULL);
return 0;
case WM_ERASEBKGND:
return 1;
}
return CallWindowProc((WNDPROC)oldProc, hwndBtn, uMsg, wParam, lParam);
}
The buttons are created and subclassed with the following code:
for (i=0; i<n; i++)
{
btn = CreateWindow(WC_BUTTON, L"", WS_VISIBLE|WS_CHILD, 0,0,0,0, hwndDlg, (HMENU)(firstBigBtnId+i), hInst, NULL);
long btnProcCur = GetWindowLong(btn, GWL_WNDPROC);
SetWindowLong(btn, GWL_USERDATA, btnProcCur);
SetWindowLong(btn, GWL_WNDPROC, (long) invisibleBtnProc);
}
When I built this code with MinGW & Code::Blocks, it works flawlessly. (both in debug and release builds)
Unfortunately, when built with MSVC & VS2010, I observe different behaviour. The debug-mode build is okay, but the release build is not. When one of the invisible buttons is clicked, the system is drawing it, obscuring the underlying 'button'.
I've a large WMF (emf? I forget) that needs to be drawn - it's quite slow and produces flicker when the window is resized, for those that wonder why the custom-draw-everything approach.
Here's what I'm seeing:
Note, that before I tried to click on the leftmost button it was not visible - just like the one on the right. Only upon clicking it does windows decide to draw it.
Resizing the parent window - (a dialog which triggers a call to InvalidateRect for the dialog) removes the erroneous drawing. Clicking the button once again causes it to be drawn.
Any ideas where I've made a mistake in my thinking?
EDIT: Added code below for a SCCCE (This displays the same unwanted behaviour when built with GCC debug & release, that the original program showed in debug build only)
#include <windows.h>
/* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
RECT btnRect;
const int btnSize = 150;
const int btnId = 1000;
HINSTANCE hInst;
/* Make the class name into a global variable */
char szClassName[ ] = "CodeBlocksWindowsApp";
int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nCmdShow)
{
HWND hwnd; /* This is the handle for our window */
MSG messages; /* Here messages to the application are saved */
WNDCLASSEX wincl; /* Data structure for the windowclass */
/* The Window structure */
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */
wincl.style = CS_DBLCLKS; /* Catch double-clicks */
wincl.cbSize = sizeof (WNDCLASSEX);
/* Use default icon and mouse-pointer */
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL; /* No menu */
wincl.cbClsExtra = 0; /* No extra bytes after the window class */
wincl.cbWndExtra = 0; /* structure or the window instance */
/* Use Windows's default colour as the background of the window */
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
/* Register the window class, and if it fails quit the program */
if (!RegisterClassEx (&wincl))
return 0;
/* The class is registered, let's create the program*/
hwnd = CreateWindowEx (
0, /* Extended possibilites for variation */
szClassName, /* Classname */
"Code::Blocks Template Windows App", /* Title Text */
WS_OVERLAPPEDWINDOW, /* default window */
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
544, /* The programs width */
375, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);
/* Make the window visible on the screen */
ShowWindow (hwnd, nCmdShow);
/* Run the message loop. It will run until GetMessage() returns 0 */
while (GetMessage (&messages, NULL, 0, 0))
{
/* Translate virtual-key messages into character messages */
TranslateMessage(&messages);
/* Send message to WindowProcedure */
DispatchMessage(&messages);
}
/* The program return-value is 0 - The value that PostQuitMessage() gave */
return messages.wParam;
}
LRESULT CALLBACK invisibleBtnProc(HWND hwndBtn, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
long oldProc = GetWindowLong(hwndBtn, GWL_USERDATA);
switch (uMsg)
{
case WM_PAINT:
ValidateRect(hwndBtn, NULL);
return 0;
case WM_ERASEBKGND:
return 1;
}
return CallWindowProc((WNDPROC)oldProc, hwndBtn, uMsg, wParam, lParam);
}
void onSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
RECT mRect;
GetClientRect(hwnd, &mRect);
btnRect.left = (mRect.right - btnSize) / 2;
btnRect.top = (mRect.bottom - btnSize) / 2;
btnRect.right = btnRect.left + btnSize;
btnRect.bottom = btnRect.top + btnSize;
HWND btn;
btn = GetDlgItem(hwnd, btnId);
MoveWindow(btn, btnRect.left, btnRect.top, btnSize, btnSize, false);
InvalidateRect(hwnd, NULL, false);
}
void onPaint(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
HBRUSH bkBrush, redBrush;
RECT mRect;
GetClientRect(hwnd, &mRect);
hdc = BeginPaint(hwnd, &ps);
bkBrush = CreateSolidBrush(RGB(51,51,51) );
redBrush = CreateSolidBrush(RGB(255,0,0) );
FillRect(hdc, &mRect, bkBrush);
FillRect(hdc, &btnRect, redBrush);
DeleteObject(bkBrush);
DeleteObject(redBrush);
EndPaint(hwnd, &ps);
}
/* This function is called by the Windows function DispatchMessage() */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) /* handle the messages */
{
case WM_CREATE:
HWND tmp;
tmp = CreateWindow("Button", "Press Me", WS_VISIBLE|WS_CHILD, 0,0,0,0, hwnd, (HMENU)btnId, hInst, NULL);
long oldProc;
oldProc = GetWindowLong(tmp, GWL_WNDPROC);
SetWindowLong(tmp, GWL_USERDATA, oldProc);
SetWindowLong(tmp, GWL_WNDPROC, (long)invisibleBtnProc);
return 0;
case WM_DESTROY:
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
break;
case WM_SIZE:
onSize(hwnd, wParam, lParam);
return 0;
case WM_PAINT:
onPaint(hwnd, wParam, lParam);
return 0;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case btnId:
MessageBeep(MB_ICONEXCLAMATION);
break;
}
return 0;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
Setting the BS_OWNERDRAW style tells Windows that it shall not draw the button itself, but that you are responsible for that. That does the trick.
There is not much you need to change. Just create the button with this style.
tmp = CreateWindow("Button", "Press Me", WS_VISIBLE|WS_CHILD|BS_OWNERDRAW, 0,0,0,0, hwnd, (HMENU)btnId, hInst, NULL);
Then in your invisibleBtnProc you can add
case WM_DRAWITEM:
ValidateRect(hwndBtn, NULL);
return TRUE;