I do this
HWND lParentWinHandle = GetForegroundWindow();
if (!CreateProcessA(NULL, lArgs, NULL, NULL, FALSE,
CREATE_NEW_CONSOLE, NULL, NULL,
&StartupInfo, &ProcessInfo))
{ ... }
// at this point a new window appears in the foreground.
HWND lChildWinHandle = GetForegroundWindow();
SetParent(lChildWinHandle , lParentWinHandle );
but lParentWinHandle and lChildWinHandle have the same value !
If you want to get the handle of the new process. You should use the process and thread handle of the PROCESS_INFO structure.
And according to the documentation you should wit with WaitForInputIdle until you start the search.
Code is similar to this (it is just a dirty sample no error checking):
BOOL CALLBACK EnumThreadWndProc(HWND hWnd, LPARAM lParam)
{
hWndMain = hWnd; // got the main window...
return FALSE; // ...stop enum
}
// ...
STARTUPINFO siStartInfo = {0};
PROCESS_INFORMATION piProcessInfo = {0};
CreateProcessA(NULL, pCmd,
NULL, NULL, FALSE, 0, NULL, NULL,
&siStartInfo,&piProcessInfo);
if(WaitForInputIdle(piProcessInfo.hProcess, 5000)==0)
{
EnumThreadWindows(piProcessInfo.dwThreadId,
EnumThreadWndProc, NULL);
}
else
{
// Could not start program
...
}
// ...
But please read Is it legal to have a cross-process parent/child or owner/owned window relationship?
Related
I trying to write program that is working properly with GetRawInputBuffer API and consuming all input with little overhead on separate thread.
But I found that WM_INPUT_DEVICE_CHANGE messages get lost when reading RAWINPUT via GetRawInputBuffer instead of usual WM_INPUT approach.
My code (I removed some error checking):
void RawInputDeviceManager::RawInputManagerImpl::ThreadRun()
{
// if I set it to true then WM_INPUT_DEVICE_CHANGE does not come
constexpr bool buffered = false;
// prepare buffer for up to 32 raw input messages
m_InputDataBuffer.resize(std::max({ sizeof(RAWKEYBOARD), sizeof(RAWMOUSE), sizeof(RAWHID) }) * 32);
m_WakeUpEvent = ::CreateEventExW(nullptr, nullptr, 0, EVENT_ALL_ACCESS);
WNDCLASSEXW wc{};
wc.cbSize = sizeof(wc);
wc.lpszClassName = L"Message";
wc.lpfnWndProc = [](HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -> LRESULT
{
RawInputManagerImpl* manager = reinterpret_cast<RawInputManagerImpl*>(::GetWindowLongPtrW(hWnd, 0));
if (manager)
{
switch (message)
{
case WM_INPUT_DEVICE_CHANGE: // <== get lost after ::GetRawInputBuffer(..) call
{
manager->OnInputDeviceChange();
return 0;
}
case WM_INPUT:
{
manager->OnInput(reinterpret_cast<RAWINPUT*>(lParam));
return 0;
}
}
}
return ::DefWindowProcW(hWnd, message, wParam, lParam);
};
wc.cbWndExtra = sizeof(RawInputManagerImpl*); // add some space for this pointer
wc.hInstance = ::GetModuleHandleW(nullptr);
ATOM classAtom = ::RegisterClassExW(&wc);
HWND hWnd = ::CreateWindowExW(0, reinterpret_cast<LPCWSTR>(classAtom), nullptr, 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, wc.hInstance, 0);
::SetWindowLongPtrW(hWnd, 0, reinterpret_cast<LONG_PTR>(this));
Register(hWnd);
// enumerate devices before start
OnInputDeviceChange();
// main message loop
while (m_Running)
{
MSG msg;
if (buffered)
OnInputBuffered();
while (true)
{
bool haveMessage = false;
if (buffered)
{
// retrieve any message but WM_INPUT
haveMessage = ::PeekMessageW(&msg, 0, 0, WM_INPUT - 1, PM_REMOVE) ||
::PeekMessageW(&msg, 0, WM_INPUT + 1, 0, PM_REMOVE);
}
else
{
// retrieve any message
haveMessage = ::PeekMessageW(&msg, 0, 0, 0, PM_REMOVE);
}
if (!haveMessage)
break;
// not needed since we are not interested in WM_CHAR or WM_DEADCHAR
//::TranslateMessage(&msg);
// dispatch message to WndProc
::DispatchMessageW(&msg);
}
// wait for new messages
::MsgWaitForMultipleObjectsEx(1, &m_WakeUpEvent, INFINITE, QS_ALLEVENTS, MWMO_INPUTAVAILABLE);
}
Unregister();
::DestroyWindow(hWnd);
::UnregisterClassW(reinterpret_cast<LPCWSTR>(classAtom), wc.hInstance);
}
bool RawInputDeviceManager::RawInputManagerImpl::Register(HWND hWnd)
{
RAWINPUTDEVICE rid[] =
{
// register for all HID device generic types (keyboard/mouse/joystick etc)
{
HID_USAGE_PAGE_GENERIC,
0,
RIDEV_DEVNOTIFY | RIDEV_INPUTSINK | RIDEV_PAGEONLY,
hWnd
}
};
return ::RegisterRawInputDevices(rid, ARRAYSIZE(rid), sizeof(RAWINPUTDEVICE));
}
void RawInputDeviceManager::RawInputManagerImpl::OnInputBuffered()
{
// Processing all pending WM_INPUT messages in message queue
while (true)
{
UINT size = static_cast<UINT>(m_InputDataBuffer.size());
RAWINPUT* input = reinterpret_cast<RAWINPUT*>(m_InputDataBuffer.data());
UINT result = ::GetRawInputBuffer(input, &size, sizeof(RAWINPUTHEADER));
if (result == 0 || result == static_cast<UINT>(-1))
return;
// hack for a undefined QWORD used in NEXTRAWINPUTBLOCK macro
using QWORD = __int64;
for (; result; result--, input = NEXTRAWINPUTBLOCK(input))
{
OnInput(input);
}
}
}
Is this bug in Windows?
PS: This WM_INPUT_DEVICE_CHANGE event was added in Windows Vista and have proven that it has some bugs in its implementation.
PPS: As a workaround I can subscribe to WM_DEVICECHANGE message via RegisterDeviceNotification but I not sure if I doing something wrong in this case.
I'm building a real time graphics application and I've noticed that under certain conditions the operating system will post nonqueued messages that block my program. For example, this will happen during the entire time that a user is resizing the window.
To solve this problem, I would like to put the window on a different thread, but I read that this is a terrible idea because of the way windows messages work. Is it still a problem if I process all messages on the other thread, and never send or receive messages from the main thread? I'm thinking about setting up my own message queue with a mutex so I can pass the data I care about back and forth. If this won't work, are there any other ways to solve this problem?
Here's a simplified version of the code I'm running:
LRESULT CALLBACK WindowCallback(HWND window, UINT message, WPARAM wParam, LPARAM lParam) {
if (message == WM_SIZE) {
// This event gets called a lot
// Handle window resize
return 0;
} else {
return DefWindowProc(window, message, wParam, lParam);
}
}
int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int numCmd) {
WNDCLASSA class = {};
class.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
class.lpfnWndProc = WindowCallback;
class.lpszClassName = "Name";
class.hInstance = instance;
RegisterClassA(&class)l
HWND win = CreateWindowExA(0, class.lpszClassName, "Name",
WS_VISIBLE | WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 600, 400,
0, 0, instance, 0);
while(true) {
// Handle Events
MSG msg = {};
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) {
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Do Updating of State and Graphics Rendering Here
}
}
Thanks in advance!
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'm creating a custom shell for Windows 7 / 8. How do I create a custom tray for my shell ? I'm aware that the tray window has the class name "Shell_TrayWnd". I tried creating it on my own and posted "TaskbarCreated" message using PostMessage but I'm not getting "WM_COPYDATA" message in my Tray WndProc. I'm missing out on something? Need help.
The code is as follows:
static LRESULT CALLBACK tray_proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_COPYDATA)
{
COPYDATASTRUCT *cpdata = (COPYDATASTRUCT*)lParam;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
int init_tray(void)
{
WNDCLASS wc;
memset(&wc, 0, sizeof(wc));
wc.lpfnWndProc = tray_proc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = TEXT("Shell_TrayWnd");
if (!RegisterClass(&wc))
{
return 0;
}
tray_window = CreateWindowEx(
WS_EX_TOOLWINDOW,
wc.lpszClassName,
NULL,
WS_POPUP,
0, 0, 0, 0,
NULL, NULL,
wc.hInstance,
NULL);
if (!tray_window)
return 0;
/* let running apps know that a new tray is around */
PostMessage(HWND_BROADCAST, RegisterWindowMessage(TEXT("TaskbarCreated")), 0, 0);
return 1;
}
Got it working!
SendNotifyMessage(HWND_BROADCAST, RegisterWindowMessage("TaskbarCreated"), 0, 0);
This message is the key. Without this, the Tray WndProc will not get data.
I'm trying to use win32api on user32.dll to get mouse wheel input.
I tried the following:
#state = Win32API.new('user32','DefWindowProc',['i'],'i')
p #state.call(0x0800)
But it keeps returning 0 no matter what I do with my mouse wheel.
I thought something is wrong with my mouse, so I tried to do:
#state = Win32API.new('user32','GetKeyState',['L'],'L')
p #state.call(0x01) #left key
p #state.call(0x02) #right key
It worked, so I'm not sure what's wrong with the mouse wheel.
Regards
Edit 2/20/2014:
I tried to do the following:
GetActiveWindow = Win32API.new('user32','GetForegroundWindow','','i')
DefWindowProc = Win32API.new('user32','DefWindowProc','iiii','i')
DefWindowProc.call(GetActiveWindow.call,
Then, I got stuck with the UINT / Message (2nd argument).
Edit 2/20/2014:
I found the WM_MOUSEWHEEL message
Latest revision:
GetActiveWindow = Win32API.new('user32','GetForegroundWindow','','i')
DefWindowProc = Win32API.new('user32','DefWindowProc','iiii','i')
#WM_MOUSEWHEEL message 0x020A
DefWindowProc.call(GetActiveWindow.call, 0x020A,
Now I just need to figure out the 3rd and 4th argument.
As I don't know Ruby, I will give here a solution in C/C++ using only standard Win32 APIs. If you can call Win32 APIs from Ruby, that will work.
Note that the solution involves "callback" and "pointer". I know that's possible in Python (for example, with the ctypes module) and hope that same thing is possible with Ruby.
Register a Window Class and create a Window from that class, the window will be a Message-Only Window, so it will be invisible, with no GUI.
Use the RegisterRawInputDevices API to ask for raw events from mouse devices.
Set up a message loop, with the standard GetMessage/DispatchMessage combo.
Process the sent WM_INPUT message in your Window Procedure
4.1. Allocate memory for the raw datas
4.2. Retrieve the raw datas
4.3. Filter for mouse event and wheel datas
4.4. Process (I just print the wheel delta)
4.5. Free allocated memory.
Below, full source code. Build with VS2012 Express on Windows 7.
#include <Windows.h>
#include <stdio.h>
LRESULT CALLBACK MyWindowProc( HWND, UINT, WPARAM, LPARAM );
int main( void ) {
WNDCLASS WndClass;
memset( &WndClass, 0, sizeof( WndClass ) );
WndClass.hInstance = GetModuleHandle( NULL );
WndClass.lpszClassName = L"MyRawInputClass";
WndClass.lpfnWndProc = MyWindowProc;
RegisterClass( &WndClass );
HWND hWnd = CreateWindow( WndClass.lpszClassName, NULL, 0, 0, 0, 0, 0,
HWND_MESSAGE, 0, WndClass.hInstance, 0 );
RAWINPUTDEVICE RawInputDevice;
RawInputDevice.usUsagePage = 0x01; // Generic Desktop Controls
RawInputDevice.usUsage = 0x02; // Mouse
RawInputDevice.dwFlags = RIDEV_INPUTSINK;
RawInputDevice.hwndTarget = hWnd;
BOOL bWin32Success = RegisterRawInputDevices( &RawInputDevice, 1,
static_cast<UINT>( sizeof( RAWINPUTHEADER ) ) );
BOOL bRet;
MSG msg;
while( ( bRet = GetMessage( &msg, hWnd, 0, 0 ) ) != 0 ) {
if (bRet != -1) {
DispatchMessage(&msg);
}
}
// NO GUI, UNREACHABLE
DestroyWindow( hWnd );
UnregisterClass( WndClass.lpszClassName, WndClass.hInstance );
return 0;
}
LRESULT CALLBACK MyWindowProc( HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam ) {
switch ( uiMsg ) {
case WM_INPUT: {
UINT dwSize;
HRAWINPUT hRawInput = reinterpret_cast<HRAWINPUT>( lParam );
UINT uiRetCode = GetRawInputData( hRawInput, RID_INPUT, NULL, &dwSize,
static_cast<UINT>( sizeof( RAWINPUTHEADER ) ) );
if ( uiRetCode != 0xffffffff ) {
LPBYTE lpb = new BYTE[ dwSize ];
uiRetCode = GetRawInputData( hRawInput, RID_INPUT, lpb, &dwSize,
static_cast<UINT>( sizeof( RAWINPUTHEADER ) ) );
if ( uiRetCode > 0 ) {
RAWINPUT* praw = reinterpret_cast<RAWINPUT*>( lpb );
if ( praw->header.dwType == RIM_TYPEMOUSE ) {
if ( praw->data.mouse.usButtonFlags & RI_MOUSE_WHEEL ) {
signed int siDelta = static_cast<SHORT>( praw->data.mouse.usButtonData );
printf( "WHEEL EVENT: Delta = %d\n", siDelta );
}
}
}
delete[] lpb;
}
break;
} // WM_INPUT
default:
return DefWindowProc( hWnd, uiMsg, wParam, lParam );
}
return 0;
}