toub/rsd.ashx" /> Position Changer Add-In updated for Windows XP Media Center Edition 2005 Update Rollup 2 - Stephen Toub - Site Home - MSDN Blogs
-->

Stephen Toub

.NET, MSDN Magazine, and other Adventures in Life

Position Changer Add-In updated for Windows XP Media Center Edition 2005 Update Rollup 2

Position Changer Add-In updated for Windows XP Media Center Edition 2005 Update Rollup 2

  • Comments 49

Today, the Media Center team released Update Rollup 2 for Windows XP Media Center Edition 2005 (you may have heard of the update referred to previously as "Emerald"). Update Rollup 2 includes a plethora of modifications to improve the stability, reliability, and functionality of Windows XP Media Center Edition 2005.  A new SDK to match was also released, available at http://www.microsoft.com/downloads/details.aspx?FamilyId=1D836C29-ABD5-4FDD-90C5-5C5ABAE97DB4&displaylang=en.

One of the problems users of MCE 2005 experience is poorly written add-ins and HTML applications hosted by MCE. In MCE 2005, add-ins are hosted in-process in ehShell.exe (the main MCE application), so when something goes wrong in one of these add-ins, the entire MCE experience can be affected.

To address this reliability issue in Update Rollup 2, MCE has moved to an out-of-process hosting model. When an add-in is run, a new hosting application, ehExtHost.exe, loads the add-in rather than ehShell.exe loading it (take a look in task manager after an add-in loads and you'll see an instance of the application listed). That way, if MCE detects that the add-in is being a bad citizen, it can tear down the whole hosting process. A named pipe remoting channel is used to provide communication between ehShell and the add-in loaded in ehExtHost. Since ehShell previously loaded add-ins into a separate application domain, remoting was already necessary for add-ins to communicate with the hosting code in the primary application domain, so this change for Update Rollup 2 shouldn't affect the functionality of any existing add-ins. Or, at least not those that play by the rules...

As I mentioned in my article Time Travel with Windows XP Media Center, while developing the Position Changer Add-In, I made several assumptions about Media Center and the environment that would be hosting the add-in. Most of these assumptions are still valid for Update Rollup 2 (for example, I had assumed that, while watching a recorded video, the arrow keys served no purpose, and that remains the case in this update). However, if you recall from the article, my add-in uses a keyboard hook in order to intercept and handle certain remote control interactions. The keyboard hook requires that the add-in be running in the same process as the GUI thread handling the keyboard commands issued by the remote. But as I've just outlined, with Update Rollup 2, add-ins are no longer hosted in ehShell, and thus the version of the add-in I posted with my article no longer works once you upgrade to Update Rollup 2. It won't cause anything to crash; rather, the add-in will just fail to load, a message will be written to the event log detailing that the keyboard hook couldn't be installed, and the add-in will exit silently.

Have no fear, though. If you've come to rely on the add-in (as I have), you'll be comforted to know that I've updated the code so that it is compatible with the new version. You can download the source code and installers for both versions here. Just uninstall the version you currently have installed, install the new version, and you should be good to go.

For those of you who are just interested in getting this functionality back, you can stop reading now. For those of you who are interested in the changes I had to make to get this to work, keep reading.

First and foremost, I had to abandon the type of keyboard hook I was using in the previous version. My add-in is now running in a separate process from the thread that I want to hook, and thus I would need to use a global hook. However, if you read Knowledge Base article 318804, you'll discover that global hooks are not supported in the .NET Framework:

Except for the WH_KEYBOARD_LL low-level hook and the WH_MOUSE_LL 
low-level hook, you cannot implement global hooks in the Microsoft 
.NET Framework. To install a global hook, a hook must have a 
native DLL export to inject itself in another process that requires 
a valid, consistent function to call into. This behavior requires a 
DLL export. The .NET Framework does not support DLL exports. 
Managed code has no concept of a consistent value for a function 
pointer because these function pointers are proxies that are 
built dynamically."

Ah, but there's a glimmer of hope. The KB article mentions that WH_KEYBOARD_LL can be used to implement global hooks. What is WH_KEYBOARD_LL? From the documentation:

The LowLevelKeyboardProc hook procedure is an application-defined or 
library-defined callback function used with the SetWindowsHookEx function. 
The system calls this function every time a new keyboard input event is 
about to be posted into a thread input queue. The keyboard input can come 
from the local keyboard driver or from calls to the keybd_event function. 
If the input comes from a call to keybd_event, the input was "injected". 
However, the WH_KEYBOARD_LL hook is not injected into another process. 
Instead, the context switches back to the process that installed the hook 
and it is called in its original context. Then the context switches back 
to the application that generated the event."

Perfect! This means that from the hosting process, I can use a low-level keyboard hook instead of a standard keyboard hook to intercept the remote control commands routed to ehShell. Doing so required only minor modifications to my KeyboardHook class, which you can examine by downloading the source code I've made available. There is a downside to this approach, of course. This is now a global hook, which means that every keystroke to be handled by any application on the machine will cause a context switch to the hosting process so that my add-in can determine whether it wants to handle it. To minimize this impact, when the add-in starts up, it retrieves the process ID of ehShell.exe and caches it away. When the hook callback executes to process keyboard input, the handler first checks to see if the process responsible for this input is ehShell (it make an educated guess about what process is responsible by retrieving the ID of the process that owns the current foreground window). If it's not, it immediately returns in order to minimize the amount of time spent processing in the hook (of course, we also only want to handle ehShell input, so this is necessary anyway).

With that change in place, the new version of the add-in is almost fully functional. However, if you attempt to use the bookmarking functionality, you'll find that it is partially broken as it's unable to associate bookmarks with a particular DVR-MS file (if you read the article, you'll remember that this was one of my goals, to be able to set a bookmark per file). The reason for this becomes clear if you look at the event log after attempting to set or jump to a bookmark, as you'll see a message like the following in the log:

Unable to access filter graph for current show. 
System.Runtime.InteropServices.COMException (0x8001010D): 
An outgoing call cannot be made since the application is 
dispatching an input-synchronous call.
   at System.Runtime.InteropServices.UCOMIRunningObjectTable.GetObject(
      UCOMIMoniker pmkObjectName, Object& ppunkObject)
   at Toub.MediaCenter.AddIns.MceGraph.GetGraphForProcess(Int32 pid)
   at Toub.MediaCenter.AddIns.MceGraph.GetCurrentMediaInfo(Int32 pid)

In order to figure out the current DVR-MS file, I access the running object table (ROT) in order to get at the current playback filter graph used by MCE. This allows me to iterate through the filters in the graph looking for the source filter that will reveal to me the path to the current DVR-MS. In order to retrieve a filter graph from the ROT, I need to make a call to the ROT's GetObject method. However, this is a call on a COM component. A quick look at Knowledge Base article 131056 reveals the following:

A synchronous OLE call made by the recipient of an inter-process/
inter-thread SendMessage fails with RPC_E_CANTCALLOUT_ININPUTSYNCCALL(0x8001010D).

It explains why if you're interested in the nitty gritty. A slightly more relevant explanation is available from Knowledge Base article 198996:

COM does not allow normal outgoing COM method calls 
from a thread that is currently servicing a 
SendMessage request.

And another look at the documentation for LowLevelKeyboardProc reveals this:

This hook is called in the context of the thread that installed it. 
The call is made by sending a message to the thread that installed 
the hook. Therefore, the thread that installed the hook must have a 
message loop.

Aha. "The call is made by sending a message". Thus, our hook callback is the result a message being sent. Our hook then attempts to make a normal outbound COM call from the same thread. And boom: RPC_E_CANTCALLOUT_ININPUTSYNCCALL.

To get around this, I simply do the work to access the filter graph on a separate thread. And with that, Position Changer Add-In should work just like it did prior to Update Rollup 2. The only thing you might notice is a slightly sluggish response to keystrokes, as each one requires that the hosting process handle it.

I hope you enjoy it.

-Steve

Attachment: PositionChangerAddIn.zip
social-media-sharing no-wrapper" id="fragment-3364">
  • I installed your new update for Rollup 2 after upgrading and deinstalling the old one and noticed an odd behavior.
    When viewing live or recorded TV and pressing the details button, I couldn't navigate the pop-up menu, which is practical to change the zoom level. Instead, the down and up arrows on the remote would backstep and frontstep through the episode.
    When I deinstalled Position Changer, the behavior went back to normal.
  • Hi Jerome-

    As I mention in the article, I'm doing something highly unorthodox in this add-in, hijacking the arrow keys while full-screen video is displayed. Most of the time this doesn't cause a problem, but if a pop-up menu is displayed over the video such thatup/down navigation is required, yes, it will be an issue. As far as I'm aware, there is no way I can programmatically detect whether a popup menu is currently being displayed. Thus, if this is a scenario you encounter frequently, you can disable bookmarking in the add-in but keep the rest of its functionality (rather than uninstall the whole thing). In the article I detail all of the configuration options I've built into the add-in, but in short, you can simply set the SupportBookmarking value in the HKLM\Software\Toub\PositionChangerAddIn registry key to 0, and the up/down arrows will not be intercepted by the add-in (this change will not take effect until ehShell is restarted).

    Of course, as I've provided the full source for the add-in, you can also tweak it to your liking, possibly utilizing other input keys for the bookmarking functionality.

    -Stephen
  • Thanks, I'll try that.
  • Hello
    A great idee to improve MCE! Hovever I have proble to install it, I have a fresh install of MCE 2005 with all upgrades (incl rollup 2). Then I run the setup it stops in the middle of the installation and says it cant continue because the app is for MCE 2005 or later. I have killed ehshell before installation and tried both versions but the same errormessage. Any idees?!
    Regards
    Oscar
  • Hi Oscar-

    I believe the RegisterAddIn.dll I'm using from the old SDK has an issue with correctly determining the version of some non-English versions of MCE 2005. What's your locale? I'm told that the new SDK might have an updated version of this DLL that would fix the problem, but I haven't tried it yet. Regardless, it's an installation issue rather than an actual add-in issue. When I get some free time over the next week or two, I'll see if I can resolve this for you.

    -Stephen
  • Hello again!
    It would be realy great if it was posible to solve! Hovever I run a English (US) version of MCE, the only difference is that then I installed it I choose to set the region, keyboard time etc to Sweden witch gave me local content on onliespotlight (and maybe a tvguide soon). Could that realy be the problem?! The installation discs are bought about a year ago so there was quite much to download after the install.
    Regards
    Oscar
  • Hi, great app, I think I discovered a bug.
    It happens when there is two active instances of ehShell.exe, e.g: One in a MCX device and another in a the main desktop.

    The code to get the ehShell.exe may get the wrong process.

    Here is some code to fix it:
    public static Process GetEhShellProcess()
    {
    System.Diagnostics.Process[] processes = System.Diagnostics.Process.GetProcessesByName("ehshell");
    string userName = Environment.UserDomainName + "\\" + Environment.UserName;
    foreach(System.Diagnostics.Process process in processes)
    {
    WindowsIdentity processIdent=NativeMethods.GetProcessOwner(process);
    if ( processIdent!=null && String.Compare(userName,processIdent.Name,true)==0)
    {
    return process;
    }
    }
    throw new InvalidOperationException("Cannot find ehShell.exe process for user: " + userName);
    }


    public static WindowsIdentity GetProcessOwner(System.Diagnostics.Process process)
    {
    IntPtr token = IntPtr.Zero;
    try
    {
    if( OpenProcessToken(process.Handle, TOKEN_ACCESS.TOKEN_QUERY, out token) == 0 )
    {
    return null;
    }
    return new WindowsIdentity(token);
    }
    catch
    {
    token = IntPtr.Zero;
    }
    finally
    {
    CloseHandle(token);
    }
    return null;
    }
  • Nice. Thanks, Oscar! I don't have an extender device at home, so it's difficult to test for those scenarios.
  • Hello again!

    Im just wondering if there will be an update of the RegisterAddin / SDK issue?! Since last I have downloaded and installed the SDK Developer kit to se if that worked (actually not a qlue how to use it) but still the message that it cant install because it has to be MCE 2005 or later (I have RU2 and of course used that version (and olso tried the "wrong" version)). I have also tried to install the Add-in on a friends computer running MCE 2005 Swedish version, exactly the same problem about saying its not beeing MCE 2005.....
    As said earlier its a realy great idee of an plug-in and I would be very happy if I could get it to work!

    Regards
    Oscar
  • In the time travel article, it also talks about how to use a dummy NativeWindow to override WndProc to intercept windows messages. It appears this example no longer works either with RU2. That's a shame, because I was trying to use that to write some code to integrate Skype with MCE, and need to intercept the windows messages to do that.

    Any chance of updating the WndProc example to let me know how it should work post RU2? I have tried just changing the _iw.AssignHandle(p.MainWindowHandle); line to pass the handle of the ehshell window as you derive in your new code, but that doesn't work. Any help is really very much appreciated.

    Thanks

    Martin Millmore
  • Hello!

    Is there any fix for the bug wich fixxes the issue with the position addin install?

    I have rollup 2 but I get the message the app is for MCE 2005 or later... please advice...?
    (MCE 2005 with swedish language pack.)

    / Steve
  • I just updated the installer by compiling it against RegisterAddIn.dll from updated SDK. I don't know if it'll help at all (I don't have a non-US install to test it against), but if it does, great!
  • Hi again!

    It works fine to Install now, after your update.
    But I do not seem to get any extra functions i MCE?

    Should it not be possible to "jump" via the remote now? Does not seem to work for me.

    Could you please give a short description how it is supposed to work.. or am I missing something?

    Thx!
    Steve
  • Glad it now installs. Are you following the instructions as outlined in the article? For example, while watching a recorded show, you should be able to press the right arrow button immediately followed by the four digit time code (such as 1234 to mean 12 minutes 34 seconds) to which you want to jump, followed by the enter button (there won't be any onscreen feedback until you press enter or until the timer expires after entering the time). If that's not working, check the event log to see if there are any related messages in there.
  • Aahh yes.
    Got it working. Must have done something wrong the first time.

    Wouldn't it be possible to actually see the time code you are entering?
    That would have been nice. (like for example "06:55")

    Thank you for great add-in!

    /Steve
Page 1 of 4 (49 items) 1234
Leave a Comment
  • Please add 6 and 2 and type the answer here:
  • Post