你也可以玩转Skype -- 基于Skype API开发外壳程序入门
Skype是目前这个星球上最厉害的IM+VOIP软件,Skype现在已经改变了全球2.8亿人的生活方式。你,值得拥有! :)
Skype中文官网:http://skype.tom.com/
Skype全球官网:http://www.skype.com/
Skype也是世界上最开放,最具创新意识的IM工具,他提供了Skype API, Skype4COM, Skype4Java几种形式的开发接口给Skype爱好者编写Skype的交互程序或者Skype的插件。你可以使用任何你熟悉的语言,比如C/C++,VB, C#,Delphi,Java甚至PHP,VBScript。通过你的专业知识,去影响2.8亿的Skype用户。你也可以做到!:)
Skype全球开发者社区:http://developer.skype.com/
下面我们将展示一个最简单的访问Skype API的C++代码:
这里可以下载对应的VC6工程:http://wh.hust.colin.googlepages.com/SkypeInteractiveDemo.rar
//
// Copyright (c) 2004-2006, Skype Limited.
// All rights
reserved.
//
// Redistribution and use in source and binary forms, with
or without
// modification, are permitted provided that the following
conditions
// are met:
//
// * Redistributions of source
code must retain the above copyright
// notice, this
list of conditions and the following disclaimer.
// *
Redistributions in binary form must reproduce the
above
// copyright notice, this list of conditions
and the following
// disclaimer in the documentation
and/or other materials provided
// with the
distribution.
// * Neither the name of the Skype Limited nor the
names of its
// contributors may be used to endorse
or promote products derived
// from this software
without specific prior written permission.
//
// THIS SOFTWARE IS
PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE
// POSSIBILITY OF SUCH DAMAGE.
//
// tab size: 2
#include <windows.h>
#include <conio.h>
#include
<stdio.h>
#include <string.h>
#include
<process.h>
#include <rpcdce.h>
HWND hInit_MainWindowHandle; ///<本程序创建的窗口的句柄
HINSTANCE
hInit_ProcessHandle; ///<本程序的进程句柄
char
acInit_WindowClassName[128]; ///<本程序创建的窗口类名
HANDLE
hGlobal_ThreadShutdownEvent;
bool volatile
fGlobal_ThreadRunning=true;
//Skype定义的消息ID,Skype通过向第三方程序发送这类消息来告知请求连接的结果
UINT
uiGlobal_MsgID_SkypeControlAPIAttach;
//Skype定义的消息ID,当第三方程序想获取Skype的交互时,
//必须通过广播(HWND_BROADCAST)发送这个消息,Skype收到后给用户弹出提示
//当用户允许后,交互关系就建立起来了。
UINT
uiGlobal_MsgID_SkypeControlAPIDiscover;
//Skype的窗口句柄
HWND hGlobal_SkypeAPIWindowHandle=NULL;
//BOOL变量标识是否打印更详细的消息内容
#if defined(_DEBUG)
bool volatile
fGlobal_DumpWindowsMessages=true;
#else
bool volatile
fGlobal_DumpWindowsMessages=false;
#endif
DWORD
ulGlobal_PromptConsoleMode=0;
HANDLE volatile
hGlobal_PromptConsoleHandle=NULL;
enum
{
//第三方程序连接成功,在wParam中可以获取到Skype的API窗口句柄
SKYPECONTROLAPI_ATTACH_SUCCESS=0,
//Skype已经收到连接请求了,并给用户弹出了第三方程序请求访问Skype的提示
//这时候连接并没有成功建立,必须等到SKYPECONTROLAPI_ATTACH_SUCCESS消息才行
SKYPECONTROLAPI_ATTACH_PENDING_AUTHORIZATION=1,
//Skype用户拒绝了第三方程序的访问请求
SKYPECONTROLAPI_ATTACH_REFUSED=2,
//API接口当前不可使用。这种情况有时候发生,比如当前Skype没有任何用户登录进去。
//第三方程序必须等到Skype广播了SKYPECONTROLAPI_ATTACH_API_AVAILABLE消息时再去尝试连接才有效
SKYPECONTROLAPI_ATTACH_NOT_AVAILABLE=3,
SKYPECONTROLAPI_ATTACH_API_AVAILABLE=0x8001
};
//从标准输入窗口中获取一行输入到pacPromptBuffer中
bool Global_Console_ReadRow( char
*pacPromptBuffer, unsigned int uiMaxLength)
{
HANDLE hConsoleHandle,
hDuplicatedConsoleHandle;
DWORD ulCharactersRead,
ulConsoleMode;
unsigned int uiNewLength;
BOOL
fReadConsoleResult;
bool fReturnStatus;
char
cCharacter;
fReturnStatus=false;
//获取标准输入窗口的输入缓冲区句柄
while((hConsoleHandle=GetStdHandle(STD_INPUT_HANDLE))!=INVALID_HANDLE_VALUE)
{
if(
DuplicateHandle( GetCurrentProcess(),
hConsoleHandle,
GetCurrentProcess(),
&hDuplicatedConsoleHandle, 0,
FALSE,
DUPLICATE_SAME_ACCESS)==FALSE
)
break;
GetConsoleMode(
hDuplicatedConsoleHandle, &ulConsoleMode);
SetConsoleMode(
hDuplicatedConsoleHandle,
ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT|ENABLE_ECHO_INPUT);
hGlobal_PromptConsoleHandle=hDuplicatedConsoleHandle;
ulGlobal_PromptConsoleMode=ulConsoleMode;
fReadConsoleResult=ReadConsole(
hGlobal_PromptConsoleHandle,
(LPVOID)pacPromptBuffer,
uiMaxLength, &ulCharactersRead, (LPVOID)0);
if(
hGlobal_PromptConsoleHandle==(HANDLE)0
)
break;
hGlobal_PromptConsoleHandle=(HANDLE)0;
SetConsoleMode(
hDuplicatedConsoleHandle,
ulConsoleMode);
CloseHandle(hDuplicatedConsoleHandle);
if(
fReadConsoleResult==FALSE || ulCharactersRead>uiMaxLength
)
break;
pacPromptBuffer[ulCharactersRead]=0;
uiNewLength=ulCharactersRead;
while(uiNewLength!=0)
{
cCharacter=pacPromptBuffer[uiNewLength-1];
if(
cCharacter!=‘/r‘ && cCharacter!=‘/n‘
)
break;
uiNewLength--;
}
pacPromptBuffer[uiNewLength]=0;
fReturnStatus=true;
break;
}
if(
fReturnStatus==false
)
pacPromptBuffer[0]=0;
return(fReturnStatus);
}
//输入控制台的回收清理
void Global_Console_CancelReadRow(void)
{
if(
hGlobal_PromptConsoleHandle!=(HANDLE)0
)
{
SetConsoleMode( hGlobal_PromptConsoleHandle,
ulGlobal_PromptConsoleMode);
CloseHandle(hGlobal_PromptConsoleHandle);
hGlobal_PromptConsoleHandle=(HANDLE)0;
}
}
static LRESULT APIENTRY SkypeAPITest_Windows_WindowProc(
HWND
hWindow, UINT uiMessage, WPARAM uiParam, LPARAM ulParam)
{
LRESULT
lReturnCode;
bool
fIssueDefProc;
lReturnCode=0;
fIssueDefProc=false;
switch(uiMessage)
{
case
WM_DESTROY:
hInit_MainWindowHandle=NULL;
PostQuitMessage(0);
break;
case
WM_COPYDATA:
//Skype与第三方程序的消息传送使用WM_COPYDATA
//当Skype通过WM_COPYDATA向所有已连接的第三方程序发送消息时,会把Skype的窗口句柄放到uiParam中
if(
hGlobal_SkypeAPIWindowHandle==(HWND)uiParam
)
{
PCOPYDATASTRUCT
poCopyData=(PCOPYDATASTRUCT)ulParam;
printf( "Message from
Skype(%u): %.*s/n", poCopyData->dwData, poCopyData->cbData,
poCopyData->lpData);
lReturnCode=1;
}
break;
default:
//如果消息类型是uiGlobal_MsgID_SkypeControlAPIAttach,则表示连接相关的
if(
uiMessage==uiGlobal_MsgID_SkypeControlAPIAttach
)
{
switch(ulParam)
{
case
SKYPECONTROLAPI_ATTACH_SUCCESS:
printf("!!!
Connected; to terminate issue
#disconnect/n");
hGlobal_SkypeAPIWindowHandle=(HWND)uiParam;
break;
case
SKYPECONTROLAPI_ATTACH_PENDING_AUTHORIZATION:
printf("!!!
Pending
authorization/n");
break;
case
SKYPECONTROLAPI_ATTACH_REFUSED:
printf("!!!
Connection
refused/n");
break;
case
SKYPECONTROLAPI_ATTACH_NOT_AVAILABLE:
printf("!!!
Skype API not
available/n");
break;
case
SKYPECONTROLAPI_ATTACH_API_AVAILABLE:
printf("!!! Try
connect now (API available); issue
#connect/n");
break;
}
lReturnCode=1;
break;
}
fIssueDefProc=true;
break;
}
if(
fIssueDefProc )
lReturnCode=DefWindowProc( hWindow, uiMessage,
uiParam, ulParam);
if( fGlobal_DumpWindowsMessages
)
{
printf( "WindowProc: hWindow=0x%08X,
MainWindow=0x%08X, Message=%5u, WParam=0x%08X, LParam=0x%08X;
Return=%ld%s/n",
hWindow, hInit_MainWindowHandle,
uiMessage, uiParam, ulParam, lReturnCode, fIssueDefProc? "
(default)":"");
}
return(lReturnCode);
}
bool Initialize_CreateWindowClass(void)
{
unsigned char
*paucUUIDString;
RPC_STATUS lUUIDResult;
bool
fReturnStatus;
UUID
oUUID;
fReturnStatus=false;
lUUIDResult=UuidCreate(&oUUID);
hInit_ProcessHandle=(HINSTANCE)OpenProcess(
PROCESS_DUP_HANDLE, FALSE, GetCurrentProcessId());
if(
hInit_ProcessHandle!=NULL && (lUUIDResult==RPC_S_OK ||
lUUIDResult==RPC_S_UUID_LOCAL_ONLY) )
{
if(
UuidToString( &oUUID, &paucUUIDString)==RPC_S_OK
)
{
WNDCLASS
oWindowClass;
//生成窗口类名(含UUID)
strcpy(
acInit_WindowClassName, "Skype-API-Test-");
strcat(
acInit_WindowClassName, (char
*)paucUUIDString);
oWindowClass.style=CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
//指定窗口的消息回调函数是SkypeAPITest_Windows_WindowProc
oWindowClass.lpfnWndProc=(WNDPROC)&SkypeAPITest_Windows_WindowProc;
oWindowClass.cbClsExtra=0;
oWindowClass.cbWndExtra=0;
oWindowClass.hInstance=hInit_ProcessHandle;
oWindowClass.hIcon=NULL;
oWindowClass.hCursor=NULL;
oWindowClass.hbrBackground=NULL;
oWindowClass.lpszMenuName=NULL;
oWindowClass.lpszClassName=acInit_WindowClassName;
//注册窗口类
if(
RegisterClass(&oWindowClass)!=0
)
fReturnStatus=true;
RpcStringFree(&paucUUIDString);
}
}
if(
fReturnStatus==false
)
CloseHandle(hInit_ProcessHandle),hInit_ProcessHandle=NULL;
return(fReturnStatus);
}
void
DeInitialize_DestroyWindowClass(void)
{
//注销窗口类
UnregisterClass(
acInit_WindowClassName,
hInit_ProcessHandle);
CloseHandle(hInit_ProcessHandle),hInit_ProcessHandle=NULL;
}
bool
Initialize_CreateMainWindow(void)
{
//创建窗口,并把句柄保存到hInit_MainWindowHandle
hInit_MainWindowHandle=CreateWindowEx(
WS_EX_APPWINDOW|WS_EX_WINDOWEDGE,
acInit_WindowClassName, "",
WS_BORDER|WS_SYSMENU|WS_MINIMIZEBOX,
CW_USEDEFAULT,
CW_USEDEFAULT, 128, 128, NULL, 0, hInit_ProcessHandle,
0);
return(hInit_MainWindowHandle!=NULL? true:false);
}
void DeInitialize_DestroyMainWindow(void)
{
//销毁窗口
if(
hInit_MainWindowHandle!=NULL
)
DestroyWindow(hInit_MainWindowHandle),hInit_MainWindowHandle=NULL;
}
void Global_MessageLoop(void)
{
//消息处理循环
MSG
oMessage;
while(GetMessage( &oMessage, 0, 0,
0)!=FALSE)
{
TranslateMessage(&oMessage);
DispatchMessage(&oMessage);
}
}
//这是一个线程函数,主要用于接收用户的输入指令并执行
void __cdecl Global_InputProcessingThread(void
*)
{
static char acInputRow[1024];
bool
fProcessed;
//线程开始执行时默认去连接Skype,发送请求访问Skype
API的消息。由于一开始我们不知道SKype的API窗口句柄值,只能
//通过HWND_BROADCAST广播这个消息给所有系统中在前台的窗口,如果Skype窗口存在,则自然也会收到。
if(
SendMessage( HWND_BROADCAST, uiGlobal_MsgID_SkypeControlAPIDiscover,
(WPARAM)hInit_MainWindowHandle, 0)!=0
)
{
//接受用户输入指令
while(Global_Console_ReadRow(
acInputRow,
sizeof(acInputRow)-1))
{
//退出程序
if(
stricmp( acInputRow, "#quit")==0 ||
stricmp(
acInputRow, "#exit")==0
)
break;
fProcessed=false;
//开启显示消息详细信息
if(
stricmp( acInputRow, "#dbgon")==0
)
{
printf(
"SkypeControlAPIAttach=%u, SkypeControlAPIDiscover=%u,
hInit_MainWindowHandle=0x%08lX/n",
uiGlobal_MsgID_SkypeControlAPIAttach,
uiGlobal_MsgID_SkypeControlAPIDiscover,
hInit_MainWindowHandle);
fGlobal_DumpWindowsMessages=true,fProcessed=true;
}
//关闭显示消息详细信息
if(
stricmp( acInputRow, "#dbgoff")==0
)
fGlobal_DumpWindowsMessages=false,fProcessed=true;
//请求访问Skype
API
if( stricmp( acInputRow, "#connect")==0
)
{
SendMessage( HWND_BROADCAST,
uiGlobal_MsgID_SkypeControlAPIDiscover, (WPARAM)hInit_MainWindowHandle,
0);
fProcessed=true;
}
//停止访问Skype
API。由代码可以看出,这是一个假断开,
//因为只是设置hGlobal_SkypeAPIWindowHandle为空,从而不再处理和打印这个窗口发送的WM_COPYDATA消息
if(
stricmp( acInputRow, "#disconnect")==0
)
{
hGlobal_SkypeAPIWindowHandle=NULL;
printf("!!!
Disconnected/n");
fProcessed=true;
}
//其它用户输入内容,则直接作为Skype
API命令发送给Skype处理
if( fProcessed==false &&
hGlobal_SkypeAPIWindowHandle!=NULL
)
{
COPYDATASTRUCT
oCopyData;
// send
command to
skype
oCopyData.dwData=0;
oCopyData.lpData=acInputRow;
oCopyData.cbData=strlen(acInputRow)+1;
if(
oCopyData.cbData!=1
)
{
if( SendMessage(
hGlobal_SkypeAPIWindowHandle, WM_COPYDATA, (WPARAM)hInit_MainWindowHandle,
(LPARAM)&oCopyData)==FALSE
)
{
hGlobal_SkypeAPIWindowHandle=NULL;
printf("!!!
Disconnected/n");
}
}
}
}
}
PostMessage(
hInit_MainWindowHandle, WM_CLOSE, 0,
0);
SetEvent(hGlobal_ThreadShutdownEvent);
fGlobal_ThreadRunning=false;
}
void
main(void)
{
//获取到Skype注册到系统的两类消息ID
uiGlobal_MsgID_SkypeControlAPIAttach=RegisterWindowMessage("SkypeControlAPIAttach");
uiGlobal_MsgID_SkypeControlAPIDiscover=RegisterWindowMessage("SkypeControlAPIDiscover");
if(
uiGlobal_MsgID_SkypeControlAPIAttach!=0 &&
uiGlobal_MsgID_SkypeControlAPIDiscover!=0
)
{
//注册窗口类并创建窗口
if(
Initialize_CreateWindowClass() )
{
if(
Initialize_CreateMainWindow()
)
{
hGlobal_ThreadShutdownEvent=CreateEvent(
NULL, TRUE, FALSE, NULL);
if(
hGlobal_ThreadShutdownEvent!=NULL
)
{
//单独启动一个进程接收用户输入指令并处理
if(
_beginthread( &Global_InputProcessingThread, 64*1024, NULL)!=(unsigned
long)-1
)
{
//main主线程在此循环处理窗口消息
Global_MessageLoop();
//垃圾回收和清理工作
Global_Console_CancelReadRow();
WaitForSingleObject(
hGlobal_ThreadShutdownEvent,
INFINITE);
}
CloseHandle(hGlobal_ThreadShutdownEvent);
}
DeInitialize_DestroyMainWindow();
}
DeInitialize_DestroyWindowClass();
}
}
}