PlaySound too slow on slower PCs? - c

I made a program that outputs a click-sound whenever a key on the keyboard has been pressed.
When I tried the program on my pc (with Ryzen 3900x) and on one of my laptops (with i7 8850h) it outputted the sound flawlessly without any delay, even when typing fast.
But on a slower laptop I use for school, it is not outputting the sound right, especially when typing fast. (The sound is either delayed or it only plays a little part before stopping)
I cant really find an issue other than that PlaySound might be too slow or something. It hasn't anything to do with loading the sound-file, because I am playing the sound from memory. It is also not the recognition of a key-press, because the console output (which key gets pressed) is almost instantly.
Here is my code:
#ifndef UNICODE
#define UNICODE
#endif // UNICODE
#include <stdio.h>
#include <stdlib.h>
#include <w32api.h>
#define WINVER WindowsXP
#include <windows.h>
#include <winuser.h>
BYTE* byteAudio = NULL;
LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if(uMsg == WM_INPUT)
{
HRAWINPUT hRawInput = (HRAWINPUT)lParam;
RAWINPUT input = { 0 };
UINT size = sizeof(input);
GetRawInputData(hRawInput, RID_INPUT,&input,&size,sizeof(RAWINPUTHEADER));
if(input.data.keyboard.Flags == 0)
{
//PlaySoundW(TEXT("res/click.wav"), NULL, SND_ASYNC | SND_FILENAME);
PlaySound(byteAudio, SND_MEMORY, SND_ASYNC | SND_MEMORY);
printf("vkey: %x, flag: %d\n",input.data.keyboard.VKey, input.data.keyboard.Flags);
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpComLine, int iShowCmd)
{
const char* CLASS_NAME = "RawInputClass";
FILE* fPtr;
long lFileSize;
//Gets file size
fPtr = fopen("res/click.wav", "rb");
if(!fPtr)
{
printf("Error while opening file!");
return -1;
}
fseek(fPtr, 0, SEEK_END);
lFileSize = ftell(fPtr);
rewind(fPtr);
//Reads file into byte-Array
byteAudio = (BYTE*)malloc(lFileSize * sizeof(BYTE));
if(!byteAudio)
{
printf("byteAudio; OUT_OF_MEMORY"); //If that happens, it is probably time for an upgrade
return -1;
}
fread(byteAudio, lFileSize, 1, fPtr);
fclose(fPtr);
//Create Message-Only window
WNDCLASS wnd = { 0 };
wnd.hInstance = GetModuleHandle(NULL);
wnd.lpfnWndProc = WinProc;
wnd.lpszClassName = CLASS_NAME;
RegisterClass(&wnd);
HWND hWnd = CreateWindowExW(0, CLASS_NAME, TEXT("THE WINDOW IS INVISIBLE SO WHO CARES!"),
0, 0, 0, 0, 0, HWND_MESSAGE, NULL, GetModuleHandle(NULL), NULL);
RAWINPUTDEVICE rid = { 0 };
rid.usUsagePage = 0x01;
rid.usUsage = 0x06; //keyboard
rid.dwFlags = RIDEV_INPUTSINK | RIDEV_NOLEGACY;
rid.hwndTarget = hWnd;
RegisterRawInputDevices(&rid, 1, sizeof(RAWINPUTDEVICE));
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

You are using a polling method, which is slow.
while(GetMessage(&msg, NULL, 0, 0))
{
/* stuff */
}
Read about what interrupts are and for what those interrupts are used. These will solve your problem.

Related

Why does the Sleep() function prevent the entire loop from working?

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

Why terminal are not showing printf messages whening running win32 window

I am running the normal win32 API, but all messages are displayed shortly after execution.
I was testing the callback of the input keys for my game and nothing happens when I press any button, but after closing the application everything goes back to normal and I have no idea what I'm doing wrong
Window file
#include <stdio.h>
#include <stdlib.h>
#include "./window.h"
Window wind;
LRESULT window_callback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
switch (uMsg) {
case WM_DESTROY: {
PostQuitMessage(0);
exit(EXIT_SUCCESS);
} break;
case WM_KEYUP: {
printf("SOME MESSAGE");
} break;
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
void init_window() {
WNDCLASSA window_class = {0};
window_class.style = CS_HREDRAW|CS_VREDRAW;
window_class.lpfnWndProc = window_callback;
window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
window_class.lpszClassName = "GAME_WINDOW_CLASS";
RegisterClassA(&window_class);
wind.ws_window = CreateWindowEx(0,
window_class.lpszClassName,
wind.title,
WS_VISIBLE | WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
wind.width, wind.height,
NULL, NULL, NULL, NULL);
wind.ws_canvas = GetDC(wind.ws_window);
}
void update_window() {
MSG message;
while (GetMessage(&message, NULL, 0, 0)) {
TranslateMessage(&message);
DispatchMessage(&message);
}
}
Window create_window(char * title, int width, int height) {
wind.title = title;
wind.width = width;
wind.height = height;
wind.running = 1;
init_window();
}
Main file
#include <stdio.h>
// #include "core/gui.h"
#include "core/window.h"
void main() {
create_window("Game Window", 600, 600);
while(1) {
// if(key_pressed("A"))
// printf("A was pressed!");
update_window();
}
}
Edit:
Puting an "\n" at the end of printf solve the problem
Win api: use TextOut() or ExTextOut() NOT printf(). You are writing to a HWND not a standard C handle.
Also Message Box() instead of printf for debug.
Next: WM_PAINT, and it's sequence BeginPaint...EndPaint. This should get you started.
Puting an "\n" at the end of printf solve the problem

What is the proper use of DTM_GETIDEALSIZE? Treating the returned size as pixels gives me very large, sometimes variable, heights

I'm trying to autosize some date-time picker controls with DTM_GETIDEALSIZE, but I don't quite get how to use this message properly. While widths returned seem to be fine if treated as pixels, heights seem to be not only way off, but sometimes change over time!
The sample program below creates three date-time pickers. The first shows both date and time with a custom format, the second shows dates only with DTS_SHORTDATECENTURYFORMAT, and the third shows times only with DTS_TIMEFORMAT. Resizing the window resizes all three pickers using DTM_GETIDEALSIZE. The widths seem to be fine. However, the heights of the first two are always 100, and the height of the last (the time picker) seems to start at 98 or so and decreases by two with each resize until it reaches 16, at which point it stays at 16.
This is tested both with Windows Vista and with Windows 7. Error checking in the below program is inconsistent (but some errors are not checked for the purposes of keeping this a simple test example). However, I will note that MSDN explicitly documents DTM_GETIDEALSIZE as only ever returning TRUE with no error condition specified.
Thanks.
// 3 june 2015
#define UNICODE
#define _UNICODE
#define STRICT
#define STRICT_TYPED_ITEMIDS
#define CINTERFACE
#define WINVER 0x0600
#define _WIN32_WINNT 0x0600
#define _WIN32_WINDOWS 0x0600
#define _WIN32_IE 0x0700
#define NTDDI_VERSION 0x06000000
#include <windows.h>
#include <commctrl.h>
#include <stdint.h>
#include <uxtheme.h>
#include <string.h>
#include <wchar.h>
#include <windowsx.h>
#include <vsstyle.h>
#include <vssym32.h>
#include <stdarg.h>
#include <oleacc.h>
#include <stdio.h>
void die(char *s)
{
// TODO
}
HWND mainwin;
HWND dtp1, dtp2, dtp3;
void idealsize(HWND dtp, char *n, int *x, int *y)
{
SIZE s;
s.cx = 0;
s.cy = 0;
printf("%s | %I32d ", n, SendMessageW(dtp, DTM_GETIDEALSIZE, 0, (LPARAM) (&s)));
printf("%I32d %I32d\n", s.cx, s.cy);
MoveWindow(dtp, *x, *y, s.cx, s.cy, TRUE);
*y += s.cy;
}
LRESULT CALLBACK wndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int x, y;
SIZE s;
switch (uMsg) {
case WM_CLOSE:
PostQuitMessage(0);
return 0;
case WM_SIZE:
x = 10;
y = 10;
idealsize(dtp1, "1", &x, &y);
idealsize(dtp2, "2", &x, &y);
idealsize(dtp3, "3", &x, &y);
break;
}
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
static HWND makedtp(DWORD style, WCHAR *format)
{
HWND hwnd;
hwnd = CreateWindowExW(WS_EX_CLIENTEDGE,
DATETIMEPICK_CLASSW, L"",
style | WS_TABSTOP | WS_CHILD | WS_VISIBLE,
0, 0, 100, 100,
mainwin, NULL, GetModuleHandle(NULL), NULL);
if (format != NULL)
if (SendMessageW(hwnd, DTM_SETFORMAT, 0, (LPARAM) format) == 0)
die("error applying format string to date/time picker in finishNewDateTimePicker()");
return hwnd;
}
#define GLI(what, buf, n) GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, what, buf, n)
HWND makeDateTimePicker(void)
{
WCHAR *date, *time, *datetime;
int ndate, ntime;
int n;
HWND hwnd;
// TODO verify that this always returns a century year
ndate = GLI(LOCALE_SSHORTDATE, NULL, 0);
if (ndate == 0)
die("error getting date string length in uiNewDateTimePicker()");
date = (WCHAR *) malloc(ndate * sizeof (WCHAR));
if (GLI(LOCALE_SSHORTDATE, date, ndate) == 0)
die("error geting date string in uiNewDateTimePicker()");
ntime = GLI(LOCALE_STIMEFORMAT, NULL, 0);
if (ndate == 0)
die("error getting time string length in uiNewDateTimePicker()");
time = (WCHAR *) malloc(ntime * sizeof (WCHAR));
if (GLI(LOCALE_STIMEFORMAT, time, ntime) == 0)
die("error geting time string in uiNewDateTimePicker()");
n = _scwprintf(L"%s %s", date, time);
datetime = (WCHAR *) malloc((n + 1) * sizeof (WCHAR));
snwprintf(datetime, n + 1, L"%s %s", date, time);
hwnd = makedtp(0, datetime);
free(datetime);
free(time);
free(date);
return hwnd;
}
HWND makeDatePicker(void)
{
return makedtp(DTS_SHORTDATECENTURYFORMAT, NULL);
}
HWND makeTimePicker(void)
{
return makedtp(DTS_TIMEFORMAT, NULL);
}
static void makeWindows(void)
{
mainwin = CreateWindowExW(0,
L"mainwin", L"Full Window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
500, 500,
NULL, NULL, GetModuleHandle(NULL), NULL);
dtp1 = makeDateTimePicker();
dtp2 = makeDatePicker();
dtp3 = makeTimePicker();
}
void initCommonControls(BOOL);
int main(int argc, char *argv[])
{
WNDCLASSW wc;
MSG msg;
HBRUSH b;
initCommonControls(TRUE);
ZeroMemory(&wc, sizeof (WNDCLASSW));
wc.lpszClassName = L"mainwin";
wc.lpfnWndProc = wndProc;
wc.hInstance = GetModuleHandle(NULL);
wc.hIcon = LoadIconW(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
RegisterClassW(&wc);
makeWindows();
ShowWindow(mainwin, SW_SHOWDEFAULT);
UpdateWindow(mainwin);
while (GetMessageW(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
static const char manifest[] = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n<assemblyIdentity\n version=\"1.0.0.0\"\n processorArchitecture=\"*\"\n name=\"CompanyName.ProductName.YourApplication\"\n type=\"win32\"\n/>\n<description>Your application description here.</description>\n<dependency>\n <dependentAssembly>\n <assemblyIdentity\n type=\"win32\"\n name=\"Microsoft.Windows.Common-Controls\"\n version=\"6.0.0.0\"\n processorArchitecture=\"*\"\n publicKeyToken=\"6595b64144ccf1df\"\n language=\"*\"\n />\n </dependentAssembly>\n</dependency>\n</assembly>\n";
static ULONG_PTR comctlManifestCookie;
static HMODULE comctl32;
void initCommonControls(BOOL comctl6)
{
WCHAR temppath[MAX_PATH + 1];
WCHAR filename[MAX_PATH + 1];
HANDLE file;
DWORD nExpected, nGot;
ACTCTX actctx;
HANDLE ac;
INITCOMMONCONTROLSEX icc;
FARPROC f;
// this is listed as WINAPI in both Microsoft's and MinGW's headers, but not on MSDN for some reason
BOOL (*WINAPI ficc)(const LPINITCOMMONCONTROLSEX);
if (comctl6) {
if (GetTempPathW(MAX_PATH + 1, temppath) == 0)
die("getting temporary path for writing manifest file");
if (GetTempFileNameW(temppath, L"manifest", 0, filename) == 0)
die("getting temporary filename for writing manifest file");
file = CreateFileW(filename, GENERIC_WRITE,
0, // don't share while writing
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (file == NULL)
die("creating manifest file");
nExpected = (sizeof manifest / sizeof manifest[0]) - 1; // - 1 to omit the terminating null character)
if (WriteFile(file, manifest, nExpected, &nGot, NULL) == 0)
die("writing manifest file");
if (nGot != nExpected)
die("short write to manifest file");
if (CloseHandle(file) == 0)
die("closing manifest file (this IS an error here because not doing so will prevent Windows from being able to use the manifest file in an activation context)");
ZeroMemory(&actctx, sizeof (ACTCTX));
actctx.cbSize = sizeof (ACTCTX);
actctx.dwFlags = ACTCTX_FLAG_SET_PROCESS_DEFAULT;
actctx.lpSource = filename;
ac = CreateActCtx(&actctx);
if (ac == INVALID_HANDLE_VALUE)
die("creating activation context for synthesized manifest file");
if (ActivateActCtx(ac, &comctlManifestCookie) == FALSE)
die("activating activation context for synthesized manifest file");
}
ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX));
icc.dwSize = sizeof (INITCOMMONCONTROLSEX);
icc.dwICC = ICC_STANDARD_CLASSES | ICC_PROGRESS_CLASS | ICC_TAB_CLASSES | ICC_LISTVIEW_CLASSES | ICC_UPDOWN_CLASS | ICC_BAR_CLASSES | ICC_DATE_CLASSES;
comctl32 = LoadLibraryW(L"comctl32.dll");
if (comctl32 == NULL)
die("loading comctl32.dll");
f = GetProcAddress(comctl32, "InitCommonControlsEx");
if (f == NULL)
die("loading InitCommonControlsEx()");
ficc = (BOOL (*WINAPI)(const LPINITCOMMONCONTROLSEX)) f;
if ((*ficc)(&icc) == FALSE)
die("initializing Common Controls (comctl32.dll)");
}

How do I get TTM_POPUP to not fade out instantly and then come back?

I've decided that th easiest way to handle tooltips in my table control is to handle the initial delay myself, create the tooltip dynamically, and then destroy it when it's not needed anymore. My problem is that when I use TTM_POPUP to show the tooltip, it fades out instantly and comes back again some time later. I'd rather have it fade in and stay there once.
My hypothesis is that the tooltip is handling the tooltip delay itself, and fading the existing tip out and back in. I'm not sure what to do about that. Should I just set the delay time to 0 and expect it to work (without worrying about TTM_POPUP at all)? Or is there a better way? Or am I wrong?
The program below demonstrates what's going on. Simply hover the mouse over the window and you should see it.
Tested on Windows XP and Windows 7. This is strictly with Common Controls 6.
EDIT Okay, I tried adding a TTM_SETDELAYTIME to set the initial time to 0 (not reflected in the program below). It didn't work. If I keep the TTM_POPUP afterward, it just fades out even faster than usual (which proves my hypothesis, maybe). If I remove the TTM_POPUP, it doesn't show up until I move the mouse anyway. So is there something else I can do to just get my tooltip to pop up?
Thanks!
// 5 april 2015
// based on wintooltipsubclasstest.c 31 march-2 april 2015
#define UNICODE
#define _UNICODE
#define STRICT
#define STRICT_TYPED_ITEMIDS
#define CINTERFACE
// get Windows version right; right now Windows XP
#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
#define _WIN32_WINDOWS 0x0501 /* according to Microsoft's winperf.h */
#define _WIN32_IE 0x0600 /* according to Microsoft's sdkddkver.h */
#define NTDDI_VERSION 0x05010000 /* according to Microsoft's sdkddkver.h */
#include <windows.h>
#include <commctrl.h>
#include <windowsx.h>
#include <stdio.h>
#include <stdlib.h>
void die(char *why)
{
fprintf(stderr, "error %s: %I32u\n", why, GetLastError());
abort();
}
HWND tooltip = NULL;
HINSTANCE hInstance;
void makeTooltip(HWND hwnd)
{
TOOLINFOW ti;
tooltip = CreateWindowExW(WS_EX_TOOLWINDOW,
TOOLTIPS_CLASSW, L"",
WS_POPUP | TTS_NOPREFIX,
0, 0,
0, 0,
hwnd, NULL, hInstance, NULL);
if (tooltip == NULL)
die("creating tooltip");
ZeroMemory(&ti, sizeof (TOOLINFOW));
ti.cbSize = TTTOOLINFOW_V2_SIZE;
ti.uFlags = TTF_IDISHWND | TTF_SUBCLASS | TTF_TRANSPARENT;
ti.hwnd = hwnd;
ti.uId = (UINT_PTR) hwnd;
ti.hinst = hInstance;
ti.lpszText = L"this is a tooltip! wow!";
if (SendMessageW(tooltip, TTM_ADDTOOL, 0, (LPARAM) (&ti)) == FALSE)
die("setting up tooltip");
}
LPARAM last = 0;
LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_CLOSE:
PostQuitMessage(0);
break;
case WM_MOUSEMOVE:
// cheap way of testing for hovers
if (lParam != last)
SetTimer(hwnd, 1, GetDoubleClickTime(), NULL);
last = lParam;
break;
case WM_TIMER:
if (wParam != 1)
break;
KillTimer(hwnd, 1);
makeTooltip(hwnd);
SendMessage(tooltip, TTM_POPUP, 0, 0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
void initCommonControls(BOOL);
int main(int argc, char *argv[])
{
BOOL comctl5;
WNDCLASSW wc;
HWND mainwin;
MSG msg;
hInstance = GetModuleHandle(NULL);
comctl5 = FALSE;
if (argc > 1)
comctl5 = strcmp(argv[1], "comctl5") == 0;
initCommonControls(comctl5);
ZeroMemory(&wc, sizeof (WNDCLASSW));
wc.lpszClassName = L"mainwin";
wc.lpfnWndProc = wndproc;
wc.hIcon = LoadIconW(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
wc.hInstance = GetModuleHandle(NULL);
if (RegisterClassW(&wc) == 0)
die("registering main window class");
mainwin = CreateWindowExW(0,
L"mainwin", L"Main Window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, GetModuleHandle(NULL), NULL);
if (mainwin == NULL)
die("creating main window");
ShowWindow(mainwin, SW_SHOWDEFAULT);
UpdateWindow(mainwin);
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
// MSDN doesn't list a constant that only includes tooltips but says this and a few others do
#define wantedICCClasses (ICC_BAR_CLASSES)
static ULONG_PTR comctlManifestCookie;
static HMODULE comctl32;
// note that this is an 8-bit character string we're writing; see the encoding clause
static const char manifest[] = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n<assemblyIdentity\n version=\"1.0.0.0\"\n processorArchitecture=\"*\"\n name=\"CompanyName.ProductName.YourApplication\"\n type=\"win32\"\n/>\n<description>Your application description here.</description>\n<dependency>\n <dependentAssembly>\n <assemblyIdentity\n type=\"win32\"\n name=\"Microsoft.Windows.Common-Controls\"\n version=\"6.0.0.0\"\n processorArchitecture=\"*\"\n publicKeyToken=\"6595b64144ccf1df\"\n language=\"*\"\n />\n </dependentAssembly>\n</dependency>\n</assembly>\n";
void initCommonControls(BOOL comctl5)
{
WCHAR temppath[MAX_PATH + 1];
WCHAR filename[MAX_PATH + 1];
HANDLE file;
DWORD nExpected, nGot;
ACTCTX actctx;
HANDLE ac;
INITCOMMONCONTROLSEX icc;
FARPROC f;
// this is listed as WINAPI in both Microsoft's and MinGW's headers, but not on MSDN for some reason
BOOL (*WINAPI ficc)(const LPINITCOMMONCONTROLSEX);
if (!comctl5) {
if (GetTempPathW(MAX_PATH + 1, temppath) == 0)
die("getting temporary path for writing manifest file");
if (GetTempFileNameW(temppath, L"manifest", 0, filename) == 0)
die("getting temporary filename for writing manifest file");
file = CreateFileW(filename, GENERIC_WRITE,
0, // don't share while writing
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (file == NULL)
die("creating manifest file");
nExpected = (sizeof manifest / sizeof manifest[0]) - 1; // - 1 to omit the terminating null character)
if (WriteFile(file, manifest, nExpected, &nGot, NULL) == 0)
die("writing manifest file");
if (nGot != nExpected)
die("short write to manifest file");
if (CloseHandle(file) == 0)
die("closing manifest file (this IS an error here because not doing so will prevent Windows from being able to use the manifest file in an activation context)");
ZeroMemory(&actctx, sizeof (ACTCTX));
actctx.cbSize = sizeof (ACTCTX);
actctx.dwFlags = ACTCTX_FLAG_SET_PROCESS_DEFAULT;
actctx.lpSource = filename;
ac = CreateActCtx(&actctx);
if (ac == INVALID_HANDLE_VALUE)
die("creating activation context for synthesized manifest file");
if (ActivateActCtx(ac, &comctlManifestCookie) == FALSE)
die("activating activation context for synthesized manifest file");
}
ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX));
icc.dwSize = sizeof (INITCOMMONCONTROLSEX);
icc.dwICC = wantedICCClasses;
comctl32 = LoadLibraryW(L"comctl32.dll");
if (comctl32 == NULL)
die("loading comctl32.dll");
f = GetProcAddress(comctl32, "InitCommonControlsEx");
if (f == NULL)
die("loading InitCommonControlsEx()");
ficc = (BOOL (*WINAPI)(const LPINITCOMMONCONTROLSEX)) f;
if ((*ficc)(&icc) == FALSE)
die("initializing Common Controls (comctl32.dll)");
}

RegisterDeviceNotification Getting WM_DEVICECHANGE

I want to catch message of WM_DEVICECHANGE.But, there is a problem which i can not understand.I want to see when usb or cd inserted.Maybe my notification filter is wrong.
I m using radstudio and the language of its c,also its commandline application.I think everything is obvious in code.What am i doing wrong,i created window for only getting messages.Also i did not understand how it message going to WndProc from message loop.
#pragma hdrstop
#pragma argsused
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <dbt.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch (uiMsg)
{
case WM_DEVICECHANGE:
{
MessageBox(0,"a","b",1);
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
BOOL bRet;
HANDLE a;
HWND lua;
HANDLE hInstance;
MSG msg;
WNDCLASSEX wndClass;
HANDLE hVolNotify;
DEV_BROADCAST_DEVICEINTERFACE dbh;
DEV_BROADCAST_VOLUME NotificationFilter;
lua = CreateWindow("lua", NULL, WS_MINIMIZE, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
wndClass.lpfnWndProc = WndProc;
ZeroMemory(&NotificationFilter, sizeof (NotificationFilter));
NotificationFilter.dbcv_size = sizeof (NotificationFilter);
NotificationFilter.dbcv_devicetype = DBT_DEVTYP_VOLUME;
a = RegisterDeviceNotification(lua,&NotificationFilter,DEVICE_NOTIFY_WINDOW_HANDLE);
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
MessageBox(0,"o","b",1);
if (bRet == -1)
{
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
What am i doing wrong,i created window for only getting messages.
You are asking CreateWindow() to create a window of class "lua" but you have not actually registered the "lua" class via RegisterClass/Ex() before calling CreateWindow(), and you are not checking to see if CreateWindow() returns a NULL window handle on failure.
Also i did not understand how it message going to WndProc from message loop.
That is handled by DispatchMessage(). You need to assign wndClass.lpfnWndProc and register it with RegisterClass() before calling CreateWindow(). Afterwards, when DispatchMessage() sees a message that targets the window created by CreateWindow(), it knows that WndProc() has been associated with that window and will call it directly, passing it the message.
Try this instead:
#pragma hdrstop
#pragma argsused
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <dbt.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
if (uiMsg == WM_DEVICECHANGE)
{
MessageBox(NULL, TEXT("WM_DEVICECHANGE"), TEXT("WndProc"), MB_OK);
return 0;
}
return DefWindowProc(hWnd, uiMsg, wParam, lParam);
}
int _tmain(int argc, _TCHAR* argv[])
{
HINSTANCE hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(NULL));
WNDCLASS wndClass = {0};
wndClass.lpfnWndProc = &WndProc;
wndClass.lpszClassName = TEXT("lua");
wndClass.hInstance = hInstance;
if (RegisterClass(&wndClass))
{
HWND lua = CreateWindow(wndClass.lpszClassName, NULL, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
if (lua != NULL)
{
DEV_BROADCAST_VOLUME NotificationFilter = {0};
NotificationFilter.dbcv_size = sizeof(NotificationFilter);
NotificationFilter.dbcv_devicetype = DBT_DEVTYP_VOLUME;
HDEVNOTIFY hVolNotify = RegisterDeviceNotification(lua, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
if (hVolNotify != NULL)
{
MSG msg;
while( GetMessage(&msg, NULL, 0, 0) > 0 )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnregisterDeviceNotification(hVolNotify);
}
DestroyWindow(lua);
}
UnregisterClass(wndClass.lpszClassName, hInstance);
}
return 0;
}
For added measure, you can use CreateWindowEx() instead of CreateWindow() to create a message-only window instead, if desired:
HWND lua = CreateWindowEx(0, wndClass.lpszClassName, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, hInstance, NULL);
You need to set the dbcv_unitmask field of the DEV_BROADCAST_VOLUME structure to indicate which drive letters you're interested in. If you want to see media changes you also need to set the DBTF_MEDIA flag in the dbcv_flags field.

Resources