Author Archives: Darin

Gaining Access to a Running Instance of Windows Media Player In VB.net

3
Filed under Code Garage, VB Feng Shui, WindowsMediaPlayer

Over the past few weeks, I’ve been working on a little screensaver side project to run in an old radio cabinet that I’ve converted into a touchscreen jukebox. Details of that project will be coming, but for now, I thought I’d share some interesting software bits I’ve discovered.

The screensaver that I’ve working on is intended to:

1) Recognize a Media Player that’s running on the computer when the screensaver loads up.

2) Connect to and monitor that media player for any changes in what’s it playing. Particularly, I want to know Artist name, album name and track name.

3) Hit a few search engines for some images related to what’s playing and display them in a nice “Ken Burns Pan and Zoom” style animation.

Well, long story short, WPF made short work of the image animations, and connecting to two of my favorite media players, J River Media Center and Album Player, was pretty trivial.

But, within just a few days of posting the first version, someone asked if it worked, or could work, with Windows Media Player (and, as it turns out, Windows Media Center, which is just a nicer shell over WMP).

Why Not?

My first thought was, sure! All I have to do it be able to see what’s currently playing and when it changes. Shouldn’t be too tough, right?

Well, after quite of bit of digging, it turns out that Windows Media Player (WMP), is far more gracious about hosting “plugins” that it is about being remotely attached to. There are several plugins available for WMP that write out info on the current playing media to an XML file, or the registry, or a web service, or whatever. But that requires "my application’s user” to install some other application to make things work. Not cool. At least, not for me.

Plan two. Most MS Office apps register themselves with the Running Object Table (The ROT). Other programs can query the ROT and retrieve objects from it that they’re interested in connected to. You often see the VB GetObject() function used for this purpose.

But WMP doesn’t register itself with the ROT, so that’s not an option.

On to Plan C.

WMP Remoting

However, as luck would have it, MS did do something about this type of situation. They call it “Media Player Remoting”. However, it’s just about the least documented concept I’ve come across yet. There’s just very little info about exactly how to set up this “remoting” or what it’s capable of.

Eventually,though, I did come across mention of a demo project written by Jonathan Dibble, of Microsoft no less, that illustrates the technique in C#. There’s a thread here that contains links to the original code, though that page appears to be in Japanese.

Looking further, I found several variations of Dibble’s project, some vastly more involved and complex than others.

I grabbed the simpler version and started hacking!

The Conversion

Converting Mr. Dibble’s code was fairly straightforward. He did a pretty fair job in commenting it and breaking things down nicely. As usual, one of my favorite Web Resources, DeveloperFusion’s CodeConverter, got a workout, and did a fine job on most of the conversion gruntwork.

But when the dust cleared, it didn’t work.

After a lot of stepping with the debugger, it turns out that Jonathan’s handling of  IOleClientSite.GetContainer isn’t quite right. His original code threw a “Not implemented” exception that would cause a crash for me every single time.

The function itself isn’t particularly useful for what I needed to do, and after reading up on the documentation, I felt certain that there’s really wasn’t anything that “needed” to be done in that function. But, it did get called by WMP, and something other than throwing an exception had to be done there.

Then, I realized that a number of other OLE-centric functions that Jonathan had implemented had a return value of HRESULT, and simply returned a E_NOTIMPL value.

So, I changed up the definition of GetContainer and had it returning an E_NOTIMPL, and presto! It works!

Since Jonathan’s demo project appears to be at least 4 years old, I’m not sure whether I may have indeed worked that way at one point, or was a bug, or, quite possibly, was something I didn’t get quite right in the conversion in the first place. Regardless, this version works, so bob’s your uncle.

How to Use It

For anyone not interested in the details, I’ll dive right in to how you actually use this technique.

First off, you’ll need to add a reference to WMP.DLL. This file should be in your Windows\system32 folder if a Windows Media Player is installed. Once added, you’ll have a WMPLib reference in your References tab:

image

Next, copy the WMPRemote.vb file into your own project.

Finally, though this isn’t strictly necessary, you may want to alter your project’s AssemblyInfo.vb file and set the CLSAttribute to false.

....
<Assembly: AssemblyCopyright("blah")>
<Assembly: AssemblyTrademark("blah")>
'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
'THE BELOW LINE YOU MIGHT ADD OR CHANGE TO "FALSE"
<Assembly: CLSCompliant(False)> 

'The following GUID is for the ID of the typelib if this project is exposed to COM
<Assembly: Guid("12333333-33454-1233-1234-123451234512")>
.....

The main class to work with is WMPRemote. It has two static properties; IsWindowMediaPlayerLoaded, and ActivePlayer.

These are static properties, so you access them using the WMPRemote class, like so:

If WMPRemote.IsWindowsMediaPlayerLoaded Then
   Dim Player = WMPRemote.ActivePlayer
End If

At that point, if WMP is loaded, you’ll have a reference to the full WMPlib.WindowsMediaPlayer object in the Player variable.

From there, you can do whatever you need to.

Query properties:

Debug.print Player.playState 

Attach to events:

AddHandler Player.CurrentItemChange, AddressOf CurrentItemChange
AddHandler Player.PlayStateChange, AddressOf PlayStateChange

Or whatever else is necessary.

How It Works

WMPRemote

Since this class is the main access point, I’ll start here. There are only 2 static functions defined here:

IsWindowsMediaPlayerLoaded

This function simply uses the .net Processes object to query for any processes named WMPlayer. If there are any, it returns TRUE, if not, FALSE. Obviously, it could be wrong, but that’s not terribly likely.

ActivePlayer

If things have already been initialized, this function just returns whatever it’s already retrieved for the Active WindowsMediaPlayer object.

If not, it checks if WMP appears to be loaded and, if so, creates and shows an instance of the internal frmWMPRemote form. During the load of this form, it’s immediately hidden so the user will never see it.

The only purpose of frmWMPRemote, is to host the WindowsMediaPlayer ActiveX Control. This all happens during the form Load event:

Me.Opacity = 0
Me.ShowInTaskbar = False
_InternalPlayer = New WMPRemoteAx
_InternalPlayer.Dock = System.Windows.Forms.DockStyle.Fill
Me.Controls.Add(_InternalPlayer)
Me.Hide()

Note that it actually is creating an instance of the WMPRemoteAx control, and then siting it on the form.

WMPRemoteAx

This class is based on the AxHost control, and is what allows an ActiveX COM-based control to exist on a .net WinForms form.

Once created as actually sited on a control (or WinForms Form, in this case), the AttachInterfaces method is called by the .net runtime to connect this host up with whatever COM ActiveX Control it will be hosting. I’ve told this control to host the WindowsMediaPlayer ActiveX control by setting the GUID in the constructor:

MyBase.New("6bf52a52-394a-11d3-b153-00c04f79faa6")

AttachInterfaces connects up the ActiveX control with a clientsite as necessary, but more importantly, it exposes the internal OCX instance by casting it as an instance of WindowsMediaPlayer:

Dim oleObject As IOleObject = TryCast(Me.GetOcx(), IOleObject)

Dim a = TryCast(Me, IOleClientSite)
oleObject.SetClientSite(a)

_RemotePlayer = DirectCast(Me.GetOcx(), WMPLib.WindowsMediaPlayer)

At this point, COM calls several other interfaces that have been implemented by WMPRemoteAx, including:

  • IOleServiceProvider
  • IOleClientSite

    Most of the methods on these interfaces need not actually do anything. They are required for more sophisticated integrations. However, IOleServiceProvider_QueryService does have a very specific purpose.

    Remoting

    Remember that this entire situation is made possible by something WMP calls “Remoting”. Turns out, this callback method is how our control communicates to WMP that we are, in fact, setting up a remoting situation.

    If riid = New Guid("cbb92747-741f-44fe-ab5b-f1a48f3b2a59") Then
        Dim iwmp As IWMPRemoteMediaServices = New RemoteHostInfo()
        Return Marshal.GetComInterfaceForObject(iwmp, GetType(IWMPRemoteMediaServices))
    End If

    When WMP calls this function with the given riid as above, our control has to respond by returning an object of type IWMPRemoteMediaServices (in this case implemented by the RemoteHostInfo object in the project).  That object has a few properties that WMP queries for some basic information, but really, the fact that our control (WMPRemoteAx) has responded by returning an IWMPRemoteMediaServices is the main thing that sets up the remoting connection.

    The OLE Interfaces and Enums

    The rest of WMPRemote.vb is made up of several COM Enums and interfaces that are necessary to bring all this together, but that aren’t readily defined in VB.net. None of that code should normally be altered in any way because it’s actually defining interfaces and values that have already been defined in COM, but that we need defined for use in a .net application. For instance:

    <ComImport(), ComVisible(True), Guid("00000118-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
    Public Interface IOleClientSite

    This is the start of the definition of the IOleClientSite interface.  That interface is a “well defined” interface with a specific GUID (the value of the GUID attribute), and a specific interface type. Changing our .net definition of that interface would likely break everything.

    Where’s the code?

    Right here. This particular version is in VS2010 with .net 4.0 framework, but I don’t imagine there’d be any problems converting back to VS2008, or possibly even VS2005. I suspect you will need at least .net framework 2.0 though, but I haven’t tested this.

    Also, check out a few of my other projects in the Code Garage.

    And finally, let me know what you think!

  • KeePass and PhraseExpress

    2
    Filed under Troubleshooting, Utilities

    These are two incredibly handy utilities to have around. I’ve used them for years.

    KeePass is a task tray utility for keeping track of and auto pasting usernames and passwords into forms. Works with local applications as well as web forms. The password database itself is highly secure.

    PhraseExpress is an “autotype” tool that works across multiple applications, and can even do limited automatic correction of misspelling in any program.

    However, when I updated to KeePass 2.18 recently, password pasting into WebForms (Specifically FireFox in my case) stopped working.

    Well, sort of.

    The username would typically paste in, followed immediately by the password. Even though KeePass was configured to send a TAB key between them and ENTER after the password, those keys weren’t getting sent, so the password paste didn’t work right.

     

    Long story short, something changed in Keepass that suddenly makes the “Route Enter and Tab Key Through PhraseExpress” option of PhraseExpress very important.

    image

    I originally had this setting CHECKED, but that now appears to conflict with Keepass.

    Unchecking the setting seems to correct the issue and Keepass works fine now.

    I’m not sure what, if any, negative effect there is from unchecking this option though, when using PhraseExpress normally.

    Can’t see a Windows XP machine from a Windows 7 Machine

    0
    Filed under Networking, Troubleshooting

    I ran into a strange problem today. I had brought up a new Windows XP machine, wirelessly connected to my network. It has SP3 installed, could see the internet, and could see other machines (including Windows 7 machines) on my network.

    But, my Windows 7 workstation could not see the XP machine.

    By “see”, I mean:

    • it didn’t show up in explorer
    • I couldn’t ping the machine by name
    • I couldn’t browse to the machine by using a unc (like \\majestic\Drivec)

    However, I had another XP machine that my Win7 workstation could see!

    After a few google searches, I came across a post that mentioned changing a few Win7 networking settings.

    In particular:

    Open up Advanced Sharing Settings

    image

    And alter a few settings:

    image

    Notice that

    1. I’ve set File Sharing Connections to the 40-56 bit encryption option
    2. I’ve set Password protected sharing to OFF
    3. I’ve set HomeGroup connections to “Use user accounts”

    I then logged out and logged back in (no need to turn off).

    Presto! I can see both of my XP machines!

    Ok, so which settings actually did the trick?

    Long story short, I ended up setting ALL these settings back to their original values, rebooting between each, and even using IPCONFIG /flushdns, but I can still see my XP machines just fine.

    Ugh.

    In the end, I’m guessing switching those settings allowed “something” about the XP machine to register itself on my Win7 machine, after which, I could switch the settings back, but everything will still be OK.

    But that’s just a guess at this point.

    Visual Basic 11 beta is out

    3
    Filed under .NET, Rants, VB Feng Shui

    vs2010_iconlibrary

    OK, It’s been a few weeks, so this is likely not “new” news, but still.

    Looks like MS isn’t backing off on VB one bit, and that’s a good thing. Heck, as I understand it, there’s even a project in skunkworks build VB in VB! I’ve always said the mark of a complete language is when it’s actually written in itself.

    But I digress. Back to VB 11. The VB Team website has a nice rundown of some of the high points of the release here.

     

     

     

     

    To summarize:

    1. Async support. Meh. VB (actually any .net language) has always had this. The Async stuff definitely makes it easier, so I won’t complain too much. Personally, I’ve kind of gotten into the Javascript notion of lambdas for callbacks in support of async stuff. You can do that in VB, but lambda’s, being limited to single lines) haven’t been all that useful for it, up till now with….
    2. Multi-line Lambdas. That’s right, you can finally declare a Function()….. lambda that spans multiple lines.
    3. Iterators. Which basically means a Yield keyword that can fall out of an iteration loop to allow the iterating code to perform it’s work.
    4. Caller info. This is a big one for me. I’ve written more than my share of error logging/trapping/handling frameworks over the years, starting with VB3, on through VB6 and then into .net. Unfortunately, this feature has nothing to do with that!
      Caller info allows a function to easily and straightforwardly retrieve the Name, Line number or FilePath, of the calling function. While the Line number and Calling path are of dubious value, the caller name is incredibly powerful for things like front ending field accessors in data access libraries. Say you have a small function that retrieves a value from a VB, via a name. With this feature, you can easily create properties of an object that are named that name and then pass the property name down to the accessor function to retrieve the value.
      This has been doable for ever in .net as well (via the call stack), but was not straightforward and was susceptible to corruption if you used code obfuscators. No word on whether this feature will play better with obfuscators at this point, though.
    5. Call View Hierarchy. Looks like a very nice feature, but I’ll have to play with it more before making any concrete observations.
    6. Global Namespace. Another meh. On some projects, I could see this being handy. But it’s nothing to get too excited over. A very incremental improvement.

     

    What’s missing

    1. Unsafe code. At least, I’ve seen no mention of it.
    2. Property-level scope. They added auto properties in VB10. Property-level scope seems like the next natural step for that. And by this, I mean:
      Public Property Name() as string
           Private _Name as string      <—– Property level scope
           Get()
                  Etc
            End Get()
            Set ()
                  Etc
            End Set
      End property

      EDIT: The suggestion that I posted to MS’s suggestion board ended up getting a comment from Lucian Wischik. You can read his full comments here.
      Property Scope Variables
    3. More Dynamic functionality, though you could argue that you can actually do quite a lot of dynamic stuff now in .net languages.

    It’s good to see the language getting very close to parity with C#, and the news of a forthcoming VB.net coded compiler is even more exciting.

    Coding in College vs a real job

    0
    Filed under Uncategorized

    Just came across this at http://i.imgur.com/Lus4Y.png

    image

    Ring any bells for you? <g>

    Squiggly Spell Checking in Notepad++

    0
    Filed under Utilities

    imageNotepad++ has been a favorite editor of mine for quite some time. It’s quite capable, flexible, fast, has tons of plugins, and continues to be reasonably maintained.

    But something that’s been missing is a decent spell checker. Sure there’s the available spell check plugin from the site, but that requires you click a button to spell check the document, and you have to laboriously wade through all the false positives to spell check. Sooooo last century.

    However, a recent faux pas with a readme.txt that ended up with some nasty misspellings in it had me off to find something of a solution. I either had to give up Notepad++, or resolve this some other way. Misspellings are my priority 1000 issue!

    Fortunately, Karim Sharif has (or rather HAD, back in 2010) come to rescue with his Squiggly Spell Check plugin.

    Installation is not for the faint of heart, as it’s completely manual and requires also installing ASpell and making mods to your system’s PATH environment var, but, once it’s going, yeehaw! Nice, Word style squiggly spell check in the document types of your choice, right in Notepad++.

    Granted, it’s bare bones, but it works a treat with the latest unicode Notepad++ (version 5.9.6.2 as I write this).

    If you use Notepad++, it’s definitely worth checking out.

    Ban SOPA/PIPA!!!

    0
    Filed under Rants

    imageIf you don’t know about it, go here.

    http://sopastrike.com/strike

    Basically, SOPA/PIPA is a bill about to go before congress that could dramatically affect your ability to create new web content, as well as use existing sites freely.

    For instance, do you like Craigslist.com?

    Under SOPA/PIPA, Monster Cable (the company) could press to shut them down simply because people sell used Monster Cables through Craigslist and that eats away a little at Monster Cable’s revenue.

    While I’m not blacking out my blog on the 18’th as many sites will be, this is definitely an important issue.

    Call or email your congressman/senator now!

    Windows Can’t Acquire IP Address Via DHCP

    0
    Filed under Troubleshooting

    I’ve run into a situation a few times where, for some unknown reason, a Windows machine will be unable to obtain an IP address via DHCP.

    Then that happens, the machine is likely to get an APIPA address, such as 169.254.xxx.xxx. This is known as the DHCP failover situation, and generally, it’ll mean that while you might be able to see sites out on the internet, most likely, you won’t be able to see any computers connected to your internal network (often computers on your internal network will be associated with IP addresses of 192.168.xxx.xxx).

    In my case, I could set static addresses for those affected computers and they’d work fine, but reset them back to using DHCP and they’d be unable to obtain an IP address.

    Googling turned up a variety of possible solutions (unplug your router, reset the IP stack by deleting registry entries, plug your computer into a different power outlet <huh?>, running IPCONFIG /RELEASE….IPCONFIG /RENEW, etc), but nothing was working for me.

    On a lark, I browsed over to my router (in this case a Verizon Fios router) to check it’s configuration.

    I have several routers, but the Verizon router is the only one set up to be a DHCP server (there should only be one machine, router, or actual server, on your internal network set up as a DHCP server), and it all looked good, but then I noticed something.

    I had a LOT of devices showing up in the connected devices list. Many were offline, but they were still there.

    This means that the router was still holding leases for them and tying up those IP addresses. Hmmmm.

    I counted. 21 of them.

    So I navigated to the DHCP configuration screen of the router and lo and behold, I had it set to lease addresses from 192.168.100.100 to 192.168.100.120. Exactly 21 addresses!

    Ugh.

    Why?

    Way back when I set up this router, my thinking was “I’ll never have more than 20 devices connecting, so why should I accommodate any more than that?” so I’d limited to the available IP address range that the router would pick from.

    In retrospect, I suppose that was being a little to prematurely security-conscience.

    It was that limit, along with a router than apparently does not like to recycle IP leases readily, that caused me to simply run out of DHCP addresses, and thus the failure of Windows to acquire an IP address.

    Moral of the Story

    At the end of the day, I probably shouldn’t have limited the router to 21 addresses. It seemed like a reasonable number, but these days, with several computers in each house, plus smart phones, TIVOs, internet-connected TVs, IPADs, laptops, game systems, etc, it’s completely conceivable to have far more than 20 devices or so vying for IP addresses through DHCP.

    When I exhaust the range from 192.168.100.100 to 192.168.100.200, then I’ll know I have too much tech <g>!

    Authoring VB6 IDE Addins in VB.net

    0
    Filed under .NET, Utilities, VB6

    imageOk, Ok, I know what you’re asking yourself right now.

    WTF would you want to do this?

    But bare with me.

    Realistically speaking, VB6 has a very limited shelf life. I have plenty of VB6 apps that run just fine under Win7 (even 64 bit), but Under Win8? Who knows.

    But, the truth is, there are plenty of businesses that rely on WinXP and VB6 apps today, and those businesses aren’t converting those apps to .net (or anything else) particularly quickly.

    As it happens, I’m currently doing some maintenance and upgrade work on a VB6 application, and, while it IS a 10+ year old IDE, it does have it niceties and not-so-niceties.

    CodeSmart, MZTools, CodeHelper, and other add-ins can certainly help, but one thing I’ve definitely missed now that I’ve worked with VS2005+ is persistent bookmarks and breakpoints.

    That fact that VB6 didn’t save bookmark and breakpoint locations is a real shame.

    Now, people have asked about this capability for years. I found posts going back to 2001 asking for this function, but the only utility that even came close was the open source CodeDawg project. And while I still have the source here, I can no longer even locate a link for it on the web. No matter, really, because it never did work particularly well, would miss breakpoints or bookmarks that you set sometimes, and just generally had a terribly interface.

    But it was a valiant attempt!

    Fast forward to today, and I’m back in VB6, working in a large and not particularly straightforward codebase, and I find myself really needing bookmarks and breakpoints that I can set and come back to the next day or whenever.

    But .net provides so much simpler coding paradigms for many things now (generic collections, and direct support for subclassing anyone!) that after a few days working up a prototype add-in in VB6, I couldn’t stand it and had to see if it was possible to put something together in .net.

    That’s right. A VB6 addin written in VB.net 2010!

    It’s not quite finished yet, so I’ll hold off on presenting the full monty for now, but it most certainly is doable, and actually makes for a quite nice development and debugging experience.

    First, fire up VS2010 and create a DLL Library project.

    In the project  properties, you’ll need to make sure to check the “Make assembly COM visible”

    image

    Next, On the compile tab, you need to set “Register for COM Interop”

    image

    You’ll also need to add a reference to the VBIDE.DLL file:image

    You can usually find the file under the COM tab on the Add Reference screen:

    image

    Finally, create a new CLASS, call it Connect, and paste this:

    <ComClass("96861C1E-73A0-46E2-9993-AE66D2BC6A91", "1AEA0235-959D-4424-8231-8EBB9B9C85FE")> _
    <ProgId("BookmarkSave.Connect")> _
    Public Class Connect
        Implements VBIDE.IDTExtensibility
    
        Private _App As Application
    
        Public Sub OnAddInsUpdate(ByRef custom As System.Array) Implements VBIDE.IDTExtensibility.OnAddInsUpdate
            _App.OnAddInsUpdate(custom)
        End Sub
    
        Public Sub OnConnection(VBInst As Object, ConnectMode As VBIDE.vbext_ConnectMode, AddInInst As VBIDE.AddIn, ByRef custom As System.Array) Implements VBIDE.IDTExtensibility.OnConnection
            Try
                If _App Is Nothing Then
                    _App = New Application
                End If
                _App.OnConnection(VBInst, ConnectMode, custom)
            Catch ex As Exception
                ex.Show("Unable to start addin.")
            End Try
        End Sub
    
        Public Sub OnDisconnection(RemoveMode As VBIDE.vbext_DisconnectMode, ByRef custom As System.Array) Implements VBIDE.IDTExtensibility.OnDisconnection
            _App.OnDisconnect(RemoveMode, custom)
        End Sub
    
        Public Sub OnStartupComplete(ByRef custom As System.Array) Implements VBIDE.IDTExtensibility.OnStartupComplete
            _App.OnStartupComplete(custom)
        End Sub
    End Class

    Now to explain a few things.

    The two GUIDs you see in the first COMCLASS line, you’ll need to generate your own unique id’s for them.

    Obviously, the ProgID will also need to be changed (the general convention is {addin-name.Connect}).

    The Application object is my actual core addin application. I’ve intentionally kept all the addin’s code OUT of the Connect class, to keep it simple, and because this class MUST be made visible to COM so that VB6 itself can instantiate the CONNECT object (and thus kickstart your addin). Usually, the less “stuff” you have to expose to COM in a .net assembly, the better, and this represents the bare minimum.

    To make debugging seamless, be sure to set the Debug Start Action to “Start External Program” and point it at your VB6.EXE file:

    image

    You’ll also need to make sure your addin is registered with VB6, so it will know to load it along with any other addins. You do that by creating a key like the “BookmarkSave.Connect” key below:

    image

    Be sure to change the “BookmarkSave.Connect” to whatever ProgID you’ve decided on.

    Give it a whirl

    With all that in place, you should be able to RUN your addin from VS2010, VB6 should start and your addin CONNECT object’s OnConnection method should be called.

    Now it’s time to pull out my Duran Duran and SoundGarden discs and get to coding some good ol’ fashioned pseudo-OO VB6 goodness!

    Determining Whether you’re in DesignMode in a Windows Phone 7 Project

    1
    Filed under .NET, Windows Phone 7

    When you’re building usercontrols in a Windows Phone 7 project, it’s often necessary to know whether the code is running in Design Mode (i.e. being invoked by the Visual Studio IDE or by Expression Blend), or whether you’re actually running in the real program.

    It’s a trivial matter, really, but it can be hard to remember exactly how to do it when I need it, so I wrapped it in an IsInDesigner function:

        Public Function IsInDesigner() As Boolean
            Return System.ComponentModel.DesignerProperties.IsInDesignTool
        End Function