Windows Media Player UI not updating when sending commands with WMP SDK - winapi

I am using WMP SDK to control Windows Media Player. I first created a new project using Windows Media Player Plug-in Wizard and then added code which communicates with my application using a named pipe. When my application needs music to be muted, for example, it send a message to the WMP plugin and then the plugin mutes the music:
CComPtr<IWMPSettings> settings;
if (SUCCEEDED(core_->get_settings(&settings)))
{
settings->put_mute(VARIANT_TRUE);
}
It works, and when I send commands, they get executed, but the UI doesn't get updated. So, for example, if I send a mute command, music gets muted (there's no sound), but the mute button still displays as if music isn't muted. To prove this, I can change the skin at this point or skip to another song, and the UI gets updated and correctly shows mute state. If I click it, it unmutes, and then again displays unmuted state (which is now the correct state).
So, is there any way to force refreshing of the UI, for example, or some other workaround?

Yes. If put_mute isn't working with your window dialog, you can use Interop and WM_APPCOMMAND as mentioned in this related question. Specifically, you'll want to use APPCOMMAND_VOLUME_MUTE.
Merging the two code snippets, we get:
private const int APPCOMMAND_VOLUME_MUTE = 0x80000;
private const int WM_APPCOMMAND = 0x319;
[DllImport("user32.dll")]
public static extern IntPtr SendMessageW(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
CComPtr<IWMPSettings> settings;
if (SUCCEEDED(core_->get_settings(&settings)))
{
SendMessageW(new WindowInteropHelper(this).Handle, WM_APPCOMMAND, new (IntPtr)APPCOMMAND_VOLUME_MUTE);
}
(Warning: untested code.)
This seems suboptimal, and every bit of documentation I could find indicates your prior solution should just work. You may want to consider contacting Microsoft about this one.

Related

How does Windows changes keyboard layout?

The hotkeys for doing so are:
Alt+Shift - Windows 7 and Win button + Space in Win 8 and 10.
Programmatically I can do that using WM_INPUTLANGCHANGEREQUEST, but it is not the way Windows does that. I am trying to figure out how Windows changes the layout. Using spy++ I figured out that Windows sends WM_INPUTLANGCHANGE message which is changing layout, so I tried it myself:
SendMessage(myHWND, WM_INPUTLANGCHANGE, 0xCC, 0x4190419);
I have keyboard hook bind, when press f1 do the sendmessage to currently active window's active control.
but it didn't work, though the messages in spy++ are thes same:
first one using "Windows hotkey"/"Taskbar" to change layout, it works. Second my message, it did not work. Am I missing something, why message works for "Windows" but not for me.
The WM_INPUTLANGCHANGEREQUEST works, but it freezes some certain apps, and I would like to figure out the way Windows does the layout changing to avoid that.
-- update.
In DxO Photolab 3 it freezes when using WM_INPUTLANGCHANGE in "Export to Disk" Dialog:
When you change layout using "Windows" Method(Keyboard Hotkey/Taskbar):
Works normally, no freezing.
Posting the WM_INPUTLANGCHANGE:
Received the WM_INPUTLANGCHANGEREQUEST and froze:
Also similar freezing I've seen in Skype, MS Office, Adobe After Effects.
From WM_INPUTLANGCHANGEREQUEST,
When the DefWindowProc function receives the
WM_INPUTLANGCHANGEREQUEST message, it activates the new input locale
and notifies the application of the change by sending the
WM_INPUTLANGCHANGE message.
We can view the details through spy++.
Only after the application receives the WM_INPUTLANGCHANGEREQUEST message, it will activate the new input locale, and notifies the application of the change by sending the WM_INPUTLANGCHANGE message.
A simple test:
According to my understanding, what actually works is the WM_INPUTLANGCHANGEREQUEST message, but I have not found an alternative API to complete its work.
For the freezing problem of some certain apps you encountered, I found some similar cases.
Refer #Barmak Shemirani's answer,
Apparently WM_INPUTLANGCHANGEREQUEST fails if the target itself is a
dialog based application (I don't know why!) To solve the problem you
can post WM_INPUTLANGCHANGEREQUEST message to dialog's descendants (in
addition to WM_INPUTLANGCHANGEREQUEST message to the dialog itself)
Updated:
My test code:
#include <Windows.h>
int main()
{
HWND hwnd = (HWND)0x00070EBA; // hwnd of skype
while (1)
{
Sleep(1000);
PostMessage(hwnd, WM_INPUTLANGCHANGEREQUEST, 0, 0);
}
return 0;
}
Result:

SetWindowsHookEx with WH_JOURNALRECORD hook in Qt4

I am debugging a closed source legacy application that uses Qt 4.8.6
The application developed a problem after an automatic update of Windows 10
When the problem is triggered application (and also whole Windows desktop) stops receiving keyboard and mouse events, but applications continue to run, mouse cursor also moves.
Everything goes back to normal if Ctrl+Esc is pressed to open the start menu, until a specific action is done in the application which triggers the problem again.
I traced the problem to Qt's QWidget::grabMouse() which is called on a custom widget in the application.
Tracing the execution of QWidget::grabMouse() shows that the problem happens when it executes:
journalRec = SetWindowsHookEx(WH_JOURNALRECORD, (HOOKPROC)qJournalRecordProc, GetModuleHandle(0), 0);
Its qJournalRecordProc looks like this:
// The procedure does nothing, but is required for mousegrabbing to work
LRESULT QT_WIN_CALLBACK qJournalRecordProc(int nCode, WPARAM wParam, LPARAM lParam)
{
return CallNextHookEx(journalRec, nCode, wParam, lParam);
}
full source of QWidget.cpp where all of this is defined can be found here
Googling reveals that SetWindowsHookEx has special requirements since Vista in order to deter malware from using it. The app seems to fulfill the requirements that I found by googling (signed by trusted certificate although it is using SHA1, installed in "Program Files",...)
And now for the actual questions:
Why Qt needs WH_JOURNALRECORD to perform mousegrabbing? I thought that by using WH_JOURNALRECORD the hook procedure gets mouse/keyboard events and this does not affect the individual widgets. I patched QtGui4.dll so it does not call SetWindowsHookEx (and related Unhook). This fixes the app and does not have any noticeable side effects.
Why would using the WH_JOURNALRECORD hook in this way stop keyboard/mouse events being delivered? I also set a breakpoint on the hook procedure and it seems that it never gets called. I also rigged the hook procedure so it would crash the app (in case this strange hook behavior messes up the debugger) and the app did not crash which in my opinion confirms that the hook procedure is never called.
All this hooking stuff looks like a real ugly hack...
And apparently Qt developers recognized this since Qt5's implementation does not use SetWindowsHookEx anymore...
Edit (to clarify some comments):
I already modified QtGui4 library's grabMouse to not call SetWindowsHookEx and not to call the corresponding UnhookWindowsHookEx.
Since the closed source app apparently uses Qt libraries under the commercial license, they made some closed source modifications to Qt, which forced me to actually patch their QtGui4.dll (changing the part which calls the SetWindowsHookEx into NOPs.
This resolves the problem with the app and produces no noticeable side effects. But I would like to know why Qt developers deemed necessary to put the hook (which does nothing) there in the first place. And what makes it misbehave.
You say you are patching QtGui4.dll but your SetWindowsHookEx line contains GetModuleHandle(0) indicating that qJournalRecordProc is in your .EXE!
Global hooks should be implemented in a .DLL and the correct HINSTANCE should be passed to SetWindowsHookEx.

How to provide a HWND to DirectSound SetCooperativeLevel in a console program?

I'm writing a console program that uses DirectSound API to render some audio data. I stumbled on a curious problem when following the DirectSound Programming Guide (from Microsoft). According to the documentation :
After creating a device object, you must set the cooperative level for the device by using the IDirectSound8::SetCooperativeLevel method. Unless you do this, no sounds will be heard.
The problem is that I'm writing a console program, and SetCooperativeLevel requires a HWND as a first argument. I don't have any HWND to deal with in the console program. I tried providing a null pointer but it failed with a DSERR_INVALIDPARAM error code.
What HWND value should be provided to IDirectSound8::SetCooperativeLevel in a console program ? The audio part of the program is planned to be built as a shared library, so it has little to no knowledge of the "outside" program.
Thanks for any advice !
Note : I know that there is a somewhat better solution for simply rendering audio, like using SDL, OpenAL, SFML (based on OpenAL), but for my current project DirectSound is enforced.
Edit : I found a message from a Microsoft engineer that removes doubts about using the desktop window or the console window as a HWND for SetCooperativeLevel when creating GLOBAL_FOCUS buffers.
Although I have not tested this myself, you may have some success creating a hidden window and passing its HWND to the SetCooperativeLevel method. SetCooperativeLevel uses this hwnd to determine when your application has input focus; therefore, if you select a cooperative level where the input focus doesn't matter (eg, DSSCL_NORMAL), a hidden window (which will never receive input focus) should be fine.
you can use this ::GetDesktopWindow().

How do I prevent Window resizing when the Workstation is Locked then Unlocked?

We have an application that is run in multi-monitor environments. Users normally have the application dialog spread out to span multiple mointors.
If the user locks the workstation, and then unlocks it, our application is told to resize.
Our users find this behavior frustrating, as they then spend some time restoring the previous layout.
We're not yet sure whether it is the graphics driver requesting the resize or Windows. Hopefully through this question, it will become clearer which component is responsible,
Popular applications like (File) Explorer and Firefox behave the same way in this setup. To replicate just:
open Explorer (Win+E)
drag the Explorer window to being horizontally larger than 1 screen
lock workstation (Win+L),
unlock
the application should now resize to being solely on 1 screen
How do I prevent Window resizing when the Workstation is Locked then Unlocked?
Will we need to code in checks for (un)locking?
Is there another mechanism we're not aware of?
Before the window is resized, the application will get a WM_WINDOWPOSCHANGING message from Windows. You can intercept that message and change the parameters, forcing the window to stay put. You need to be careful, because you'll get the same message when the user is trying to move or resize the window. Probably when it's maximized or minimized, too.
Edit: You can use the WTSRegisterSessionNotification function to get additional messages. The messages are intended for fast user switching, but the lock screen is implemented in Windows as a system session.
A similar question has an answer that allows you to restore window size in a .net application after the session is unlocked.
Someone asked essentially the same question on SuperUser, but from your user's perspective: How can I stop big windows from resizing when I lock my workstation?
I tried the solution given in the question referenced by Leif and found that the SessionSwitchReason.SessionUnlock event seemed to be fired after the computer had been locked rather than before. This meant that the window size and location had already been reset, so the resize failed.
Therefore, I had to find another way of storing the current size and location before the computer was locked. The only thing I could see to do was to subscribe to the ResizeEnd for Winforms applications and update the "pre-lock" size and location there.
I haven't been able to get it working for WPF applications yet, because WPF doesn't have the equivalent of ResizeEnd (or I haven't found it yet) and subscribing to SizeChanged and LocationChanged isn't good enough as these are fired when the computer is locked as well overwriting the size and location.
In the end I had to hook into the Windows ExitSizeMove event to save the current size and position. Details of how to hook into this event can be found here:
private const int WM_EXITSIZEMOVE = 0x232;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
source.AddHook(new HwndSourceHook(WndProc));
}
private IntPtr WndProc(IntPtr hwnd, int msg,
IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_EXITSIZEMOVE)
{
// save location and size of window
handled = true;
}
return IntPtr.Zero;
}

VB6 quitting while debugging with active Windows message hook

One of our largest old VB6 apps has some code in it to allow other apps (including some dotNET ones) to pass an ID to it via a Windows message - this ID is then used by the VB6 app to load an entry in a regular Windows form. The message hook is added after the user is logged in and authenticated, and removed once they logout.
Public Sub HookClaimFinderCall()
lpPrevWndProc = SetWindowLong(gHW, GWL_WNDPROC, AddressOf WindowProc)
End Sub
Public Sub UnhookClaimFinderCall()
Dim temp As Long
If gHW <> 0 Then temp = SetWindowLong(gHW, GWL_WNDPROC, lpPrevWndProc)
End Sub
Private Function WindowProc(ByVal hw As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
If uMsg = WM_FINDCLAIM Then
MasterFindClaim lParam
End If
WindowProc = CallWindowProc(lpPrevWndProc, hw, uMsg, wParam, lParam)
End Function
However, there are two issues with this. The first relates to Visual Studio 6. If the code is being debugged, and an error occurs to bring up the "Continue End Debug Help" dialog box, pressing End quits Visual Studio instantly (losing any unsaved changes). This does not happen if the message hook has not been activated yet. What causes this, and is there anything I can do to stop it short of commenting out the code that loads the hook?
Secondly, if the users quit the app without logging out properly (by whatever means), what happens to the message hook?
I hope I've got all the terms right in the above...
You are going to get crashing (or disappearing VB6) if you initiate a global hook and attempt to Debug in VB6. VB6 is kind of like a simulator, it doesn't exactly duplicate the runtime of a VB6 application and hooking is one of the areas it fails miserably at (though it can't really be blamed if you understand what is going on). For all of the global hooks we use in our applications we check to see if VB6 is running in IDE mode (there are several ways to do this) and if it is, don't run the global hook. If you absolutely have to run the global hook, do not stop the application in the debugger - use debug.print or some other means, but don't stop the application otherwise you will have milliseconds to seconds before it "goes away".
Although you should unhook before exiting the application, when the message pump hits the hook and the handle of the application where the hook takes place no longer exists, it is a fairly cheep operation to ignore that hook. Now if you ran the application and exited thousands of times, it would probably build up, but that I think is the least of your concerns.
Kris's answer is correct. A couple of additional points.
Strictly this is subclassing not message hooking.
It's also good practice in your WindProc to detect WM_NCDESTROY and unhook when that message is received. The message means the window is about to be destroyed - it should be received if the user quits the app, no matter how they do it.
There are some API calls in Windows 2000 and later that make it easier to manage subclassing. As always, Karl Peterson has an excellent article with excellent VB6 code here.

Resources