This post is also available in: Русский (Russian)
Once upon a time I chatted in one cozy Skype conference and suddenly one of the members got a rather interesting idea: to develop a Skype plugin, which allows to view content of links thrown into chat without opening them. "Why not?", - I thought. It is possible to open pictures in reduced size, if the link points to the image (including animated ones). If the link leads to usual HTML page, it is possible to display its title. Running ahead, I'll show you the result:
And now some history and coding details. Those who want to download the plugin right away please go to the end of the article. So, I
opened Visual Studio opened Google to see, what public API are available for Skype at the moment. It turned out that there is no SDK for Skype. Earlier there was an SDK based on COM interfaces (skype4com), but it is not supported anymore, and it is unreasonable to use it. There is a defective mini-API based on URI (description), but it is impossible to do something useful with it. Thus, the only way to introduce your own functionality to Skype is to examine the program structure and try to embed your own code (hooks, windows messages interception or something) in it. Fortunately, Skype user interface is window-based (usual windows, without WPF and other wrappers), and this plays into our hands, because we will be able to intercept window messages and WinAPI functions responsible for text drawing (I remind that it is required to display link content preview when the cursor hovers it). As a result I came to the following decisions on Skype functionality modifications:
1. How to add your own option to the Skype menu? I hooked WinAPI functions DrawMenuBar and PeekMessageW. In PeekMessageW we check, whether the thread which called the function is the main Skype window thread, and if so, then menu redrawing is performed using already hooked DrawMenuBar function, and after this PeekMessageW hook is turned off. This is required for the case when the plugin is injected into Skype, which is working at the moment. If the plugin is injected into Skype before launch, then Skype, most likely, will call our hooked DrawMenuBar function itself, but it will be better to make sure anyway. What happens in hooked DrawMenuBar function? In general, nothing complicated: the main Skype window subclassing is performed (to add new menu click handling) and the menu itself is being added actually.
2. How do we understand that the user hovered the cursor over the active link in the Skype chat window? To do this, I hooked the WinAPI function ExtTextOutW. Skype uses it to draw text. It was not easy to trace, when actually the active link in the chat window is drawn, and still this plugin mode doesn't work stable enough (sometimes the plugin considers that the user placed the cursor over the link, but this is not true), but I'm lazy to improve this to perfection now. So, in intercepted ExtTextOutW function it is checked if the call came exactly from Skype.exe module, that cursor is visible and no mouse buttons are pressed, and that exactly the URL beginning with http:// or https:// is passed. Sometimes Skype likes to redraw links in opened Skype window without dependence on whether the user hovers them, and the plugin may consider that the user hovered the cursor over the link in this case. All hooks are performed in main.cpp file of the project, and I can offer people who know coding as an exercise to understand, how you can distinguish user activity from some random link redrawing in this scenario more precisely. At first, for example, you can make sure that the cursor is really over Skype message window.
3. There is mode of operation in the plugin, when link preview is displayed if the link is clicked, and a link itself can be opened with double click. This is made with simple ShellExecuteW function hook. The most complicated part is to write your own handler, which distinguishes click from double click, and it is made with boost::asio (double_click_monitor.h, double_click_monitor.cpp).
Now in short about building. I decided not to reinvent the wheel and used some libraries. Here is the list of the libraries, which are necessary to build full plugin version:
1. curl 7.35.0 - well, this is clear. Used for Web surfing with cookies and redirects support.
1.1. OpenSSL 1.0.0e - used in curl building to make in support HTTPS-links.
1.2. zlib 1.2.8 - used in curl building to make gzip work.
1.3. libidn 1.28 - used in curl building to support cyrillic and other japanese domains. This library is unessential, if you configured curl to use WinAPI, which provides similar functionality, but this will work on Windows Vista or higher. To build curl with libidn in Windows, you will have to fuck a lot with curl CMakeLists (because bat files for building in Windows, which are provided with curl, are unable to use libidn). If anyone will be interested, I can provide details in commentaries.
2. libiconv - to display page header preview in different encodings. It was built for Windows with this project.
3. MinHook - to perform WinAPI function hooks.
4. Boost 1.55 - for other purposes like double click tracking in Skype window or saving/loading settings in XML format.
5. GdiPlus - standard Windows library, used to change size of images or text drawing.
Besides the DLL file itself, which is injected to Skype, I developed the loader (EXE file) too, which allows to load the plugin to Skype (if it is running already), or to load Skype with preloaded plugin.
Projects are available for download HERE (only VS2010 projects without libraries and binaries). All projects are built in Visual Studio 2010. To build the plugin and the loader Debug or Release configurations are used. DebugExe configuration allows to create plugin debug version instead of DLL file (EXE file, see main.cpp code). If you decide to build everything on your own, get ready to
notable butthurt download the big number of libraries and not very easy process of building them. In Visual Studio project settings all library paths are set like they were located on my computer, so you have to edit them to your own.
For those, who didn't read the article (or those who read, and doesn't want to build): DOWNLOAD PLUGIN (BINARIES). Start SkypeLoader.exe (it is not important whether the Skype itself is running). You can place SkypePreview.dll and SkypeLoader.exe in any folder, then create a shortcut on Desktop or in Start menu to SkypeLoader.exe file, and then use it to launch Skype.