原文:你也可以玩转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();
  }
 }
}

你也可以玩转Skype -- 基于Skype API开发外壳程序入门的更多相关文章

  1. windows下使用pycharm开发基于ansible api的python程序

    Window下python安装ansible,基于ansible api开发python程序 在windows下使用pycharm开发基于ansible api的python程序时,发现ansible ...

  2. 基于 Aliexpress API 的小程序 : 批量 Copy 产品到不同的店铺

    第一个基于 Aliexpress API 的小程序 : 批量 Copy 产品到不同的店铺 还没来得及用 API 重写软件, 先写个小程序来缓解一下手工压力: 批量Copy 产品到不同的店铺. 开网店 ...

  3. 【核心API开发】Spark入门教程[3]

    本教程源于2016年3月出版书籍<Spark原理.机制及应用> ,在此以知识共享为初衷公开部分内容,如有兴趣,请支持正版书籍. Spark综合了前人分布式数据处理架构和语言的优缺点,使用简 ...

  4. Skype发布视频API

    原文:Skype发布视频API 相信很多人对Skype多少都应该有一些了解,如果以前没有使用过它的服务的话,也应该在最近的新闻中听说过它的大名.因为,它和我们每天都在接触的公司--Mircrosoft ...

  5. Angular SPA基于Ocelot API网关与IdentityServer4的身份认证与授权(一)

    好吧,这个题目我也想了很久,不知道如何用最简单的几个字来概括这篇文章,原本打算取名<Angular单页面应用基于Ocelot API网关与IdentityServer4+ASP.NET Iden ...

  6. Angular SPA基于Ocelot API网关与IdentityServer4的身份认证与授权(三)

    在前面两篇文章中,我介绍了基于IdentityServer4的一个Identity Service的实现,并且实现了一个Weather API和基于Ocelot的API网关,然后实现了通过Ocelot ...

  7. Atitit 游戏的通常流程 attilax 总结 基于cocos2d api

    Atitit 游戏的通常流程 attilax 总结 基于cocos2d api 加载音效1 加载页面1 添加精灵1 设置随机位置2 移动2 垃圾gc2 点击evt2 爆炸效果3 定时生成精灵3 加载音 ...

  8. 基于 ArcGIS Silverlight API开发的WebGIS应用程序的部署

    部署流程概述 在微软的iis服务器上部署基于ArcGIS  Silverlight API的应用程序,主要包括以下几个步骤: 1)(可选)部署GIS服务 如果需要将GIS服务也部署在Web服务器上,则 ...

  9. 基于MFC与第三方类CWebPage的百度地图API开发范例

    在进行百度地图API开发之前你需要到http://developer.baidu.com/map申请密匙 密匙申请之后就可以进行百度地图API的开发了. 下面我们以在visual c++6.0里进行地 ...

随机推荐

  1. FTP文件操作之删除文件

    上面我已经介绍了利用ftp进行上传跟下载,接下来跟大家分享的是删除ftp服务器上的文件的部分.有了上传,有了下载,那么删除自然也是不能少的. 删除相对于上传跟下载更简单一些,它不需要进行文件的传输,只 ...

  2. 经excel要将数据库(ORACLE)要插入数据

    大家都知道PL/SQL可以excel数据复制.我们也可以通过相同excel将数据插入到数据库. 下面我们就来简单的样品,并与主题演示 首先,我们创建了一个表test CREATE TABLE test ...

  3. CF417D--- Cunning Gena(序列+像缩进dp)

    A boy named Gena really wants to get to the "Russian Code Cup" finals, or at least get a t ...

  4. Highcharts中文教程

    http://www.hcharts.cn/docs/index.php?doc=start

  5. Android Studio 1.0 (稳定版) 完全攻略

    这篇博文中主要从以下几点进行叙述: 1.Android Studio安装与使用 2.Android Studio特性 3.Android Studio优点 Android Studio 安装与使用 A ...

  6. Atitit.Hibernate于Criteria 使用汇总and 关系查询 and 按照子对象查询 o9o

    Atitit.Hibernate于Criteria 使用总结and 关联查询 and 依照子对象查询 o9o 1. Criteria,,Criterion ,, 1 <2. 基本的对象黑头配置磊 ...

  7. 信息二战炸弹:中国到美国咨询公司Say no

    疯抢/文 在禁止Windows8操作系统參与政府採购,以及在中国销售的全部外国IT产品和服务都须通过新的安全审查之后,英国<金融时报>今天报料中国已禁止国企使用美国咨询公司服务. 这则消息 ...

  8. Idea开发环境

    Idea开发环境中搭建Maven 1.配置Maven的环境变量 a.首先我们去maven官网下载Maven程序,解压到安装目录,如图所示: b.配置M2_HOME的环境变量,然后将该变量添加到Path ...

  9. codeforces Round #260(div2) D解决报告

    D. A Lot of Games time limit per test 1 second memory limit per test 256 megabytes input standard in ...

  10. javascript 比量str今天的日期是,参数diff

    function isToday(str) { var d = new Date(str.replace(/-/g, "/")); var todaysDate = new Dat ...