Recording Skype
Written by Harry Fairhead   
Sunday, 12 July 2009
Article Index
Recording Skype
Getting started
Message in a queue
Talking to Skype
Make the call
Talking to Skype

If you add a call to SkypeFirstConnect to the button’s click handler and run the program you should see Skype status messages displayed in the textbox until the “Available” status is reached and the connection made. Now we have Skype’s attention it’s time to send it some commands. Fortunately this is mostly more of the same. We send Skype a message and Skype sends us messages back, letting us know what is happening. The new element is that the messages now contain commands and status information in the form of strings. To do this we need a slightly different version of the SendMessageTimeout API call. The only difference is that the lParam parameter is used as a pointer to a CopyDataStruct:

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

Of course we also need a definition of CopyDataStruct and this can be found in the API documenation and translated to C#:

[StructLayout(LayoutKind.Sequential)]
public struct CopyDataStruct
{
public string ID;
public int Length;
public string Data;
}

This struct is used to pass the command and data string in the Data property as part of the content of a WM_COPYDATA message. Notice that WM_COPYDATA isn’t a custom message, it’s a perfectly standard Windows message and can be used by any application to transfer large amounts of data. The WM_COPYDATA message has to be defined as:

public static readonly uint
WM_COPYDATA = 0x004a;

Now we have everything we need to send Skype a command. If you look at the documentation all of the commands take the form of text with parameters. For example, to dial a number you use the “Call” command followed by the number you want to call. To send a command to Skype it’s a good idea to implement a suitable method:

public IntPtr SendCommand(string Command)
{
skypedata.valid = false;
skypedata.data = string.Empty;

The Skypedata buffer has been invalidated to get ready for Skype to respond to the command. To send the command we have to initialise a suitable CopyDataStruct:

CopyDataStruct data = 
new CopyDataStruct();
data.ID = "1";
data.Data = Command;
data.Length = data.Data.Length + 1;

The ID property can be used to identify muliple commands of the same type but, as we are using a “blocking” appproach to using Skype, one comamnd at a time is the rule. Now we can send the command:

 IntPtr result;
IntPtr RetVal = SendMessageTimeout(
HSkype,
WM_COPYDATA,
this.Handle,
ref data,
SendMessageTimeoutFlags.SMTO_NORMAL,
100,
out result);
return RetVal;
}

As soon as Skype gets the message it sends a WM_COPYDATA message back with the Data property of the CopyDataStruct full of text that give the status and other information about what is happening.

How do we deal with this return message? In the same way as the APIAttach message earlier, i.e. within the overridden WindProc method. We simply have to add another if statement that checks to see if the message is a WM_COPYDATA message:

if ((UInt32)m.Msg == WM_COPYDATA)
{

Next check to make sure that the WM_COPYDATA message was sent by the Skype window we have attached to, after all other application windows can send WM_COPYDATA messages for a range of reasons:

if (m.WParam == HSkype)
{

As long as this is indeed a WM_COPYDATA message from Skype we can unpack the data it contains by casting the object returned by the GetLParam method:

CopyDataStruct data =
(CopyDataStruct)m.GetLParam(
typeof(CopyDataStruct));

To understand exactly what is going on here you need realise the that GetLParam method uses the LParam value as a pointer to an area of memory which it then converts to an object of the type you specify. Finally we move the returned data into the skypedata buffer, display it in the textbox and set the buffer valid indicator to true:

  skypedata.data = data.Data;
m.Result = new IntPtr(1);
textBox1.Text += "Skype data=" +
skypedata.data +
Environment.NewLine;
skypedata.valid = true;
return;
}
}

<ASIN:1597490326>

<ASIN:0596101899>

<ASIN:0977057526>



Last Updated ( Monday, 27 July 2009 )