吉林大学远程教育课件 Windows A P I编 程 (第四十五讲) 主讲人 : 翟慧杰 学 时:48
函数 BroadcastTransaction是一个自定义的函数,在例13-2中的具体定义如下:
在上述程序段中,函数DdeQueryNextServer用于获得连接列表中的下一个连接句柄。其函数原型定义如下:
如果函数 DdeQueryNextServer返回值不为空,则程序将进入一个 while循环,在 while循环体中定义了一个计数器,用于记录服务器的个数。同时循环体通过函数DdeClientTransaction来开始一个客户事务处理, DdeClientTransaction函数的原型定义如下:
如果服务器不能对客户方的事务进行处理,程序中通过调用函数 DdeAbandonTransaction放弃事务处理并回收所有的资源。函数DdeAbandonTransaction的原型定义如下: 在主窗口的消息处理函数MainwindProc中,处理WM_TIMER消息时还用到了一个DdePostAdvise函数,这个函数将使系统发送一条XTYP_ADVREQ消息给DDE回调函数,DdePostAdvise函数的原型定义如下:
例13-2的核心是 DDE回调函数,本例中定义的回调函数如下:
回调函数参数表中的wType参数用于指定回调类型;hszTopic参数用于指定主题名:参数hszItem比m用于指定主题中的某一项的名称。不难看出,上面的回调函数和消息处理函数有几分相似。 当服务器处理完XTYP_CONNECT事务并返回TRUE时,DDE会话也就开始了。服务器和客户之间的通信实际上都是借助上述的事务类型来实现的。当客户向服务器发送一个请求时,DDE管理库就会用相应的事务类型调用服务器的回调函数。 回调函数接收的事务确定于应用程序在DdeInitialize中指定的回调过滤标志,以及应用程序是否是客户或服务器,还是二者兼是。DDE事务类型如表 13-2所示。
例13-2的源程序 #include <windows.h> #include <ddeml.h> #include <stdlib.h> #include <string.h> #ifdef UNICODE #define STRICMP wcsicmp #define ITOA(c, sz, b) (itoa(sizeof(szA), szA, b), mbstowcs(sz, szA, b), sz) #else #define STRICMP stricmp #define ITOA itoa #endif //函数声明 HDDEDATA CALLBACK DdeCallback(WORD wType, WORD wFmt, HCONV hConv, HSZ hszTopic, HSZ hszItem, HDDEDATA hData, DWORD lData1, DWORD lData2);
VOID PaintDemo(HWND hwnd); LONG APIENTRY MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LONG lParam); VOID BroadcastTransaction(PBYTE pSrc,DWORD cbData,UINT fmt,UINT xtyp); #define BASE_TIMEOUT 100 BOOL fActive; //标识数据是否正在被改变 DWORD idInst = 0; // DDEML对象 HANDLE hInst; //应用程序句柄 HCONVLIST hConvList = 0; //连接列表 HSZ hszAppName = 0; //应用程序名 HWND hwndMain; //主窗口句柄 TCHAR szT[20]; //用于重绘的静态缓冲区句柄 #ifdef UNICODE CHAR szA[20]; //用于UNICODE 转化缓冲区 TCHAR szTitle[] = TEXT("DDEmo (U)"); #else TCHAR szTitle[] = TEXT("DDEmo"); #endif
TCHAR szApp[] = TEXT("DDEmo"); // DDE服务名 TCHAR szPause[] = TEXT("PAUSE"); // DDE可执行命令 TCHAR szResume[] = TEXT("RESUME");// DDE可执行命令 UINT OurFormat; //定制的格式 int InCount = 0; //输入数据缓冲区 int cConvs = 0; // 激活的conversations数 int count = 0; //输出数据 int cyText, cxText, cyTitle; //重绘区域 //函数:WinMain //作用:程序入口 int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,INT nCmdShow) { MSG msg; WNDCLASS wc; TEXTMETRIC metrics; HDC hdc;
//窗口初始化 wc.style = 0; wc.lpfnWndProc = MainWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = szTitle; if (!RegisterClass(&wc)) return(FALSE); //DDE初始化 if (DdeInitialize(&idInst, (PFNCALLBACK)MakeProcInstance((FARPROC)DdeCallback, hInstance), APPCMD_FILTERINITS |CBF_SKIP_CONNECT_CONFIRMS |CBF_FAIL_SELFCONNECTIONS |CBF_FAIL_POKES,0))
hwndMain = CreateWindow( szTitle, szTitle, hInst = hInstance; //创建窗口 hwndMain = CreateWindow( szTitle, szTitle, WS_CAPTION | WS_BORDER | WS_SYSMENU, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, NULL, NULL, hInstance, NULL); if (!hwndMain) { DdeUninitialize(idInst);//结束DDE return(FALSE); } hdc = GetDC(hwndMain); GetTextMetrics(hdc, &metrics);//获取当前所用字体 cyText = metrics.tmHeight + metrics.tmExternalLeading; cxText = metrics.tmMaxCharWidth * 8; cyTitle = GetSystemMetrics(SM_CYCAPTION); ReleaseDC(hwndMain, hdc);
//创建字符串句柄 hszAppName = DdeCreateStringHandle(idInst, szApp, 0); //注册自定义数据格式 OurFormat = RegisterClipboardFormat(szApp); //注册服务器名 DdeNameService(idInst, hszAppName, 0, DNS_REGISTER); //建立DDE会话列表 hConvList = DdeConnectList(idInst, hszAppName, hszAppName, hConvList, NULL); //向所有的服务器广播事务 BroadcastTransaction(NULL, 0, OurFormat, XTYP_ADVSTART); SetWindowPos(hwndMain, 0, 0, 0, cxText, (cyText * (cConvs + 1)) + cyTitle, SWP_NOMOVE | SWP_NOZORDER); ShowWindow(hwndMain, nCmdShow); UpdateWindow(hwndMain); //消息循环
while (GetMessage(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } DestroyWindow(hwndMain); UnregisterClass(szTitle, hInstance); return(FALSE); //函数:BroadcastTransaction //作用:向所有服务器广播客户的事务请求 VOID BroadcastTransaction(PBYTE pSrc,DWORD cbData,UINT fmt,UINT xtyp) { HCONV hConv; DWORD dwResult; int cConvsOrg; cConvsOrg = cConvs; cConvs = 0; //初始化连接数为0 if (hConvList)
{ //下一个会话句柄 hConv = DdeQueryNextServer(hConvList, 0); while (hConv) { cConvs++; //处理客户事务 if (DdeClientTransaction(pSrc, cbData, hConv, hszAppName, fmt, xtyp, TIMEOUT_ASYNC, &dwResult)) //放弃客户事务 DdeAbandonTransaction(idInst, hConv, dwResult); } hConv = DdeQueryNextServer(hConvList, hConv); if (cConvs != cConvsOrg)
{//重绘窗口 InvalidateRect(hwndMain, NULL, TRUE); } //函数:MyProcessKey //作用:处理键盘消息 VOID MyProcessKey(TCHAR tchCode,LONG lKeyData) { switch (tchCode)
case TEXT('B'): case TEXT('b'): *((PBYTE)(-1)) = 0; break;}} //函数:MainWndProc //作用:主窗口消息循环 LONG APIENTRY MainWndProc(HWND hwnd,UINT message,WPARAM wParam,LONG lParam) { RECT rc; switch (message) {
case WM_CREATE://创建窗口 fActive = FALSE; break; case WM_RBUTTONDOWN: //按下鼠标右键 if (GetKeyState(VK_CONTROL) & 0x8000) //判断控制键的状态 {
//广播事务处理 BroadcastTransaction((PBYTE)szPause, sizeof(szPause), 0, XTYP_EXECUTE); MessageBeep(0); } KillTimer(hwndMain, 1);//销毁定时器 fActive = FALSE; InvalidateRect(hwnd, NULL, TRUE);//重绘窗口 UpdateWindow(hwnd); break; case WM_LBUTTONDOWN: //按下鼠标左键 if (GetKeyState(VK_CONTROL) & 0x8000) //判断控制键状态 { BroadcastTransaction((PBYTE)szResume, sizeof(szResume), 0, XTYP_EXECUTE); MessageBeep(0); //发出蜂鸣声
//设置定时器 SetTimer(hwndMain, 1, BASE_TIMEOUT + (rand() & 0xff), NULL); fActive = TRUE; InvalidateRect(hwnd, NULL, TRUE);//重绘窗口 UpdateWindow(hwnd); break; case WM_CHAR://键盘消息 MyProcessKey((TCHAR)wParam, lParam); case WM_TIMER://定时器消息 count++; //发送XTYP_ADVREQ事务给服务器的回调函数 DdePostAdvise(idInst, hszAppName, hszAppName); SetRect(&rc, 0, 0, cxText, cyText); InvalidateRect(hwndMain, &rc, TRUE);//重绘窗口 UpdateWindow(hwndMain);
case WM_PAINT://绘制窗口 PaintDemo(hwnd); break; case WM_CLOSE://结束应用程序 KillTimer(hwnd, 1); DdeDisconnectList(hConvList);//断开DDE会话 //撤消已注册的服务器名 DdeNameService(idInst, 0, 0, DNS_UNREGISTER); //销毁字符串句柄 DdeFreeStringHandle(idInst, hszAppName); DdeUninitialize(idInst);//结束DDE会话 PostQuitMessage(0); default://其他消息 return (DefWindowProc(hwnd, message, wParam, lParam)); } return(0);
//函数:PaintDemo //作用:绘制窗口 VOID PaintDemo(HWND hwnd) { PAINTSTRUCT ps; RECT rc; HCONV hConv; CONVINFO ci; int cConvsOrg = cConvs; BeginPaint(hwnd, &ps);//开始绘制 SetRect(&rc, 0, 0, cxText, cyText);//设定矩形区域 SetBkMode(ps.hdc, TRANSPARENT);//设置背景模式 SetTextColor(ps.hdc, 0x00FFFFFF); //文本颜色为白色 //填充矩形区域 FillRect(ps.hdc, &rc, (HBRUSH)GetStockObject(fActive ? BLACK_BRUSH : GRAY_BRUSH));
//输出文本 DrawText(ps.hdc, ITOA(count, szT, 10), -1, &rc, DT_CENTER | DT_VCENTER); if (hConvList) { OffsetRect(&rc, 0, cyText); SetTextColor(ps.hdc, 0); //设定文本颜色为黑色 cConvs = 0; //查询下一个服务器 hConv = DdeQueryNextServer(hConvList, 0); while (hConv) cConvs++; ci.cb = sizeof(CONVINFO); //获取DDE会话信息 DdeQueryConvInfo(hConv, QID_SYNC, &ci); FillRect(ps.hdc,&rc, (HBRUSH)GetStockObject(WHITE_BRUSH)); // 例13-2的源程序色背景
//输出文本 DrawText(ps.hdc, ITOA(ci.hUser, szT, 10), -1, &rc,DT_CENTER | DT_VCENTER); OffsetRect(&rc, 0, cyText); hConv = DdeQueryNextServer(hConvList, hConv); } EndPaint(hwnd, &ps);//结束绘制 if (cConvsOrg != cConvs) { SetWindowPos(hwndMain, 0, 0, 0, cxText, (cyText * (cConvs + 1)) + cyTitle,SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); //函数:DdeCallback //作用:DDE回调函数
HDDEDATA CALLBACK DdeCallback(WORD wType,WORD wFmt,HCONV hConv,HSZ hszTopic,HSZ hszItem,HDDEDATA hData,DWORD lData1,DWORD lData2) {LPTSTR pszExec; switch (wType) { case XTYP_CONNECT://处理XTYP_CONNECT事务 return((HDDEDATA)TRUE); case XTYP_ADVREQ: case XTYP_REQUEST: //创建数据句柄 return(DdeCreateDataHandle(idInst,(PBYTE)&count, sizeof(count), 0, hszAppName, OurFormat, 0)); case XTYP_ADVSTART: return(HDDEDATA) ((UINT)wFmt == OurFormat && hszItem == hszAppName); case XTYP_ADVDATA: if (DdeGetData(hData, (PBYTE)&InCount, sizeof(InCount), 0)) {
//设置用户句柄 DdeSetUserHandle(hConv, QID_SYNC, InCount); } InvalidateRect(hwndMain, NULL, TRUE); return((HDDEDATA)DDE_FACK); case XTYP_EXECUTE: //访问DDE数据 pszExec = (LPTSTR)DdeAccessData(hData, NULL); if (pszExec) { if (fActive && !STRICMP(szPause, pszExec)) //消息定时器 KillTimer(hwndMain, 1); fActive = FALSE; UpdateWindow(hwndMain);
else if (!fActive && !STRICMP(szResume, pszExec)) { //设定定时器 SetTimer(hwndMain, 1, BASE_TIMEOUT + (rand() & 0xff), NULL); fActive = TRUE; InvalidateRect(hwndMain, NULL, TRUE); UpdateWindow(hwndMain); } MessageBeep(0); break; case XTYP_DISCONNECT: case XTYP_REGISTER:
//向服务器广播事务 BroadcastTransaction( NULL, 0, OurFormat, XTYP_ADVSTOP ); hConvList = DdeConnectList(idInst, hszItem, hszAppName, hConvList, NULL); BroadcastTransaction(NULL, 0, OurFormat, XTYP_ADVSTART); SetWindowPos(hwndMain, 0, 0, 0, cxText,(cyText * (cConvs + 1)) + cyTitle, SWP_NOMOVE | SWP_NOZORDER); UpdateWindow(hwndMain); return((HDDEDATA)TRUE); }return(0); }
例13-2的执行结果如图13-2所示。
13.3对象链接与嵌入基础知识 对象链接与嵌入实际上包括链接和嵌入对象、观察对象和编辑对象等涉及的所有技术。 11.3.1对象链接与嵌入 在这里一个对象被定义为任何一个文本中显示的并且可以被最终用户修改的数据。对象范围可从小到一个电子表格单元大到一个完整的文本,当一个对象归属于一个文本时,它与创建它的应用程序保持着一种联系。当一个对象被嵌入到其他对象之中时,不仅相关的数据被嵌入,而且相应的数据规则也被嵌入,这样用户不必返回该源应用程序就可以对一个对象进行编辑和修改。 当一个OLE文本包含了以各种不同格式表示的数据时,它就被称为一个复合文本。如果没有实现OLE技术,则每一种不同的数据类型都能够被最终用户修改。最终用户不得不在不同的应用程序中取得不同的编辑工具;如果应用程序实现了OLE技术,在同一环境下就可以实现对不同格式的数据进行编辑和修改。OLE技术给予了最终用户一个以文本为中心的参照框架系统,而不是一个以应用程序为中心的参照框架系统。
复合文本使用应用程序的许多特性来修改包含在其中的数据类型,任何一种数据均可以包含在一个复合文本之中。最终用户可以不必知道哪些数据格式与其他的数据格式相兼容,最终用户也不必知道怎样寻找创建该数据的其他应用程序。当用户在一个文本的一个部分里工作时,负责那个部分的应用程序合自动地开始工作。 OLE的好处在于: .文件的链接对象可以动态地加以改变; .当前的应用程序可以对未来的数据格式进行扩展,因为一个对 象的内容与包含它的文本无关; .文本文件可以更加紧凑,这是因为对一个对象的链接使得该文 本使用了该对象, 而不必存储该对象的数据; .一个应用程序可以很好地、专业化地做某项工作; .最终用户面向的是~个以文本为中心的用户系统; .实现了OLE协议就可以利用多种多样不同类型的对象优点。