I know that most of us tend to use MSN messenger nowadays (I realize the horrific bias in that statement but i clearly generalize to include myself and everyone i talk to… on MSN) and most of us listen to music whilst on the computer. I used to use iTunes, which has a handy feature of throwing it’s song data at your MSN personal message and thus showing everyone what song you listen to. However, after frustrations of paying 99cents a song for every friggin song i downloaded from the iTunes store i decided to go for a better deal. Many of us will have used Napster in it’s illegal days, but now i find Napster to be an extremely preferable alternative to other music stores, with it’s flat rate fee of $10 a month and unlimited song downloads. I am aware NapsterToGo only works with creatives but i make the assumption that if you are reading this you are intelligent enough to be able to convert the Napster .wma files to unencrypted .mp3′s. (I obviously do not endorse nor engage in this behaviour, i was just outlining my assumption ;]) Anyway, more to the point, i was frustrated as i began to use Napster that there was no method of me showing everyone the spectacular music i listen to. I also saw this as a challenge and a good way to learn some Interoperability and P/Invoke in C#.
To begin I looked and Napster and though to myself, “Where could I grab the song data from?”. The most obvious place is the right hand window, where it displays the album work, song title and artist. So next i had to try and find out how exactly that data was being thrown to that window. Well first thing to do was to grab a couple of programs:
Winspector Spy Download Here
WinID Download Here
Winspector Spy is just like Spy++, but free and doesn’t require you to have the big ugly monster that is Visual Studio. WinID is a more compact form and I find it more useful to grab window handles and ID’s. So i started up Winspector Spy and navigated through the screens to show me what Napster was spewing out. First thing i noticed was it’s actually interacting through windows media player. Interesting but useful. I then navigated through to the PPAppFrameClass and searched through each expansion until what do I find, but all my song information just sitting there in text classes! Who would have thought Napster would be so brilliantly laid out? Next thing I did was open up WinID to see if my little text classes had class ID’s. WinID is always on top, so when i opened up Napster i hovered my mouse over the title text and lo-and-behold! The text even had a class ID, which means grabbing the info couldn’t even be easier!
Now this tutorial doesn’t really intend to give you the ins and outs of P/Invoke in C#, nor does it really intend to go through a massive tutorial on sending information to the personal message box of MSN messenger. However, if you check the links under further reading you will find all my sources, including Bart De Smet’s C# code for sending information to MSN’s personal message. Anyway, first thing first was to set up my P/Invoke imports. (Keep in mind you need to include
at the top of your form) Below are the P/Invokes i set up, i will explain them further after:
[DllImport("USER32.DLL", CharSet = CharSet.Auto)] public static extern int SendMessage(int hWnd, uint msg, int wparam, StringBuilder text); [DllImport("User32.dll")] public static extern int FindWindow(String lpClassName, String lpWindowName); [DllImport("user32.dll")] private static extern int FindWindowEx(int parentHandle, int childAfter, string className, string windowTitle); [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern int GetDlgItem(int parent, int id);
Each DllImport essential makes a call to the windows .dll files ‘user32.dll’. This import essentially allows for the use of this technically un-managed set of functions to be used within the .NET managed environment. The first import, for the sendmessage function, allows you to make direct interactions with running programs, such as button clicks or in our case, GetText. If we look at the function parameters, the first parameter is the handle of the window we are using the call on, the second is the handle of the window’s component, in our case 0, the third parameter is the code of the message to be sent and the final parameter is the custom portion of my call, in this case a StringBuilder to store my SendMessage response. The second import is used to find the handle of the window we are searching for by searching for the window’s title, in this case “Napster”. The third import is used to find the handles of windows within the main application we are searching within. The fourth import is used to return the ID of the dialog we are searching for, in this case or song information! I haven’t really gone into detail about what each function does, but if further clarification is needed i recommend doing some research on P/Invoke in C#. It is also important to note that the wParam for the sendmessage function is a hexadecimal number code which is custom to each type of call. The code for GETTEXT is 0x000D. Next comes the meat and potato of the code, which essentially works its way logically down the tree we can see in Winspector Spy, grabbing handles and then ID’s and then finally our song information!
hwnd = FindWindow(null, "Napster"); StringBuilder titletext = new StringBuilder(buffer_size); StringBuilder artisttext = new StringBuilder(buffer_size); StringBuilder timetext = new StringBuilder(buffer_size); StringBuilder totaltimetext = new StringBuilder(buffer_size); CUISW = FindWindowEx(hwnd, 0, "CUISplitterWindow", null); frame = FindWindowEx(CUISW, 0, "#32770", null); CUIHORSW = FindWindowEx(frame, 0, "CUIHORSplitterWindow", null); hwndChild = FindWindowEx(CUIHORSW, 0, "#32770", null); title = GetDlgItem(hwndChild, id); artist = GetDlgItem(hwndChild, id2); time = GetDlgItem(hwndChild, id3); totaltime = GetDlgItem(hwndChild, id4); SendMessage(title, WM_GETTEXT, buffer_size, titletext); SendMessage(artist, WM_GETTEXT, buffer_size, artisttext); SendMessage(time, WM_GETTEXT, buffer_size, timetext); SendMessage(totaltime, WM_GETTEXT, buffer_size, totaltimetext);
As can be seen, i make a number of calls, firstly to grab the window ID of the current instance of Napster. I then define some StringBuilder variables to store my data in. Next i sequentially grab the handle of each child window until i eventually have the handle of the window that contains the information i require. I then do the tricky bit of grabbing the handle of the dialog boxes (aka text boxes) containing the data i want. The trick is, where did the ID’s come from? Well from WinID and i had defined them earlier in the code as constants. The sendmessage is then used using my WM_GETTEXT constant which contains the value of my hexadecimal parameter (it also may be handy to note the ID’s are hex as well). This then returns the information i want into my earlier created StringBuilder variables and voila! I have successfully grabbed the information from Napster! Now the key thing to note here that whilst this is an extremely specific use of grabbing formation through Napster Interoperability. If there is any real interest in this i may release a small library for grabbing particular information from Napster such as text of songs, or button presses or even searching within Napster!