I have a WINAPI application and I want to start it by drag and drop of a file onto the Icon of my application.
The main window is created with
hWnd = CreateWindow("app-name",
"",
WS_OVERLAPPEDWINDOW
| WS_CLIPSIBLINGS
| WS_EX_ACCEPTFILES,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
NULL, NULL, hInstance, NULL);
and has a WndProc with
static LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
DragAcceptFiles(hWnd, TRUE);
// etc (other controls and windows created here)
}
break;
case WM_DROPFILES:
{
char filename[MAX_PATH];
HDROP hDropInfo = (HDROP) wParam;
DragQueryFile(hDropInfo, 0, filename, MAX_PATH);
// etc open file...
}
break;
The problem is that when I drag and drop a file from the Explorer onto my program Icon, the program is started, but I don't receive any WM_DROPFILES message. That is also the case if I configure the file-type to "open with" my program - it's started but no WM_DROPFILES is received.
But once the program is initialized I can drag a drop a file into some of its windows, and then I get the WM_DROPFILES message as I expect it.
What am I doing wrong?
Thanks for advice!
BR Alfred
As mentioned, you can use the lpCmdLine argument of WinMain function:
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// remove double quotes from filename with spaces
if( lpCmdLine[0] == '"' ) {
lpCmdLine++;
lpCmdLine[strlen(lpCmdLine)-1] = 0;
}
printf("%s\n",lpCmdLine);
// ...
}
Related
I found that on windows 11, even if I created windows with WS_EX_TOPMOST style, the window still get covered by taskbar if you click anywhere on the taskbar. The only exception to this is task manager. How does it achieve it?
I want a feature to draw stuff on the taskbar, or at least mimic that behavior using a transparent window. I think there used to be 2 ways to achieve that:
Get DC of taskbar and draw (which no longer work on Windows11)
Create a transparent window and keep it on top of taskbar (issues with how to keep it topmost)
I kind of get stuck now. Any help is appreciated.
UPDATE:
I just noticed that the taskbar window, shown as DesktopWindowXamlSource in Spy++ does not have the WS_EX_TOPMOST style set. I guess it's possible to cover it for some ways?
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <iostream>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(
WS_EX_TOPMOST, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
// Run the message loop.
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// All painting occurs here, between BeginPaint and EndPaint.
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
And this is how the window get covered by taskbar when I click start.
I am new to programming with the Win32 API, and have been trying to figure out why this application is not returning when I close the window.
#include <windows.h>
LRESULT CALLBACK WindowProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
);
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{
// Register window class
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"Window Class";
RegisterClass(&wc);
// Create window
HWND window = CreateWindowEx(
0,
wc.lpszClassName,
L"Window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
// Show window
ShowWindow(window, nShowCmd);
// Main Program Loop
MSG msg = {};
while (msg.message != WM_QUIT)
{
if (PeekMessage(&msg, window, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
LRESULT CALLBACK WindowProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
I stepped through the code with a debugger and saw that after destroying the window, the value of the message was WM_PAINT, and so the program was continuously looping, but I don't understand why WM_QUIT is not being posted.
By providing window as an argument to PeekMessage you are telling it you only want to retrieve messages posted or sent to that window.
But WM_QUIT is a thread message - it's not associated with any given window. To retrieve it you need to call PeekMessage with nullptr for the window filter.
In addition to Jonathan Potter's answer, your message loop should be using GetMessage() instead of PeekMessage():
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
GetMessage() blocks the calling thread until a message arrives, and returns FALSE when WM_QUIT is received and the hWnd parameter is NULL.
By using PeekMessage(), you are running a tight busy loop, and the contents of msg are indeterminate when PeekMessage() returns FALSE when no message is available.
I've starting watching the handmade hero videos and I'm trying to make a win32 window but the CreateWindowEx() function keeps failing.
I checked the error code and I get 1407.
Code is below.
Thanks in advance.
#include <Windows.h>
LRESULT CALLBACK WindowProcedure(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
LRESULT result;
switch (uMsg)
{
case WM_ACTIVATEAPP:
{
OutputDebugStringA("The window is now active");
break;
}
case WM_SIZE:
{
OutputDebugStringA("The window is now being resized");
break;
}
case WM_CREATE:
{
OutputDebugStringA("The window has been created");
break;
}
default:
{
result = DefWindowProc(hwnd, uMsg, wParam, lParam);
break;
}
}
return result;
};
int CALLBACK WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
)
{
WNDCLASS GameWindow;
GameWindow.style = CS_OWNDC|CS_HREDRAW|CS_VREDRAW;
GameWindow.lpfnWndProc = WindowProcedure;
GameWindow.hInstance = hInstance;
// HICON hIcon;
GameWindow.lpszClassName = "HandmadeHeroWindowClass";
RegisterClass(&GameWindow);
if (HWND GameWindowHandle = CreateWindowEx(
0,
GameWindow.lpszClassName,
"Handmade Hero",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
hInstance,
0
))
{
for (;;)
{
MSG message;
BOOL messageResult = GetMessage(&message, GameWindowHandle, 0, 0);
if (messageResult != 0)
{
DispatchMessage(&message);
}
else if (messageResult == 0)
{
break;
}
else
{
// ERROR
}
}
}
else
{
OutputDebugStringA("Couldn't create window");
}
DWORD error = GetLastError();
return 0;
};
Your window procedure returns an uninitialized variable in every path except for default:, this is undefined behavior and failure of window creation is entirely possible.
For WM_CREATE, the documentation says:
If an application processes this message, it should return zero to continue creation of the window.
As Michael noted in the comments, RegisterClass is failing. Same category of mistake, you're passing a WNDCLASS structure leaving most members uninitialized.
Thanks to Remy Lebeau for the answer, the problem was that my WNDCLASS had uninitialized values for all fields except those I changed, this caused the RegisterClass() to fail and consequently the CreateWindowEx() to fail.
I changed WNDCLASS declaration to this:
WNDCLASS GameWindow = {0};
Thanks to everyone who helped.
I am having trouble correctly running a message loop from within a message handler. In effect replicating how DialogBox() processes messages, minus all of the windowing.
Simply invoking GetMessage() from within a message handler nearly works except when the WM_SYSKEYDOWN event opening the system menu also triggers the entry into a sub-loop. After this weird things happen, with keys presses being swallowed and WM_MOUSEMOVE messages relative to the system menu getting sent to the main window.
For the record this happens both in Windows 8 and XP.
To give some context I am attempting a threading model where a (windowless) worker thread communicates through blocking SendMessage calls back to the main window acting as a server. These actions may require further input or depend on other I/O and thus regular messages need to be processed until the reply is ready.
I'm fairly certain that this is a basic mistake or misunderstanding on my part, just like last time I posted here, but I can't quite seem to work out what I'm doing wrong on my own.
Here is my repro case. Try navigating after pressing ALT+SPACE to open the system menu,
#include <windows.h>
BOOL update;
LRESULT WINAPI WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
MSG msg;
char text[256];
switch(uMsg) {
case WM_DESTROY:
ExitProcess(0);
// Trigger an update on input
case WM_SYSKEYDOWN:
update = TRUE;
break;
// Display the update from the worker thread, returning once it is time to
// ask for the next one
case WM_USER:
wsprintf(text, TEXT("%u"), (unsigned int) lParam);
SetWindowText(hwnd, text);
while(!update && GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
update = FALSE;
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
DWORD WINAPI ThreadProc(void *hwnd) {
// Submit updates as quickly as possible
LONG sequence = 1;
for(;;)
SendMessage(hwnd, WM_USER, 0, sequence++);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCommandLine, int nCmdShow) {
HWND hwnd;
MSG msg;
// Create our window
WNDCLASS windowClass = { 0 };
windowClass.lpfnWndProc = WindowProc;
windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
windowClass.hCursor = NULL;
windowClass.lpszClassName = TEXT("Repro");
RegisterClass(&windowClass);
hwnd = CreateWindow(TEXT("Repro"), TEXT("Repro"),
WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL,
hInstance, 0);
// Launch the worker thread
CreateThread(NULL, 0, ThreadProc, hwnd, 0, NULL);
// And run the primary message loop
while(GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
Modal message loops are perfectly fine. Raymond Chen has a series of articles on writing modal message loops properly.
One thing I notice: Your thread should post the message, not send it; SendMessage calls directly into the window proc. Don't use PostThreadMessage, either; that's designed for threads without visible UI (and the nested DispatchMessage won't know how to dispatch the thread message, resulting in dropped messages).
I'm trying to know when a console window has moved so I created a new message-only window to get the messages of the console but I don't know if it is working because the message is apparently never being received.
#define WINVER 0x0501
#include <windows.h>
#include <iostream>
WNDPROC glpfnConsoleWindow; // NEW
using namespace std;
LRESULT APIENTRY MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_SIZE:
cout<<"Window moved"<<endl;
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
// NEW
return CallWindowProc(glpfnConsoleWindow, hwnd, uMsg, wParam, lParam);
//return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
HWND hwnd;
MSG Msg;
const char lpcszClassName[] = "messageClass";
WNDCLASSEX WindowClassEx;
// == NEW
HWND consHwnd;
consHwnd = GetConsoleWindow();
glpfnConsoleWindow = (WNDPROC)SetWindowLong(consHwnd, GWL_WNDPROC, (LONG)MainWndProc);
// ==
ZeroMemory(&WindowClassEx, sizeof(WNDCLASSEX));
WindowClassEx.cbSize = sizeof(WNDCLASSEX);
WindowClassEx.lpfnWndProc = MainWndProc;
WindowClassEx.hInstance = hInstance;
WindowClassEx.lpszClassName = lpcszClassName;
if (RegisterClassEx(&WindowClassEx) != 0)
{
// Create a message-only window
hwnd = CreateWindowEx(0, lpcszClassName, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, hInstance, NULL);
if (hwnd != NULL)
cout<<"Window created"<<endl;
else
UnregisterClass(lpcszClassName, hInstance);
}
ShowWindow(hwnd,nCmdShow);
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return (int)Msg.wParam;
}
Perhaps you should process WM_MOVE, which, according to the documentation, is sent after the window has been moved. WM_SIZE is sent when the size changes.
I think your problem is that the WM_MOVE and WM_SIZE messages are going to the console window rather than to your hidden window.
I suspect you'll have to call GetConsoleWindow to get the console window handle, and then call SetWindowLong to attach your window proc to the console window. Be sure to pass messages on to the original window proc.