Click here to Skip to main content
15,895,192 members
Articles / Programming Languages / C#

Controlling Skype with C#

Rate me:
Please Sign up or sign in to vote.
4.59/5 (40 votes)
29 Oct 2013CPOL3 min read 282K   8K   116   40
C# example code for controlling Skype
Sample Image - skypecontrolapicsharp.gif

Read this first - Discontinuation of Skype API

Microsoft plans to switch off the Skype desktop API in December 2013. The explanation is that:

"The Desktop API was created in 2004 and it doesn’t support mobile application development. We have, therefore, decided to retire the Desktop API in December 2013."

However, many developers are using Skype API in their products enhancing Skype functionality. And many Skype users rely on these products in their businesses and everyday life.

There is a petition initiative which requests Skype to reconsider this decision.

Introduction

I have put together an example code in C# for controlling the Skype VoIP application. The Skype Control API documentation is freely available. So why not give it a try with C#?

The downloadable ZIP package contains a Visual Studio 2005 solution with two projects - the Skype Control class library and the Test Application.

Background

The Control API for Windows uses window messages for communication between Skype and client applications. This involves usage of Windows API functions. Working with window messages is not directly supported by the .NET Framework classes. Fortunately there is a mechanism in .NET called platform invoke, which enables calls to unmanaged code in DLLs.

I will not explain the Control API in detail here, however I will provide enough information for everyone to start experimenting with the code and playing with Skype through the Test application.

Communication Initiation

Skype uses window messages to communicate with client applications. Registered window messages are used to initiate communication. The SkypeControlAPIDiscover message is used to find the Skype window handle by broadcasting it to all windows. A client application window handle must be specified when sending this message. Skype, if running, will respond with SkypeControlAPIAttach message sent directly to our client window.

C#
internal class Constants
{
    public const string SkypeControlAPIDiscover = "SkypeControlAPIDiscover";
    public const string SkypeControlAPIAttach = "SkypeControlAPIAttach";
}

The LPARAM of the SkypeControlAPIAttach message contains the attach code. This can be Success in case of a successful connection. When the client application tries to connect for the first time, Skype pops up an authorization dialog and notifies the client by sending SkypeControlAPIAttach message with the PendingAuthorisation code. Depending on the user action, Skype will next send a Success or Refused notification.

C#
public enum SkypeAttachStatus : uint
{
    Success = 0,
    PendingAuthorizaion = 1,
    Refused = 2,
    NotAvailable = 3,
    Available = 0x8001
}

When the Skype application is starting, it broadcasts SkypeControlAPIAttach message with the Available code.

Be aware that Skype assumes that our client application will process messages with result of 1 within one second, otherwise the connection times out and the communication is closed. This is important to know while debugging the client. Fortunately Skype can be set to a special "development mode" (by modifying the registry, see Skype API documentation).

Sending Commands

The WM_COPYDATA window message is used for sending Skype commands and receiving command replies and notifications.

Handling the WM_COPYDATA messages in C# is a little bit tricky. The CopyDataStruct needs to be correctly marshaled to and from the native code. The straightforward solution is to simply pass the CopyDataStruct this way:

C#
[DllImport("user32.dll")]
public static extern IntPtr SendMessageTimeout(IntPtr windowHandle,
    uint Msg,
    IntPtr wParam,
    ref CopyDataStruct lParam,
    SendMessageTimeoutFlags flags,
    uint timeout,
    out IntPtr result
);

Note that when filling CopyDataStruct, the Length member also counts the terminating '0' character.

Code for getting the CopyDataStruct form is also straightforward, the Message class has a handy GetLParam method for this.

C#
Platform.CopyDataStruct aCDS = 
    (Platform.CopyDataStruct)m.GetLParam(typeof(Platform.CopyDataStruct));

string aResponse = aCDS.Data;

The SkypeControl Library

All the code for handling window message level communication can be found in the SkypeClient class (SkypeControl library). Platform specific stuff is in the Platform.cs. All of the above is internal to the Skype control library.

The SkypeProxy class is designed to be used for controlling Skype by a client application. Currently, it is only wrapping calls to the SkypeClient class and contains only methods for low level Skype API communication. It is meant to be expanded with methods that encapsulate Skype API at a higher level, e.g. GetUserStatus, Ping.

To control the Skype, just create an instance of the SkypeProxy class. Communication is initiated by a call to Connect. A successful connection is indicated by firing of the SkypeAttach event with AttachStatus set to Success.

Commands are sent to Skype by calling Command with appropriately formatted text command. Results and notifications from Skype are received through the SkypeResponse event.

For more information on Skype Control API and command syntax, please refer to the Skype documentation which is freely available at Skype developer zones.

Enjoy!

History

  • 15th February, 2006: Initial post.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Slovakia Slovakia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralSkype login Pin
KhangBKIT9-Oct-08 10:06
KhangBKIT9-Oct-08 10:06 
GeneralNot UTF 8 Pin
Tor-Helge Persett29-Jul-08 21:26
Tor-Helge Persett29-Jul-08 21:26 
Generalwow, super examplik diky moc Pin
Member 301469616-Jun-08 22:35
Member 301469616-Jun-08 22:35 
GeneralConnect Refused [modified] Pin
Rajan Hans22-Apr-08 17:06
Rajan Hans22-Apr-08 17:06 
GeneralRe: Connect Refused Pin
Gabriel Szabo23-Apr-08 3:54
Gabriel Szabo23-Apr-08 3:54 
GeneralRe: Connect Refused Pin
Rajan Hans23-Apr-08 4:54
Rajan Hans23-Apr-08 4:54 
GeneralRe: Connect Refused Pin
xaml.net4-Aug-10 23:20
xaml.net4-Aug-10 23:20 
GeneralUnicode bug fix Pin
Vlad019-Sep-07 6:42
Vlad019-Sep-07 6:42 
First of all, I am sorry for underestimating
the SkypeControl wrapper in my previous message.
Creating a wrapper alternative to standard
Skype4COM is interesting per se. Especially,
if it is a purely .NET wrapper. And
I was wrong, stating that this wrapper
"duplicates part of the functionality
of the standart Skype4COM". In fact,
both wrappers realize the whole functionality
of raw Skype API. And SkypeControl
permits one to do a bit more than
Skype4COM: it seems that there is no counterpart
in Skype4COM to Disconnect() method of
SkypeControl.SkypeProxy class.

Of course, Skype4COM, being a "thick"
wrapper, permits one to write shorter
code for many operations. But "thin"
SkypeControl, being highly extensible,
may serve as a bases for writing "thick"
.NET wrappers alternative to Skype4COM
(and more "developer-friendly" than the latter).

The only drawback of the original SkypeControl
code is that it has a bug: commands, containing
non-ASCII Unicode characters
(like "MESSAGE user something in hebrew or cyrillics")
are non-adequate (see http://forum.skype.com/index.php?showtopic=96777&st=0 ).
The reason is that Skype communicates
with external programs via UTF8 coded sz strings
(lpData member of COPYDATASTRUCT), whereas the .NET
counterpart of this (CopyDataStruct.Data) is
of the type string. Default marshaling for strings
is CharSet = CharSet.Ansi, which means that
only strings not containing characters with highest byte
different from 0 will be marshaled correctly.

Replacing "string Data" with "byte[] Data" in the definition
of CopyDataStruct (together with converting strings to
UTF8 byte arrays and vice versa where necessary)
does not help. .NET arrays of bytes are not being
marshaled to plain C-Style arrays. In fact,
they are not being marshaled at all. Managed array
just coincides with unmanaged one. But this unmanaged array
is not a plain C-style array, but the so called SAFEARRAY.
It contains 12 extra bytes before the "real" C-type array.
And there is no way to tell to Skype that he must read
the corresponding data starting at position 12 instead of
position 0.

So the marshaling must be a bit more complicated.
In the bug fix I wrote it uses "manual" marshaling
via Marshal.Copy() instead of "automatic" one.
The changes I made to the original code are as follows.

I have replaced original definition of CopyDataStruct
(in Platform.cs) with the following one:

<br />
[StructLayout(LayoutKind.Sequential)]<br />
public struct CopyDataStruct<br />
{<br />
  public UInt32 ID;<br />
  public int Length;<br />
  public IntPtr Data;<br />
}<br />

replacing in the original definition "string Data" by "IntPtr Data".
I have changed as well the type of ID member from string to UInt32.
Because it is just this type that corresponds to DWORD
entering into unmanaged COPYDATASTRUCT. Of course,
there will be no bug produced, if one compiles the code for x86:
DWORD and pointer to something both occupy 4 bytes, and ID
is just the placeholder not used for nothing.
But on x64 a pointer occupies 8 bytes, so
the incorrect type for ID will produce a real bug here.

The changes to the file SkypeClient.cs:
a) The line
<br />
using System.Runtime.InteropServices;<br />


is added
b) In the code of Command() 3 lines after the line
<br />
Platform.CopyDataStruct aCDS = new Platform.CopyDataStruct();<br />

are replaced by:
<br />
aCDS.ID = 0;<br />
theCommand += (char)0;<br />
byte[] data = Encoding.UTF8.GetBytes(theCommand);<br />
IntPtr buffer = Marshal.AllocCoTaskMem(data.Length);<br />
Marshal.Copy(data, 0, buffer, data.Length);<br />
aCDS.Data = buffer;<br />
aCDS.Length = data.Length;<br />

and the line
<br />
Marshal.FreeCoTaskMem(buffer);<br />

is added after the line with "SendMessageTimeout(...)".
c) In the code of WndProc() the line
<br />
string aResponse = aCDS.Data;<br />

is replaced by:
<br />
byte[] data = new byte[aCDS.Length-1];//The last byte is 0; bad text formatting if copied to data!<br />
Marshal.Copy(aCDS.Data, data, 0, aCDS.Length-1);<br />
Marshal.FreeCoTaskMem(aCDS.Data);<br />
string aResponse = Encoding.UTF8.GetString(data);<br />

In fact I have not throw away the original buggy code from
Platform.cs and SkypeClient.cs. I've just added the switch
<br />
#define UNICODE_BUG_FIX<br />

to the beginning of both files. If one comments out
these switches, the original code is reproduced.

I have added as well the switch
<br />
#define Skype4COM<br />

to the file Form1.cs of "TestApp" project
and added the code to it, permitting one
to compile TestApp with Skype4COM library
instead of SkypeControl. I've done this
after algoaddict discovered the Unicode bug
in the original code (see the link above)
as a straightforward way to circumvent the bug.

The bug fixed project can be downloaded here:
http://forum.skype.com/index.php?act=Attach&type=post&id=11110
GeneralRe: Unicode bug fix Pin
Mikhail Diatchenko12-Oct-08 21:34
Mikhail Diatchenko12-Oct-08 21:34 
QuestionWhy not just use Skype4COM.dll? Pin
Vlad013-Sep-07 4:26
Vlad013-Sep-07 4:26 
GeneralSkype Wrapper for .NET Pin
Nicolae Mogoreanu21-Jul-07 10:51
Nicolae Mogoreanu21-Jul-07 10:51 
GeneralVB.NET version Pin
hlbcal18-Feb-07 4:14
hlbcal18-Feb-07 4:14 
Generalneed help!!! Pin
gfhyang3-Jul-06 21:02
gfhyang3-Jul-06 21:02 
GeneralRe: need help!!! Pin
10der9-Nov-13 11:33
10der9-Nov-13 11:33 
QuestionSkype sound stream Pin
Ermi30-Mar-06 21:33
Ermi30-Mar-06 21:33 
Generaljópofa :) Pin
NJoco23-Feb-06 6:11
NJoco23-Feb-06 6:11 
GeneralRe: jópofa :) Pin
Gabriel Szabo23-Feb-06 23:55
Gabriel Szabo23-Feb-06 23:55 
Generalgreat Pin
MP3Observer16-Feb-06 22:49
MP3Observer16-Feb-06 22:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.