返回
计算机世界2000年第21期

用NetMeeting开发网络会议软件

北京航空航天大学图像中心 赵 宇

一、 NetMeeting概述

  Microsoft NetMeeting是微软家族中的一个网络通信服务产品。有了 NetMeeting,您可以参加网络会议、进行协同工作以及通过Internet或企业Intranet共享信息。

  NetMeeting的基本原理是:在两个IP之间建立起语音、视频和数字会议,获得一个丰富多彩的实时协作环境。双方或者多方可以交换文件,在白板上交流思想,进行讨论或者共享应用程序和桌面。

  NetMeeting支持ITU的H.323标准和T.120标准,以及IETF 的LDAP目录服务标准,有关这些标准的细节请参考相关资料。在Windows 98第一版中捆绑了NetMeeting 2.0,在Windows 98第二版和Windows 2000中捆绑了NetMeeting 3.0。 NetMeeting本身是基于Microsoft COM技术设计的,它提供了一整套接口函数,使我们可以在NetMeeting的基础上进行二次开发。有了NetMeeting,我们可以跳过网络通信的底层技术细节,集中精力在软件的功能设计上,开发出满足自己需求的网络通信产品。微软的很多产品都具有二次开发的能力,这要归功于COM的威力,它可以在二进制级进行代码重用,为现代软件的开发带来了极大的革新。

二、 NetMeeting SDK介绍

  如果你使用的是中文版Windows 98或者Windows 2000,建议您到微软的主页上去下载英文版的NetMeeting 3.0,因为中文版的NetMeeting 在传输中文名字的文件时会产生乱码(这个现象好奇怪)。NetMeeting的网址是 www.microsoft.com/netmeeting,在这里您还可以下载NetMeeting的SDK包,其中包括详细的文档和一些示例程序。

  图1描述了NetMeeting的软件结构。从图中可以看到,我们只要通过调用NetMeeting的COM API函数,就可以管理一个网络会议,完成所有 NetMeeting的功能,而大量的底层技术细节都由NetMeeting自己处理了。

  NetMeeting SDK包括一个ActiveX控件和一组COM对象。其中的ActiveX控件只能实现很简单的功能,可以在Web页面上使用。如果想开发比较复杂的网络会议软件,还需要直接调用其COM API。NetMeeting的COM模型如图2所示。


  每个COM对象都有不少接口函数,几乎每个对象都有一个相应的Notification对象,我们可以通过它在运行时刻获得COM对象的事件。只要按照一定的规范使用这些COM对象,就可以创建一个与NetMeeting完全一样的网络会议软件。

三、 对NetMeeting COM API的封装

  用于COM技术比较复杂,直接调用COM API还是非常麻烦的,所以我在NetMeeting COM API的基础上用C++语言编写了一个类TConf。它把一些COM调用的细节封装起来,使得创建一个网络会议变得更加简单、方便。

  TConf是一个包括语音通道、数据传输和文件传输的网络会议类,视频和应用程序共享并没有包含在内。下面是TConf的头文件,其中没有包括 Private成员:

    class TConf
    {
    public:
    TConf(HWND);
    ~TConf();

    HRESULT 	Initialize();
    HRESULT 	Uninitialize();
    HRESULT 	Call(BSTR);
    HRESULT 	HangUp();
    BOOL  	  	InConnection();
    HRESULT 	RejectCall();
    HRESULT 	AcceptCall();
    HRESULT 	HookDataChannel();
    HRESULT 	HookFtChannel();
    HRESULT 	SendData(ULONG uSize, BYTE *pvBuffer);

    virtual HRESULT CallCreated(INmCall *pCall);
    virtual HRESULT ConferenceActive();
    virtual HRESULT ConferenceCreated(INmConference *);
    virtual HRESULT ConferenceIdle();
    virtual HRESULT MemberAdded(INmMember *pMember);
    virtual HRESULT MemberRemoved(INmMember *pMember);
    virtual HRESULT MemberUpdated(INmMember *pMember);
virtual HRESULT DataSent(INmMember *pMember, 
ULONG uSize, LPBYTE pb);
virtual HRESULT DataReceived(INmMember
 *pMember, ULONG uSize, LPBYTE pb);
    virtual HRESULT CallRing();
    virtual HRESULT CallRejected();
    virtual HRESULT CallAccepted();
    virtual HRESULT FtUpdate(CONFN uNotify, INmFt * pFt);
    HRESULT FtSendFile(BSTR);
    HRESULT SetReceiveFileDir(BSTR bstrDir);
    HRESULT GetReceiveFileDir(BSTR *pbstrDir);
    };

  使用TConf时,应先把必要的文件包含入C++工程,再从TConf派生子类,重新定义其中的虚函数,以完成定制的功能。TConf的代码比较长,这里只简要介绍一下:

  1. 在类构造函数中创建所有Notification对象,并保存一个所属窗口的句柄,以便将来向该窗口发送消息。如果重新定义虚函数,也可以不采用这种消息循环的机制。
  2. 在Initialize函数中创建Conference Manager对象,并连接其Notification对象。当Manager 对象创建成功后,创建Conference对象,并创建Data Channel、Audio Channel和File Transfer Channel。
  3. Call函数可以用计算机名字、IP地址呼叫对方。
  4. HangUp函数终止当前活动的网络会议。
  5. SendData函数用于发送小块的数据。
  6. 虚函数DataReceived在有数据块到达时被调用,可以重新定义它来接收、显示数据。
  7. 虚函数CallRing在有呼叫到达时被调用,可以在这里确认或拒绝呼叫。
  8. FtSendFile用于发送文件,它接收一个包含全路径的文件名。
  9. 虚函数FtUpdate在文件发送和接收过程中被调用,可以在这里监视文件发送接收的过程,如显示一个进程条。
  10. 语音通道从网络会议建立就一直打开,该类中没有控制它的函数。
  11. 该类是用纯C++编写的,可以在Visual C++和Borland C++ Builder中使用。

四、 开发自己的网络会议软件

  看到上面TConf的定义后,大家可能就跃跃欲试要编写自己的网络会议软件了,下面用一个C++ Builder的例子来演示TConf的用法。

  首先创建一个窗体,其中包括一个Edit1组件,用于输入呼叫的地址;Memo1组件用于显示双方Chart的内容;Edit2组件用于输入自己的发言,按“回车”键后会发送其内容;Button2用于选择一个文件并发送给对方。TConf的派生类TMyConf重新定义了DataSent函数和DataReceived函数,用于在Memo1中显示发送的数据。语音通道一直打开,双方可以通过麦克风进行交谈。该示例程序的界面如图3所示。

    TForm1 *Form1;
    class TMyConf : public TConf
    {
      public:
      TMyConf(HWND);
      ~TMyConf();
     virtual HRESULT DataSent(INmMember 
    *pMember, ULONG uSize, LPBYTE pb);
      virtual HRESULT DataReceived(INmMember
    *pMember, ULONG uSize, LPBYTE pb);
};

HRESULT TMyConf::DataSent(INmMember 
*pMember, ULONG uSize, LPBYTE pb)
    {
    return DataReceived(m_pINmMember,uSize,pb);
    }

HRESULT TMyConf::DataReceived(INmMember
 *pMember, ULONG uSize, LPBYTE pb)
    {
	WideString User;
	if (pMember)
	{
		pMember->GetName(&User);
	}
	else
	{
		User=“";
	}
	Form1->Memo1->Lines->Add(AnsiString
   (User)+“:"+AnsiString((LPTSTR)pb));
    return S_OK;
}

    void __fastcall TForm1::FormCreate(TObject *Sender)
    {
	MyConf = new TMyConf(Handle);
	MyConf->Initialize();
    }

void __fastcall TForm1::FormClose(TObject 
*Sender, TCloseAction &Action)
    {
        delete MyConf;    
    }

    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
        MyConf->Call((WideString)Edit1->Text);    
    }

    void __fastcall TForm1::Button2Click(TObject *Sender)
    {
        if (OpenDialog1->Execute())
        {
        MyConf->FtSendFile((WideString)OpenDialog1->FileName);
        }
    }

void __fastcall TForm1::Edit2KeyPress
(TObject *Sender, char &Key)
    {
        if (Key==13)
        {
       MyConf->SendData(Edit2->Text.Length() + 1, 
       (BYTE*)Edit2->Text.c_str());
        }
    }

  怎么样,只短短几行代码,一个网络会议就有个初步的模样了。由于NetMeeting是在两个IP地址之间的通信连接,所以进行网络会议时一定要使用TCP/IP协议。我们可以使用局域网连接,或者通过RAS进行连接。如果要通过因特网进行连接,则需要知道网络会议各方的IP地址。在这种情况下,可以使用Microsoft ILS 服务器,或者通过FTP、Email等方式把自己的IP地址告知对方。