原文:你也可以玩转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. 国内PaaS概述和EEPlat定位

    2014国内云计算产业进入快速发展阶段.热火多年来,所以云计算的云计算产业迅速进入栈桥的应用.IaaS.PaaS.SaaS各大厂商具有较强的市场布局,所以,云计算应用在这三个层次的访问,以实际使用阶段 ...

  2. poj 3375 Network Connection

    今天在叉姐的群里找点题目做,这题目还是很好的:提意思如下 [有M个可以提供计算机网络的端口和N台计算机(计算机数量少于端口数),每个端口和计算机有一个坐标(一维的)!其中端口与计算机链接的距离 |x  ...

  3. maven配置文件里改动默认jre

    方法一:打开%maven_home%\conf\setting.xml,仅仅会在新建项目时自己主动使用1.6的导入项目不会 在<profiles>标签内加入�例如以下配置: <pro ...

  4. 【转】c++继承:公有、私有、保护

    原文链接:http://www.cnblogs.com/qlwy/archive/2011/08/25/2153584.html 公有继承(public).私有继承(private).保护继承(pro ...

  5. 4.锁定--Java的LockSupport.park()实现分析

    LockSupport类是Java6(JSR166-JUC)引入的一个类,提供了主要的线程同步原语. LockSupport实际上是调用了Unsafe类里的函数.归结到Unsafe里,仅仅有两个函数: ...

  6. robot framework 使用三:他们主动浏览器的兼容性

    robot framework 浏览器兼容性测试 上图中黄色圈的地方默认什么都不写,是firefox浏览器.写上ie就是ie浏览器了 firefox最新版本号即可,ie须要设置: 1. IE选项设置的 ...

  7. SoC嵌入式软件架构设计II:没有MMU的CPU虚拟内存管理的设计和实现方法

    大多数的程序代码是必要的时,它可以被加载到内存中运行.手术后,可直接丢弃或覆盖其它代码. 我们PC然在同一时间大量的应用,地址空间差点儿能够整个线性地址空间(除了部分留给操作系统或者预留它用).能够觉 ...

  8. hdu 2243 考研绝望——复杂的文字(AC自己主动机+矩阵高速功率)

    pid=2243" target="_blank" style="">题目链接:hdu 2243 考研路茫茫--单词情结 题目大意:略. 解题思 ...

  9. Python学习笔记23:Django构建一个简单的博客网站(一个)

    在说如何下载和安装Django,本节将重点讨论如何使用Django站点. 一 新建project 命令:django-admin startproject mysite # 有的须要输入:django ...

  10. Effective C++ -- 继承和面向对象设计

    32.确保你的public继承了模is-a关系 public继承意味着is-a关系(里氏替换原则),一切适用于基类也适用于派生类. 矩形继承正方形问题: 可实施与矩形的操作无法实施与正方形 在编程领域 ...