原文:你也可以玩转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. Linux rpm 命令参数使用详解[介绍和应用](转)

    RPM是RedHat Package Manager(RedHat软件包管理工具)类似Windows里面的“添加/删除程序” rpm 执行安装包二进制包(Binary)以及源代码包(Source)两种 ...

  2. lsb_release: command not found 解决方法(转)

    问题:通过lsb_release -a 是查看linux系统版本时报错,具体的解决办法如下: [root@localhost ~]# lsb_release -a-bash: lsb_release: ...

  3. C# HttpClient Cookie验证解决方法

    自实现的cookie 验证,远程取值的例子 以下代码配合HttpClient使用可以实现跨域(cookie的读写) //验证 复制代码 代码如下: HttpClient httpClient = ne ...

  4. MS Open Tech 技术团队构建可靠的Windows版Redis

    可靠的Windows版Redis 副标题: 评论更精彩,教你怎么解决64位Windows版Redis狂占C盘的问题. MS Open Tech 技术团队最近花了很多时间来测试最新构建的Windows版 ...

  5. 概率统计(DP)

    问题叙述性说明 生成n个月∈[a,b]随机整数.并且将它们输出到x概率. 输入格式 输入线跟四个整数n.a,b,x,用空格分隔. 输出格式 输出一行包括一个小数位和为x的概率.小数点后保留四位小数 例 ...

  6. linux软与硬接线连接

    1.Linux链接概念 Linux链接分两种.一种被称为硬链接(Hard Link),还有一种被称为符号链接(Symbolic Link).默认情况下,ln命令产生硬链接. [硬连接] 硬连接指通过索 ...

  7. WEB功能测试说明

    站点功能測试就是对产品的各功能进行验证.依据功能測试用例,逐项測试.检查产品是否达到用户 要求的功能.经常使用的測试方法例如以下: 1.页面链接检查: 每个链接是否都有相应的页面.而且页面之间切换工具 ...

  8. 7.oracle学习门户系列七---网络管理和配置

    oracle学习门户系列七 网络管理和配置 们学习了模式和用户.包含模式定义以及模式的作用. 这篇我么来看下ORACLE数据库中的网络管理和配置.只是这篇好像和上篇没有继承啊.这怎么看? Ok,事实上 ...

  9. UVA10375 Choose and divide 质因数分解

    质因数分解: Choose and divide Time Limit: 3000MS   Memory Limit: Unknown   64bit IO Format: %lld & %l ...

  10. atitit.基于组件的事件为基础的编程模型--服务器端控件(1)---------服务器端控件和标签之间的关系

    atitit.基于组件的事件为基础的编程模型--服务器端控件(1)---------服务器端控件和标签之间的关系 1. server控件是要server了解了标签.种类型的server控件: 1 1. ...