首页 | 互联网 | IT动态 | 网络设备 | 服务器 | IDC | 安全 | Cisco | Windows | Linux | Java | .Net | Oracle | CIW | 华为 | 专题
IT技术 | 网页设计 | 平面设计 | 电子书下载 | 教学视频 | 方案 | 数字网校 | 直播室 | 虚拟考场 | 面授培训 | 搜索 | 博客 | 沙龙 | 论坛
首页 | JAVA | C# | VB | VB.NET | C/C++ | delphi | 工程管理 | 其他语言 | 论坛
免费注册一站通帐号,参与直播、论坛、下载、博客、网摘、评论,展现我的风采!
您现在的位置: 中国IT实验室 >> 桌面开发 >> VC >> 文章正文
VC下动态数据交换技术之DDE数据传送
来源:中国IT实验室整理  时间:2007-4-4

  冷数据链路的建立
  在建立DDE会话后,客户可以向服务窗口邮寄WM_DDE_REQUEST消息来请求指定的数据项。消息参数lParam的高字为指定数据项的原子标识值,低字为所要求的数据格式标识值。如果服务器没有找到匹配的数据项,将向客户邮寄一条否定应答消息WM_DDE_ACK;如果含有此数据项,则邮寄WM_DDE_DATA消息以响应客户的数据项请求,其消息参数lParam的高字为数据项原子标识值,低字为存放此数据项的全局共享内存块的DDEDATA结构句柄。
  
  对于上述方式,客户每需要一个数据项,都要向服务器窗口发送一次WM_DDE_REQUSET消息去请求数据项,由此可以建立冷数据链路。图1给出了服务器在能提供数据项和无法提供数据项的情况下所进行的消息流程:
  

  
图1 服务器在不同情况下对客户的应答

  
  客户通过下面的程序段代码完成对WM_DDE_REQUEST消息的邮寄,其中主要指定了数据项原子标识和数据格式:
  
  HWND hwndClient = GetSafeHwnd(); // 获取服务器所在应用程序的窗口句柄
  ATOM atomItem = GlobalAddAtom("Item A"); // 获取原子标识值
  if (atomItem != 0) // 向服务器发出数据请求消息
  ::PostMessage(m_hwndServer, WM_DDE_REQUEST, (WPARAM)hwndClient, (LPARAM)MAKELONG(CF_TEXT, atomItem));
  
  DDE服务器的准备工作与消息处理
  DDE服务器窗口在创建之初就要首先进行一些准备工作,主要是对DDEDATA格式化的全局共享内存块的分配与设置,并指定所使用的格式字段和数据项内容,以便在接收到客户发来的数据项请求消息后,能将此共享内存块的句柄随应答消息传送给客户:
  
  m_hwndServer = hwndServer; // 保存创建的DDE服务窗口
  CString sDataItem = "HELLO WORLD!"; // 服务器数据项内容
  // 分配DDEDATA格式化的全局共享内存块
  m_hDDEData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (LONG)sizeof(DDEDATA) + sDataItem.GetLength() + 2);
  // 锁定内存块地址
  DDEDATA * lpDDEData = (DDEDATA*)GlobalLock(m_hDDEData);
  lpDDEData->cfFormat = CF_TEXT; // 设置格式字段
  ::strcpy((LPSTR)lpDDEData->Value, sDataItem); // 填充数据项内容
  ::strcat((LPSTR)lpDDEData->Value, "\r\n"); // 添加终结字符
  GlobalUnlock(m_hDDEData); // 解锁内存块
  
  在服务器进程对WM_DDE_REQUEST消息的响应函数中,首先从消息参数lParam中提取出请求的数据项原子标识值及数据格式。如果请求的数据格式与服务器提供的数据项格式一致,则进行枚举匹配,直至找到待请求的数据项。由于DDEDATA格式化的内存块句柄和数据项原子标识值是通过消息参数lParam传送到客户的,如果继续使用MAKELONG()进行高、低字组合,势必造成32位句柄值的破坏。对于这种情况应当使用PackDDElParam()函数进行组合。需要注意的是,在向客户传递WM_DDE_DATA消息时只能用PostMessage()函数去邮寄,而不能使用SendMessage()。如果服务器枚举完其提供的所有数据项后仍未找到匹配的数据项,就认为客户的本次请求失败,将向其邮寄WM_DDE_ACK消息作为否定应答,消息参数lParam的低字包含了状态信息。下面给出此部分的完整代码清单:
  
  int ITEM_NUM = 3; // 服务器提供的数据项数目
  CString ItemName[3] = {"Item A", "Item B", "Item C"}; // 服务器提供的数据项名
  char szItemNameClient[255]; // 客户请求的数据项名
  HWND hwndClient = (HWND)wParam; // 客户窗口句柄
  short cfFormat = LOWORD(lParam); // 客户传来的数据格式
  if (cfFormat == CF_TEXT){ // 格式一致则进入下一步操作
  // 取客户请求的数据项名
  GlobalGetAtomName(HIWORD(lParam), szItemNameClient, sizeof(szItemNameClient));
  for (int i = 0; i < ITEM_NUM; i++){// 检索与服务器提供的哪个数据项匹配
  if (strcmp(szItemNameClient, ItemName[i]) == 0) // 如检索到则跳出
  
  break;
  }
  ATOM atomItem = GlobalAddAtom(ItemName[i]); // 获取原子标识值
  // 若检索到数据项就发送WM_DDE_DATA消息,否则发送WM_DDE_ACK消息
  if (i < ITEM_NUM){
  LONG lDataPack = PackDDElParam(WM_DDE_DATA, (UINT)m_hDDEData, atomItem); // 组合消息参数lParam
  ::PostMessage(hwndClient, WM_DDE_DATA, (WPARAM)m_hwndServer, (LPARAM)lDataPack); // 邮寄WM_DDE_DATA消息
  }
  else {
  DDEACK DDEAck; // 填充DDEACK结构
  DDEAck.bAppReturnCode = 0;
  DDEAck.reserved = 0;
  DDEAck.fBusy = FALSE;
  DDEAck.fAck = FALSE;
  WORD wStatus = *(WORD*)&DDEAck;
  ::PostMessage(hwndClient, WM_DDE_ACK, (WPARAM)hwndServer, MAKELONG(wStatus, atomItem)); // 邮寄WM_DDE_ACK否定应答消息
  }
  GlobalDeleteAtom(atomItem); // 删除原子
  GlobalFree(m_hDDEData); // 释放申请的内存
  }
  
  DDE客户端的处理
  客户对服务器发出的WM_DDE_DATA消息的响应主要完成对服务器提供数据项内容的提取。首先调用UnpackDDElParam()将消息参数lParam中包含的DDEDATA格式化内存块句柄和数据项原子标识值提取出来。在检验了数据格式的有效后,将共享内存块中的数据项内容读取出来:
  
  UINT uLo, uHi; // lParam消息的低、高字
  UnpackDDElParam(WM_DDE_DATA,lParam,&uLo,&uHi);//对lParam进行解包
  HANDLE hDDEData = (HANDLE)uLo; // 取出DDEDATA句柄
  ATOM atomItem = (ATOM)uHi; // 取出数据项原子标识值
  DDEDATA* lpDDEData = (DDEDATA*)GlobalLock(hDDEData); // 锁定内存块
  if (lpDDEData->cfFormat == CF_TEXT) { // 检验数据格式
  // 报告服务器返回的数据项信息
  CString sMessage = "服务器返回的数据项内容为:" + CString(lpDDEData->Value);
  AfxMessageBox(sMessage);
  }
  GlobalUnlock(hDDEData); // 内存解锁
  
  与请求传送消息相比,客户也可以在没有请求的情况下通过向服务器传送WM_DDE_POKE消息实现对服务器的数据项传输,使用的消息参数lParam的高字为数据项原子标识值,低字为全局共享内存块的DDEPOKE结构句柄。从下面给出的向服务器窗口发送WM_DDE_POKE消息的代码段不难看出其与服务器使用WM_DDE_DATA消息发送数据的过程是非常类似的:
  
  CString sDataItem = "Data from Client!"; // 发送的数据项内容
  // 分配DDEPOKE格式化的全局共享内存块
  HGLOBAL hDDEPoke = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (LONG)sizeof(DDEPOKE) + sDataItem.GetLength() + 2);
  DDEPOKE* lpDDEPoke = (DDEPOKE*)GlobalLock(hDDEPoke); // 锁定内存块地址
  lpDDEPoke->cfFormat = CF_TEXT; // 设置格式字段
  ::strcpy((LPSTR)lpDDEPoke->Value, sDataItem); // 填充数据项内容
  ::strcat((LPSTR)lpDDEPoke->Value, "\r\n"); // 添加终结字符
  GlobalUnlock(hDDEPoke); // 解锁内存块
  ATOM atomItem = GlobalAddAtom("Item B"); // 获取数据项原子标识值
  HWND hwndClient = GetSafeHwnd(); // 获取当前窗口的安全句柄
  // 组合消息参数lParam
  LONG lDataPack = PackDDElParam(WM_DDE_POKE, (UINT)hDDEPoke, atomItem);
  // 向服务器窗口发送WM_DDE_POKE消息
  ::PostMessage(m_hwndServer, WM_DDE_POKE, (WPARAM)hwndClient, (LPARAM)lDataPack);
  
  DDE服务器对数据的接收处理
  服务器在响应WM_DDE_POKE消息后必须决定其是否能够接收客户发送来的格式数据。但不管服务器是否能够处理该数据都必须向客户发送一个WM_DDE_ACK消息作为应答,通过消息参数lParam的低字区分服务器是否成功接收数据:
  
  UINT uLo, uHi; // lParam消息的低、高字
  UnpackDDElParam(WM_DDE_DATA, lParam, &uLo, &uHi); // 对lParam进行解包
  HANDLE hDDEPoke = (HANDLE)uLo; // 取出DDEPOKE句柄
  ATOM atomItem = (ATOM)uHi; // 取出数据项原子标识值
  DDEPOKE* lpDDEPoke = (DDEPOKE*)GlobalLock(hDDEPoke); // 锁定内存块
  if (lpDDEPoke == NULL) {
  // 组合消息参数lParam
  LONG lDataPack = PackDDElParam(WM_DDE_ACK, (UINT)0, atomItem);
  // 向客户发送否定应答
  ::PostMessage((HWND)wParam, WM_DDE_ACK, (WPARAM)m_hwndServer, (LPARAM)lDataPack);
  return;
  }
  if (lpDDEPoke->cfFormat == CF_TEXT) { // 检验数据格式
  // 报告服务器返回的数据项信息
  CString sMessage = "客户发来数据:" + CString(lpDDEPoke->Value);
  AfxMessageBox(sMessage);
  } else {
  // 组合消息参数lParam
  LONG lDataPack = PackDDElParam(WM_DDE_ACK, (UINT)0, atomItem);
  // 向客户发送否定应答
  ::PostMessage((HWND)wParam, WM_DDE_ACK, (WPARAM)m_hwndServer, (LPARAM)lDataPack);
  GlobalUnlock(hDDEPoke); // 内存解锁
  return;
  }
  GlobalUnlock(hDDEPoke); // 内存解锁
  // 组合消息参数lParam
  LONG lDataPack = PackDDElParam(WM_DDE_ACK, (UINT)0x8000, atomItem);
  ::PostMessage((HWND)wParam, WM_DDE_ACK, (WPARAM)m_hwndServer, (LPARAM)lData
【责编:Lili】

中国IT教育热线咨询

相关文章
VC开发多语言界面支持的简单方法
VC中利用MFC设计绘图程序初步
VC中三种常见中文内码的转换方法
如何在C++中动态分配二维数组
几种VC++数据库开发技术的相对比较
VC++与MATLAB混合编程及其应用
Delphi中动态链接库两种调用方式的比较
用auto_ptr类模板帮助动态内存管理
推荐文章
· 用C#创建COM对象
· IT管理十大失误及其对策
· VC中利用MFC设计绘图程序初步
· JAVA中对象创建和初始化过程
· C语言中的位域的使用
· 浅谈Java桌面应用程序开发
· C#的前途如何?
· 几种VC++数据库开发技术的相对比较
 精彩友情推荐
·锐捷交换机报价
·锐捷交换机
·锐捷网络网络交换机
·smc交换机
·smc交换机报价
·IDC资讯大全
·机房品质万里行
·IDC托管必备知识
·全国IDC报价
·网站推广优化
最新更新 推荐文章
·Visual Basic 9.0隐式类型的局部…09-30
·JMX+J2SE5.0实现Web应用的安全管…09-30
·多线程、Socket技术及委托技术的…09-21
·Visual C#多线程参数传递浅析09-21
·浅谈Java中利用JCOM实现仿Excel编…09-21
·基于Java的界面布局DSL的设计与实…09-21
·Java开发中的事件驱动模型实例详…09-21
·并发工程原则应用到软件项目中09-06
·Delphi初学者应小心的六大陷阱09-06
·VC开发多语言界面支持的简单方法09-06
·用C#创建COM对象09-06
·用C#创建COM对象09-06
·IT管理十大失误及其对策08-30
·VC中利用MFC设计绘图程序初步08-23
·JAVA中对象创建和初始化过程08-23
·C语言中的位域的使用08-09
·浅谈Java桌面应用程序开发08-09
·C#的前途如何?08-02
·几种VC++数据库开发技术的相对比较07-12
·用Visual C#实现网络封包监视07-12
·VB.NET中的TextBox控件详解07-12
·VB.NET实现PC与掌上电脑PPC的双向通信07-05
  培训中心