首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

3.7  使用Windows剪贴板通信

Windows剪贴板是一种比较简单同时也是开销比较小的IPC(进程间通信)机制。Windows系统支持剪贴板IPC的基本机制是由系统预留的一块全局共享内存,用来暂存各个进程间进行交换的数据。提供数据的进程创建一个全局内存块,并将要传送的数据移到或复制到该内存块;而接受数据的进程(也可以是提供数据的进程本身)获取此内存块的句柄,并完成对该内存块数据的读取。

在Windows系统和其他工具软件中有自带的使用剪贴板的命令。例如,在Microsoft Word 中,组合键Ctrl+C用于文字的复制、组合键Ctrl+X用于对文字的剪切、组合键Ctrl+V用于对文字的粘贴。使用这些命令可以很方便地对所选择字符串进行复制和移动。然而,这里关心的是如何在编写应用程序时使用剪贴板实现进程间的通信。

3.7.1  使用剪贴板实现进程间通信的方法

可以使用剪贴板函数实现进程间的数据传输。常用的剪贴板函数有:

// 打开剪贴板

BOOL OpenClipboard();

// 关闭剪贴板

BOOL CloseClipboard();

                

// 清空剪贴板,并将所有权分配给打开剪贴板的进程

BOOL EmptyClipboard( ); 

// 按指定数据格式放置剪贴板数据,用之前必须使用OpenClipboard函数

HANDLE SetClipboardData(UINT uFormat,  HANDLE hMem);

// 检测是否已经包含了所需要的数据

BOOL IsClipboardFormatAvailable(UINT uFormat); 

// 获取指定剪贴板数据

HANDLE GetClipboardData( UINT uFormat);    

其中uFormat 为剪贴板格式,见MSDN(微软开发者网络)描述,hMem为所申请的内存控制句柄。

文本剪贴板和位图剪贴板是比较常用的。其中,文本剪贴板是包含具有格式CF_TEXT的字符串的剪贴板,是最经常使用的剪贴板之一。在文本剪贴板中传递的数据是不带任何格式信息的ASCII字符。若要将文本传送到剪贴板,可以先分配一个可移动全局内存块,然后将要复制的文本内容写入到此内存区域,最后调用剪贴板函数如OpenClipboard()、SetClipboardData()将数据放置到剪贴板。从剪贴板获取文本的过程与之类似,首先用OpenClipboard()函数打开剪贴板并获取剪贴板的数据句柄,如果数据存在就复制其数据到程序变量。由于GetClipboardData()获取的数据句柄属于剪贴板,因此用户程序必须在调用CloseClipboard()函数之前使用它。

大多数应用程序对图形数据采取是位图剪贴板数据格式。位图剪贴板的使用与文本剪贴板的使用类似,只是数据格式要指明为CF_BITMAP,而且在使用SetClipboardData()或GetClipboardData()函数时交给剪贴板或从剪贴板返回的是设备相关位图句柄。

3.7.2  使用剪贴板实现进程间通信的实例

剪贴板中可以存放许多类型的数据,其中包括标准文本格式、位图格式、RTF格式等,由于类型比较多,这里只给出经常使用的文本格式的实例,其他的数据类型的操作方法基本类似。同样,用 VC++ 编写两个对话框应用程序。为了方便,仍然借用前面所使用的对话框类CDataSendDlg和CDataRecvDlg。

为了把文本放置到剪贴板上,在CDataSendDlg中,用MFC ClassWizard工具或者用手工的方法增加函数void CDataSendDlg::OnSendClipboard(),其源代码如下:

void CDataSendDlg::OnSendClipboard()

{

    UpdateData();                       // 更新数据

    CString strData=m_strClipBoard;     // 获得数据

    // 打开系统剪贴板

    if (!OpenClipboard()) return;

    // 使用之前,清空系统剪贴板

    EmptyClipboard();

    // 分配一内存,大小等于要复制的字符串的大小,返回到内存控制句柄

    HGLOBAL hClipboardData;

    hClipboardData = GlobalAlloc(GMEM_DDESHARE, strData.GetLength()+1);

   

    // 内存控制句柄加锁,返回值为指向那内存控制句柄所在的特定数据格式的指针

    char * pchData;

    pchData = (char*)GlobalLock(hClipboardData);

   

    // 将本地变量的值赋给全局内存

    strcpy(pchData, LPCSTR(strData));

   

    // 给加锁的全局内存控制句柄解锁

    GlobalUnlock(hClipboardData);

   

    // 通过全局内存句柄将要复制的数据放到剪贴板上

    SetClipboardData(CF_TEXT,hClipboardData);

   

    // 使用完后关闭剪贴板

    CloseClipboard();

}

在数据接收程序的CDataRecvDlg类中,用与前面所用的同样的方法,增加从剪贴板上获取文本的函数,即void CDataRecvDlg::OnRecvClipboard(),其源代码如下:

void CDataRecvDlg::OnRecvClipboard()

{

    // 打开系统剪贴板

    if (!OpenClipboard()) return;

    // 判断剪贴板上的数据是否是指定的数据格式

     if (IsClipboardFormatAvailable(CF_TEXT)|| IsClipboardFormatAvaila- ble(CF_OEMTEXT))

    {

        // 从剪贴板上获得数据

        HANDLE hClipboardData = GetClipboardData(CF_TEXT);

   

        // 通过给内存句柄加锁,获得指向指定格式数据的指针

        char *pchData = (char*)GlobalLock(hClipboardData);

   

        // 本地变量获得数据

        m_strClipBoard = pchData;

   

        // 给内存句柄解锁

        GlobalUnlock(hClipboardData);

    }

    else

    {

        AfxMessageBox("There is no text (ANSI) data on the Clipboard.");

    }

   

    // 使用完后关闭剪贴板

    CloseClipboard();

    // 更新数据

    UpdateData(FALSE);

}

使用剪贴板通信的方法与使用发送消息通信的方法所经历的过程是不一样的,前者是把所共享的数据先放在剪贴板上,然后由接收数据的程序去获取,而后者是直接把共享数据发送到接收数据的程序。因此,在使用剪贴板通信方法时,接收数据程序的对话框上需要增加一个获取数据的命令按钮,而使用消息通信方法则不需要这个按钮。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论