The adventures of writing a simple Skype API client: Moodgeist Pinger
As I said before, I rewrote the Moodgeist Pinger myself. The objective was twofold.
First, I just wanted a newer Pinger. The one that Kevin wrote worked fine for others, but gave some strange memory errors on my PC. And I just wanted to update it.
Secondly, I wanted to see what it takes to write a Skype API client these days.
My background
I’m what you’d call a “Sunday programmer”. It’s not my main career, but I do some scripting once in a while. I find it important to “not lose the edge” and thus I do tinker both on the client and server side when I can. I haven’t really done much on the desktop except writing a crazy plugin in Outlook VBA that bridged your Outlook contact list with a MySQL client database. And that never got used but was fun to do. But this means I had at least some previous exposure to VB and COM.
The tools
I decided to use Visual Studio.NET Express Edition and more specifically Visual Basic 2005 Express Edition, since that was most readily available and looked like something I would be most comfortable with. Frankly, before I learned about VS Express Edition, I didn’t know of any good free tools that I could have used to write cool native Windows desktop programs.
From Skype side, we have Skype API bundled with each client, and for easy access, we now have Skype4COM. The examples are mostly done for VBScript, but it’s not difficult to figure out their “full” Visual Basic counterparts. The documentation is there, but most of the time you can live with Visual Basic’s Object Inspector and suggestions that automatically inspect and list properties and methods of current object.
So as long as the installation went, it was:
- download and install VS.NET Express (I just installed VB and MSDN documentation)
- download and install Skype4COM
Now we’re sitting behind the wheel, good to go.
Doing the “hello, world” part
… is really easy in Visual Basic. You just create a new project and don’t really need to touch any settings — you get an empty dialog and can “run” your program right away. So developing the application consists of two main parts — first, designing the UI and controls, and second, writing the code that does all the work.
Since Moodgeist Pinger UI is really minimal, I spent most of the time in the code window. When you double-click on a form element, VB automatically creates or opens the relevant code function. Once you get event-driven programming (versus procedural/functional), meaning that you get that it’s the events, not your “main loop” that control which things happen when, it’s a breeze. If you double-click on the main form, the “form load” function opens. If you double-click on a button, the button handler opens. And so on.
Skype API access
Now came the time to try out some things with Skype API. This was easy too — having registered the COM DLL, just add a reference to Skype4Com in project properties, and you got access to the API straight away. Upon program lanching (first form loading), you initialize the “Skype object” and can then access all the objects, properties and methods as its children. I’ve worked a bit with the raw Skype API directly too and it’s not really that easy, but COM nicely hides all the complexity. However, if you’re up for adventure, nothing stops you from implementing the raw Windows Messaging-based Skype API, since there are some things that you can do with it but can’t with Skype4COM (mainly being notified from Skype about many events).
Say, for example, you wanted to enumerate all your Skype contacts and display their name. You could just do:
For Each contact In oSkype.Friends
MsgBox(contact.FullName)
Windowing
I’m pretty anal about having a “correct” UI that doesn’t clutter your desktop but is easy to access. I’m almost there with the Pinger. I wanted “minimize to system tray” and this is provided by the NotifyIcon object and everything around it. You add an icon, add the context menu do it, and that’s pretty much it.
What’s missing is that if you minimize the window, you need it to truly hide and not go into some weird object near the Start button as it does now. I found it difficult since there for some reason isn’t an event for the Minimize event – but there’s an event when you change window size. I didn’t get the logic fully right with it so the Pinger is not yet perfect in windowing.
Web requests
… were easy to do with .NET if you know how HTTP works. There are Request and Response objects available, but no explicity way to automatically populate a POST request. But if you happen to know that a HTTP POST request is nothing more than properly URL-encoded key/value pairs sent in the request body with the content type of “application/x-www-form-urlencoded”, you can use helper functions like System.Web.HttpUtility.UrlEncode to “manually” construct the request body.
I ran in a bit of trouble with my POST-s – they just didn’t work at all if I tried to post data. Since I’d been too aggressive in error suppression (the Pinger is really quiet about errors, and purposefully so – no need to annoy people if you can’t do anything about the error), took me a while to hunt it down. But turns out that you can’t change the Request object properties once you’ve started writing body data to its RequestStream. So you need to set Content-Length, Content-Type and all those things before actually posting any data.
HTTP RFC specifies that a client shouldn’t fire more than two requests at a time (section 8.1.4). Most browsers, RSS readers etc that I use seem to respect this. So no reason why I should not. So when I had the basic flow in place for enumerating and pinging contacts, I also implemented a global request counter that all the ping worker threads use to check if there are already more than two requests going on. If so, they wait until they can move on — just increment a counter at the start of the request, and decrement at the end of it. I’m not sure how thread-safe this is (probably isn’t), but seems to work fine for me.
Threading
Enumerating hundreds of contacts and posting all their data to the web can take quite a while. And if you’re doing it all in the frontend, your UI basically freezes. So very soon you may want to do threading.
It used to be the case that threading was complex and I was scared of it. Because in low-level languages like C++, you need to take care of all sort of crazy memory-management things to make sure that you’re not eating up all the memory and trashing the user’s computer. But in .NET, it’s just a matter of encapsulating your thread code in a separate subfunction and then initializing and running the thread. You can also pass parameters as “Object”, which is another word for “anything”. I needed to pass structured data, but couldn’t recall and find from help how to define custom data structures. Since the count of elements is always the same, I decided my Object is going to be a simple array instead of structured data with named parameters.
There are actually several levels of threads in Pinger. First there’s the frontend thread that does all the UI. Then there’s one “überfunction” that enumerates all the contacts and calls the “ping” function which actually does the web request. And the “ping” functions are again all in separate threads.
There are some tips in MSDN about how to update the UI properly in a multithreaded context. I needed to use this to update the status bar in the main form when pings start and end, and to enable and disable the “full ping” button.
Here’s the simplest version of starting a background thread.
Dim pingWorkerTrd As New Thread(AddressOf pingworker)
pingWorkerTrd.IsBackground = True
pingWorkerTrd.Start()
I’m reading just now that there’s the ThreadPool concept that might be more appropriate for background tasks. So let’s use that in the next versions.
Running on startup
As far as I know, running on Windows startup should be as easy as updating the registry key Software\Microsoft\Windows\CurrentVersion\Run with the full path to your program, and it then runs at Windows sign-in. And you can use it in either HKEY_LOCAL_MACHINE if you want the program to run for all users, or HKEY_CURRENT_USER if you want it to run for only the current user. I used the latter because updating it does not require admin privileges, and so the whole Pinger can run as non-Administrator user once installed.
There’s one problem with this approach. It doesn’t work for me
I set the registry value, but nothing happens at sign-in. Should I do anything more? It seems that I’m at least halfway there, since every time I update the value, Windows Defender kicks in to confirm that I really want to set this as the startup program.
Installer
Once you’ve completed your program, the time comes to “pack up and ship”. VB 2005 makes it easy for you – supposedly.
There are two publishing modes. The preferred one seems to be something that they call “ClickOnce Publishing”. This is the first time I saw this. I’ve never installed a ClickOnce program. It seems to be some sort of new installer type geared towards corporate deployments, where I’m sure it works great, but it’s not so cool when you just want to publish your damn program for people to the Internet so that they could download and install it as a single installer executable. At least I didn’t get it to work. It builds setup.exe which you can run, but apart from that, you need to give people a whole ton of different “manifest” and “properties” files. Why isn’t everything in the same EXE?
The other mode seems to be what they call Deployment Projects. This looks more like the standard installer stuff. But this is also where things got a bit surreal. MSDN Help says:
To create a new deployment project
1. On the File menu, point to Add, then click New Project.
2. In the resulting Add New Project dialog box, in the Project Types pane, open the Other Project Types node and select Setup and Deployment Projects.
This is nice. Now if I clicked File, Add, New Project, this is what I get.
Either I am really stupid, or there is no “Project Types” here. (One doesn’t outrule the other.)
So I needed to look for alternatives. I recalled there’s a system called NSIS — Nullsoft Scriptable Install System. Nullsoft is the guys who made Winamp, and this system is still used in Winamp installations. I’ve always thought of Winamp and their installer as a “lean and mean” way of doing things, and even though I had never looked at NSIS before, it sounded like something that would work.
In NSIS, you first write or generate an “installer script”, which is just a text file containing “instructions” about the installer. Then you dump the script and the installation files in a directory, and you “compile” it all into a final installation file. I like scripted approaches more than GUI, because in the guts of the GUI, there’s still a compiler that compiles your graphic actions into a script and eventually into machine code. So while GUI works great for things like designing dialogs, there are other things where I feel more comfortable with scripts, and installers are definitely one of those. So I just wrote a NSIS script (there’s a web-based generator that helps you get going with the basics) and I was pretty much there.
In closing
So there. Working with Skype COM API is fairly straightforward and most of the things you’ll come across when writing and packaging your application have nothing to do with Skype or the API at all. The Pinger program itself currently has about 200 lines of code and only about 20 of those are explicitly associated with Skype — the rest are about overhead (comments, function definitions), GUI management, web requests and threading.



Great post. I think the reason why you didn’t see the deployment project type is that you’re using Visual C# Express Edition.
yep, thanks Rory. it was actually Visual Basic, but yes, express edition. I guess that doesnt support making such installers then. NSIS works fine for me.