I wrote a function to register window to recieve raw mouse input using a reference from msdn.
bool N_RegisterMouseRawInput()
{
RAWINPUTDEVICE rid;
rid.usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
rid.usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE
rid.dwFlags = RIDEV_NOLEGACY; // adds mouse and also ignores legacy mouse messages
rid.hwndTarget = nullptr;
if (RegisterRawInputDevices(&rid, 1, sizeof(rid)) == FALSE)
{
CYC_WIN32_LASTERROR_NOBREAK(false, "Raw Input RegisterRawInputDevices failed")
return false;
}
return true;
}
I call this once after calling CreateWindowEx and it is making the window unresponsive. The window still shows up, but you can't interact with it. It doesn't hang or crash either.
There are no error messages.
The application works fine if I remove call to this function.
Related
I'm using the WebBrowser control in my CDialog-based MFC window. The window allows printing, using code similar to this:
CComPtr<IWebBrowser2> pWebBrowser = this->GetIWebBrowser2();
if(pWebBrowser)
{
HRESULT hr;
COleVariant varNull;
if(SUCCEEDED(hr = pWebBrowser->ExecWB(
bDoPreview ? OLECMDID_PRINTPREVIEW : OLECMDID_PRINT,
OLECMDEXECOPT_PROMPTUSER, varNull, varNull)))
{
//All good
bRes = TRUE;
}
}
IWebBrowser2* GetIWebBrowser2()
{
IWebBrowser2* pBrowser = NULL;
LPUNKNOWN unknown = m_browser.GetControlUnknown();
if(unknown)
{
unknown->QueryInterface(IID_IWebBrowser2,(void **)&pBrowser);
if(unknown)
{
unknown->Release();
}
}
return pBrowser;
}
It works, except that if the document is large enough, pWebBrowser->ExecWB() seems to return right away and all the printing is done asynchronously. So in that case if the user closes my window (that houses this WebBrowser control) printing is aborted midway.
Thus my question, how do I wait for the printing to finish before I can allow closing of the host window?
I have these links with code:
WMMouseWheel not working in Delphi
How to disable MouseWheel if mouse is not over VirtualTreeView (TVirtualStringTree)
Translated it to C++ Builder but it doesn't work:
UPDATE: After narrowing the problem down it appears that WM_MOUSEWHEEL messages don't work over unfocused TVirtualStringTree control only, they work over other controls. When focus is on e.g. TMemo control, other TMemo control scrolls on wheel but not TVirtualStringTree control. When focus is on TVirtualStringTree it scrolls TVirtualStringTree but not other controls. So the problem is now specific to TVirtualStringTree only.
void __fastcall TForm1::ApplicationEventsMessage(tagMSG &Msg, bool &Handled)
{
TPoint Pt;
HWND Wnd;
if (Msg.message == WM_MOUSEWHEEL ||
Msg.message == WM_VSCROLL ||
Msg.message == WM_HSCROLL)
{
if (GetCursorPos(&Pt))
{
Wnd = WindowFromPoint(Pt);
// It must be a VCL control otherwise we could get access violations
if (IsWindowEnabled(Wnd) && FindControl(Wnd) != NULL)
{
Msg.hwnd = Wnd; // change the message receiver to the control under the cursor
}
}
}
}
Different version of the similar code, also doesn't work:
TPoint pnt;
TWinControl *ctrl;
if ((Msg.message == WM_MOUSEWHEEL ||
Msg.message == WM_VSCROLL ||
Msg.message == WM_HSCROLL) &&
GetCursorPos(&pnt))
{
ctrl = FindVCLWindow(pnt);
if (ctrl != NULL)
{
SendMessage(ctrl->Handle, Msg.message, Msg.wParam, Msg.lParam); // No effect
// SendMessage(ctrl->Handle, WM_VSCROLL, 1, 0); // This is the only thing that actually moves scrollbars but this is not exactly the same message like above
// Msg.hwnd = ctrl->Handle; // No effect
this->Caption=ctrl->Name; // This shows correct control name so the message IS GETTING THROUGH!
Handled = true;
}
}
It should work but it doesn't. Tried other code as well. No effect - mouse wheel does not operate on unfocused control. As you can see, I checked for all 3 variants of wheel messages, it gets correct control under the mouse, it shows that control name but the control doesn't receive wheel messages.
Any ideas what piece of the puzzle am I missing to get it to work?
As nobody offered any proper solution, I am posting my own. The solution is not perfect but at least it does what it needs to do - mouse wheel scrolls all controls under it, including the VirtualTreeView controls. The code in solution is in C++ but Delphi version is very similar (it only needs to be translated).
My current solution is to grab WM_MOUSEWHEEL events and translate them into WM_VSCROLL or WM_HSCROLL to which VirtualTreeView reacts and scrolls the content. Additionally, it needs to take into account high-precision mouse wheels which can have smaller value than WHEEL_DELTA (which is set to 120). Finally, it needs to take into account user setting for number of lines to scroll (set in Control Panel in Windows). So here goes:
Put a TApplicationEvents to a form and in the OnMessage event do this:
void __fastcall TFormMain::ApplicationEventsMessage(tagMSG &Msg, bool &Handled)
{
// Check these 3 messages because some mouse drivers may use VSCROLL instead of MOUSESWHEEL message
if (Msg.message == WM_MOUSEWHEEL || Msg.message == WM_VSCROLL || Msg.message == WM_HSCROLL)
{
TPoint pnt;
TWinControl *ctrl;
if (!GetCursorPos(&pnt)) return;
ctrl = FindVCLWindow(pnt);
if (ctrl != NULL)
{
// ToDo: implement if user needs wheel-click - then we also need KEYSTATE but for this example it is not needed
// int fwKeys = GET_KEYSTATE_WPARAM(Msg.wParam);
int zDelta = GET_WHEEL_DELTA_WPARAM(Msg.wParam),
pvParam = 3; // Windows default value
unsigned MyMsg = WM_VSCROLL;
// ToDo: extract SystemParametersInfo somewhere else so it is not extracted for each WM_MOUSEWHEEL message which may not be needed
switch (Msg.message)
{
// This will translate WM_MOUSEWHEEL into WM_VSCROLL
case WM_MOUSEWHEEL:
case WM_VSCROLL:
// Windows setting which determines how many lines to scroll - we'll send that many WM_VSCROLL or WM_HSCROLL messages
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &pvParam, 0);
MyMsg = WM_VSCROLL;
break;
case WM_HSCROLL:
// Same as above but for WM_HSCROLL (horizontal wheel)
SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &pvParam, 0);
MyMsg = WM_HSCROLL;
break;
}
// This calculation takes into account high-precision wheels with delta smaller than 120
// Possible TODO: Round up values smaller than 1 (e.g. 0.75 * pvParam) if pvParam is 1
int ScrollBy = ((double)zDelta / (double)WHEEL_DELTA) * pvParam;
// Send multiple messages based on how much the zDelta value was
if (zDelta > 0)
{
do
{
SendMessage(ctrl->Handle, MyMsg, SB_LINEUP, 0);
}
while (--ScrollBy > 0);
}
else
{
do
{
SendMessage(ctrl->Handle, MyMsg, SB_LINEDOWN, 0);
}
while (++ScrollBy < 0);
}
Handled = true;
}
}
}
My code to show balloon tooltip is:
BOOL CTrayIcon::ShowBaloon(LPCTSTR title, LPCTSTR text, HICON hIcon)
{
BOOL bRes = FALSE;
if(m_hWnd != NULL)
{
NOTIFYICONDATA nfidata = {sizeof(NOTIFYICONDATA)};
nfidata.cbSize = sizeof(nfidata);
nfidata.hWnd = m_hWnd;
nfidata.guidItem = guid;
nfidata.uFlags = NIF_INFO | NIF_GUID;
if (hIcon)
{
nfidata.hBalloonIcon = hIcon;
nfidata.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON;
}
StringCchCopy(nfidata.szInfo, ARRAYSIZE(nfidata.szInfo), text);
StringCchCopy(nfidata.szInfoTitle, ARRAYSIZE(nfidata.szInfoTitle), title);
bRes = Shell_NotifyIcon(NIM_MODIFY, &nfidata);
}
return bRes;
}
The problem is that sometimes the balloon is shown after 10 seconds delay. This only happens when debugger is not connected to the application. If I connect debugger to the app then tooltip is shown immediately. I know that Windows 7 manages balloon tooltips with a different way than previous Win versions. But where can I read more about this? And how can I change this behavior? I need to show tooltip immediately after function call. Or maybe there are any alternatives to Shell_NotifyIcon?
If it's vital that the notification should be seen immediately, for example notification of a telephone ringing, then you should probably be setting the NIF_REALTIME flag.
However, be aware that this also means that the notification won't be displayed at all if for some reason it can't be displayed immediately. The assumption is that if it's a realtime notification, it is only relevant at the time it occurs.
Something like a notification that you've just received an email is not a realtime event.
I'm currently using SHBrowseForFolder() to open a browse folder window but how do I return focus to my main window when Cancel / OK is pressed. I read that I should re-enable my main window before the dialog closes but where is that exactly? Any thoughts?
void buttonPush(HWND hWnd) {
EnableWindow(hWnd, FALSE);
BROWSEINFO bi = { 0 };
TCHAR szDir[MAX_PATH] = { 0 };
LPITEMIDLIST pid = NULL;
LPMALLOC pMalloc = NULL;
if (SUCCEEDED(SHGetMalloc(&pMalloc)))
{
ZeroMemory(&bi,sizeof(bi));
bi.hwndOwner = NULL;
bi.pszDisplayName = NULL;
bi.pidlRoot = NULL;
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_USENEWUI;
bi.lpfn = BrowseCallbackProc;
pidl = SHBrowseForFolder(&bi);
if (pidl)
{
// Folder selected in dialog
pMalloc->Free(pidl);
}
pMalloc->Release();
}
EnableWindow(hWnd, TRUE);
}
Instead of enabling and disabling your main window, just set bi.hwndOwner = hWnd; Then it will enable and disable automatically.
EnableWindow(hWnd, false);
This goes wrong because you are helping too much. When the dialog closes, there is no window left in your application that can still receive the focus. Your hWnd is still disabled, it doesn't get enabled until later. So the Windows window manager is forced to find another window to give the focus to. That will be the window of another app. Inevitably your window will disappear behind it.
Delete the EnableWindow() calls. That is enough, but you can tell the dialog about your window so it won't have to guess at it, useful if your window isn't the active window for some reason:
bi.hwndOwner = hWnd;
I have lost many hours trying to grab exclusively the mouse in my application and re-releasing it.
Right now, I am grabbing it correctly: the mouse cursor disappears from screen and I can read its properties fine.
However, I can't release it correctly! The mouse cursor reappears on screen but no other application is receiving any mouse clicks any more; except mine.
Here is the problematic code :
IDirectInputDevice8* mMouse;
void Win32Mouse::grab(bool grab)
{
if (mGrabMouse == grab)
return;
mMouse->Unacquire();
if (grab)
{
// grab = true; seems to work fine
coopSetting &= ~DISCL_BACKGROUND;
coopSetting &= ~DISCL_NONEXCLUSIVE;
coopSetting |= DISCL_FOREGROUND | DISCL_EXCLUSIVE;
}
else
{
// grab = false; this surely isn't working as it should
coopSetting &= ~DISCL_FOREGROUND;
coopSetting &= ~DISCL_EXCLUSIVE;
coopSetting |= DISCL_BACKGROUND | DISCL_NONEXCLUSIVE;
}
if( FAILED(mMouse->SetCooperativeLevel(mHwnd, coopSetting)) ) {
std::cout << "Failed to set coop level\n";
}
HRESULT hr = mMouse->Acquire();
if (FAILED(hr) && hr != DIERR_OTHERAPPHASPRIO) {
std::cout << "Failed to aquire mouse!\n";
}
mGrabMouse = grab;
}
Could the problem be that I have Windows7?!
Possibly it because of this
http://doc.51windows.net/Directx9_SDK/?url=/directx9_sdk/input/ref/ifaces/idirectinputdevice9/setcooperativelevel.htm
""An application that acquires the mouse or keyboard device in exclusive mode should always unacquire the devices when it receives WM_ENTERSIZEMOVE and WM_ENTERMENULOOP messages.
Otherwise, the user cannot manipulate the menu or move and resize the window.""
May be its not totally answer
But try also to check if may be you not unacquire mouse on some other messeges.
For example on WM_LOSTFOCUS, etc.
You could try to use plain API calls:
HMODULE hDll = LoadLibrary("magicfuncdll.dll");
HOOKPROC hookLowLevelMouseFilter = (HOOKPROC)GetProcAddress(hDll, "_MagicFuncDLL_LowLevelMouseFilterProc#12");
// capture mouse events
HHOOK hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, hookLowLevelMouseFilter, hDll, 0);
... do your thing
// release mouse
UnhookWindowsHookEx(hMouseHook);