第5章进程间通信.docx_第1页
第5章进程间通信.docx_第2页
第5章进程间通信.docx_第3页
第5章进程间通信.docx_第4页
第5章进程间通信.docx_第5页
已阅读5页,还剩6页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第5章进程间通信为了实现特殊的目的,系统编程时往往使用多个进程,这些进程往往会使用动态链接库为纽带,实现进程间的数据通信。进程间的数据通信方法有很多,如果只是一个简单的通知事件,可以采用Win32提供的系统对象比如信号灯、互斥、关键临界区、事件等。如果需要大量的数据通信,而又不在乎速度和性能,适时性要求不高可以采用文件、注册表和WindowsNT提供的事件日志等。对于适时性要求较高的场合,可以采用创建内存共享文件、创建管道、邮槽,通过窗口消息来实现消息共享。对于远程的应用程序,还可以通过socket和有关网络协议实现通信。前面已经提到了多种方法,比如内存共享文件、动态链接库的共享节等,这里主要介绍系统内核对象的应用以及其他几种进程间通信的方法。5.1 只启动一份程序实例只保留一份应用程序实例的目的是显而易见的,它主要是防止在多个实例加载的系统中造成资源浪费,同时也可以避免多个实例存取同一系统对象造成的各种冲突。为了只允许运行程序的一个实例,往往需要在程序运行时,对系统进行检查,发现已经有一个实例存在就立即退出。由于每一个进程启动时都会有一个进程标识,有时会创建自己的主窗口,在注册表或者系统文件中写入一些标记信息,有时也会创建一些系统对象,这些信息都可以作为查找前面一个实例存在的标志。前面介绍进程枚举时提到了通过进程枚举得到各个进程标识,进而得到进程名称的实现。实际上这个过程本身就可以作为查找应用程序实例的一种方法。一般地,用户会尽可能地避免修改可执行文件的名称,而进程文件名称重名的几率虽然存在,但是非常小,通过进程名称查找仍然是非常有效的。当然了这种方法极其繁琐,一方面它要处理WindowsNT4和Windows9x之间的差异;另一方面,进程的枚举也不是一件轻松的任务。所以这种方法的应用不是很多。通过窗口查找应该是一个简单易行的办法,因为大部分基于GUI实现的应用程序都会创建自己的窗口,这些窗口类名和窗口标题完全相同的几率很低,使用这种方法应该是简单有效的。Windows提供的FindWindow和FindWindowEx两个函数可以根据窗口类名和窗口标题来查找是否存在一个指定特征的窗口,以此来作为进程是否存在的标志。比如InternnetExplorer程序和Shell进程都有其独特的特征。利用这些特征来判断进程实例的存在是非常有效的,而且这种方法不需要创建任何系统内核对象。但是这种方法有一个很大的局限性,就是如果一个程序运行时没有窗口消息循环,这种方法就会失效。使用内核对象来识别程序实例是最精确的,相当于DNA鉴定,下面是一段采用互斥内核对象实现的,同样用户还可以使用事件、原子等对象。例5-1通过互斥对象实现惟一实例进程。HANDLECreateOneAppMutex(LPCTSTRlpName)HANDLE hMutex;/CreatemutexhMutex=CreateMutex(NULL,TRUE,lpName);switch(GetLastError() case ERROR_SUCCESS: break; /互斥对象创建成功,没有实例运行Case ERROR_ALREADY_EXISTS: hMutex=NULL;break; /互斥对象已经存在,程序的一个实例正在运行default: break; /由于某个原因可能无法创建实例return hMutex;这种方法实现的原理是基于所有系统内核对象不允许两个重名的对象共存。它实现的原理是,每个进程启动时首先根据系统的对象(事件、互斥、原子)检查该名称标识的对象是否存在。如果不存在,就创建该对象;否则直接退出。在程序退出时,系统会释放该系统对象。显然这种方法的准确性是无庸置疑的,但是使用这种方法有一定的风险,因为系统的对象的创建和释放是通过两个步骤实现的。如果在进程运行期间,系统异常退出,那么系统内核对象回收就无法实现。这样已经创建的对象将一致保持到系统注销或者重新启动时为止,除非用户采用其他方法关闭该内核对象。否则,可以设想进程本来已经异常终止,但是它申请创建的内核对象却没有释放,检查时这个对象仍然存在,这样很有可能进程将无法创建一个惟一的实例。5.2 使用共享内存实现进程间通信尽管进程间通信的方法有多种,但是对于单机而言,最底层共享的机制是内存共享文件。上面提到的多种方法都是基于内存共享文件实现的。使用内存共享文件可以提高共享的性能,降低系统开销。使用内存共享文件实现进程共享是基于同一文件映射对象的视图来实现的,各个进程会共享同一物理存储器的同一个页面,任何一个进程对该共享文件对象的修改都会同时影响其他进程对该对象的映射,它们会立即看到修改进程对共享内存对象的变更。使用内存共享文件实现共享数据,一般都会创建一个名称字符串标识的共享内存对象。创建的方法是调用CreateFileMapping,这个函数会返回一个句柄,用户可以利用返回的句柄调用MapViewOfFile函数把这个对象映射给一个指定存取类型的内存指针,通过该指针进行访问。不再共享时可以调用UnmapViewOfFile函数取消映射,并关闭文件句柄。为了防止存取冲突,一般还会配合使用其他内核对象,比如互斥对象、关键临界区来保证一次只能一个进程访问内存共享文件。下面是采用内存共享文件实现的一个类。例5-2内存共享文件的使用。#ifndef _SHAREDMEMORY_H#define _SHAREDMEMORY_HStatic char cstrSMName=SharedMemory;Class CSharedMemory protected:HANDLE m_hSharedMemoryFile;public: void*m_pwData;protected:DWORD dwMaximumSizeHigh;DWORD dwMaximumSizeLow;DWORD dwNumberOfBytesToMap;BOOL m_bInit;BOOL m_bAlreadyExist;Cstring csMutexName;HANDLE m_hMutex;public: Cstring csName;public:void*GetData() if(m_bInit) return m_pwData;Else return NULL; CSharedMemory()csName=cstrSMName;m_bInit=FALSE;m_bAlreadyExist=FALSE;csMutexName=csName+Mutex;CSharedMemory(LPCSTRcs)csName=cs;m_bInit=FALSE;m_bAlreadyExist=FALSE;csMutexName=csName+Mutex;CSharedMemory(intsize)csName=cstrSMName;m_bAlreadyExist=FALSE;csMutexName=csName+Mutex;Init(size);CSharedMemory(LPCSTRcs,intsize)csName=cs;m_bAlreadyExist=FALSE;csMutexName=csName+Mutex;Init(size);BOOLInit(LPCSTRcs,intsize)csName=cs;m_bAlreadyExist=FALSE;csMutexName=csName+Mutex;returnInit(size);BOOLInit(intsize,LPCSTRcs)csName=cs;m_bAlreadyExist=FALSE;csMutexName=csName+Mutex;returnInit(size);BOOLInit(intsize) m_hMutex=CreateMutex(NULL,FALSE,csMutexName);dwNumberOfBytesToMap=size;m_hSharedMemoryFile=CreateFileMapping(HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0,dwNumberOfBytesToMap,csName.GetBuffer(0);if(m_hSharedMemoryFile=NULL)m_bAlreadyExist=FALSE;m_bInit=FALSE; Return FALSE;else if(GetLastError()=ERROR_ALREADY_EXISTS)m_bAlreadyExist=TRUE;m_pwData=MapViewOfFile(m_hSharedMemoryFile,FILE_MAP_WRITE, 0,0,dwNumberOfBytesToMap);if(m_pwData=NULL) m_bInit=FALSE; CloseHandle(m_hSharedMemoryFile); Return FALSE;Else m_bInit=TRUE; returnTRUE; CSharedMemory()if(m_bInit) UnmapViewOfFile(m_pwData); CloseHandle(m_hSharedMemoryFile);public: BOOL AlreadyExist() return m_bAlreadyExist;/LockstheMemlikeCSingelLocklocker(TRUE,pSyncObj)/Use:/CSharedMemory:Lockerlocker(&MySharedMem);Modify(MySharedMem-GetData();Struct Locker Locker(CSharedMemory*sm)m_sm=sm; m_sm-Lock();Locker(CSharedMemory&sm) m_sm=&sm;m_sm-Lock();Locker()m_sm-Unlock();CSharedMemory*m_sm;/LockstheMem/Use: if(MySharedMem.Lock(100)Modify(MySharedMem-GetData();MySharedMem.Unlock();BOOL Lock(DWORDdwMilliSec=INFINITE) if(WaitForSingleObject(m_hMutex,dwMilliSec)=WAIT_OBJECT_0)Return TRUE; return FALSE; BOOL Unlock(DWORD dwMilliSec=INFINITE) return ReleaseMutex(m_hMutex);#endif使用这个类,需要包含这个头文件,例子代码如下:CsharedMemory m_sm;.m_sm.Init(MyScharedMemory,256 /*bytes*/);.char*pSharedString=(char*)m_sm.GetData().CSharedMemory:Lockerlocker(m_sm); ModifySomthing(pSharedString);5.3 使用窗口消息实现进程间通信在各种进程间通信时,应该说使用窗口消息实现通信是最简单的方法。作为提供消息的服务方,可以使用SendMessage函数或者PostMessage函数向客户端程序发送WM_COPYDATA消息,向客户程序发送一个COPYDATASTRUCT类型的数据结构。在这个结构中可以存放发送的数据,字符串数据必须按照字符数组的方式填充到这个结构中,不能传递指针。不少软件实现进程间的通信都是借助于WM_COPYDATA消息实现的,AutoCAD就是这样的一个应用程序,它本身可以接收服务程序向它发送的WM_COPYDATA消息数据。SendMessage函数发送的是同步消息,只有接收消息的窗口过程处理完这个消息,这个函数才能返回。可见使用这个函数能够及时地了解消息是否被接收方收到,但是同时也意味着这个消息可能会造成消息队列阻塞,工作效率不高。PostMessage函数只是把消息放进消息队列,然后就会立即返回。这个函数不需要等待,但是无法预知接收消息的线程是否收到该消息并进行处理。SendMessage发送通知消息的格式如下:SendMessage(HWND)hWnd,/目标窗口句柄WM_COPYDATA,/发送的消息标识(WPARAM)wParam,/发送窗口的句柄,这个句柄可以为NULL(HWND)(LPARAM)lParam/发送的数据,格式为(PCOPYDATASTRUCT);其中PCOPYDATASTRUCT结构定义如下:Typedef struct tagCOPYDATASTRUCTULONG _PTRdwData;DWORD cbData;PVOID lpData; COPYDATASTRUCT,*PCOPYDATASTRUCT其中:cbData指示结构的大小(以字节为单位)。lpData这是一个数据指针,它指向传递给另外一个进程的数据。例5-3一个发送消息的客户端程序。#include#include#include#include#include#ifndef_WMCOPYHDRS_#define_WMCOPYHDRS_#define SETSTRLEN(szString,iNum)(szString)(iNum)=0)#define WINSERVERNAME_T(WMCOPYSERVER#01)#define WINCLIENTNAME_T(WMCOPYCLIENT#01)#define SERVERWNDCLASSNAME_T(WMSERVERWND#01)#define SETCONSOLETITLE(szTitle)(SetConsoleTitle(szTitle)#define GETMODULEHANDLE(GetModuleHandle(NULL)#define FINDWINDOW(szWndClassName,szWndTitle)(FindWindow(LPCTSTR)szWndClassName,(LPCTSTR)szWndTitle)#defineSTPRINTF(szBuffer,szFormat,szArgs)(_stprintf(_TCHAR*)szBuffer,(_TCHAR*)szFormat,(unsigned long)szArgs)#define STRCPY(strDest,strSource,iCount)(_tcsncpy(_TCHAR*)strDest,(_TCHAR*)strSource,size_t(iCount)#define STRLEN(szValue)(_tcslen(const_TCHAR*)szValue)#define TPRINTF(szValue)(_tprintf(_TCHAR*)szValue)#define MEMCPY(szDest,szTarget,iCount)(memcpy(void*) szDest,(constvoid*)szTarget,(size_t)iCount)#define TCMP(szDest,szTarget)(_tcsicmp(const _TCHAR*)szDest,(const_TCHAR*)szTarget)#define WM_ASCENDING1#define WM_DESCENDING2#define WM_DISPLAY_TEXT3#define WM_QUIT_SERVER4#define _WM_MAXMESSAGE_SIZE0x10000#define _WM_HEADER_SIZE(2*sizeof(DWORD)+sizeof(HWND)Typedef struct _WM_DATASTRUCTUREDWORD cbSize;DWORD iMessage;HWND hClient;_TCHARData_WM_MAXMESSAGE_SIZE-_WM_HEADER_SIZE;WM_DATASTRUCTURE,*LPWM_DATASTRUCTURE;DWORD pID;HWND hServer;BOOL WINAPITransferData(DWORD dwMsg,const _TCHAR*Buffer,DWORD dwBytes) BOOL bSend;COPYDATASTRUCT cpStructData;LPWM_DATASTRUCTURE lpMsg;cpStructData.cbData=dwBytes+_WM_HEADER_SIZE;lpMsg=(LPWM_DATASTRUCTURE)LocalAlloc(LPTR,cpStructData.cbData);lpMsg-hClient=NULL;lpMsg-iMessage=dwMsg;lpMsg-cbSize=dwBytes;cpStructData.lpData=lpMsg;if(Buffer!=NULL)SETSTRLEN(lpMsg-Data,dwBytes);MEMCPY(lpMsg-Data,Buffer,dwBytes);bSend=SendMessage(hServer,WM_COPYDATA,(WPARAM)hServer,(LPARAM)&cpStructData); return(bSend);int main(int argc,char*argv)_TCHARszWndName256; _TCHARszData256; _TCHARszSort5;pID=GetCurrentProcessId();STPRINTF(szWndName,_T(PROCESSID%.X),pID);SETCONSOLETITLE(szWndName);if(hServer=FINDWINDOW(SERVERWNDCLASSNAME,NULL)=NULL)TPRINTF(_T(WMCOPYCLIENT:Pleaselaunchtheserver!n);elsewhile(TRUE)TPRINTF(_T(Enter1tosortinascendingorder:n);TPRINTF(_T(Enter2tosortindescendingorder:n);TPRINTF(_T(Enter3todisplaytheenteredvalueontheserver:n);TPRINTF(_T(Enter4tostoptheserver:n);_getts(szSort);if(TCMP(szSort,_T(1)=0)#if UNICODE&_UNICODETPRINTF(_T(ThisoptionisinvalidforUNICODEand_UNICODE.n);#else TPRINTF(_T(Enterthedatatobesortedinanascendingorder:n);_getts(szData);TransferData(WM_ASCENDING,szData,STRLEN(szData);#endifif(TCMP(szSort,_T(2)=0)#if UNICODE&_UNICODETPRINTF(_T(ThisoptionisinvalidforUNICODEand_UNICODE.n);#elseTPRINTF(_T(Enterthedatatobesortedinandescendingorder:n);_getts(szData);TransferData(WM_DESCENDING,szData,STRLEN(szData);#endifif(TCMP(szSort,_T(3)=0)TPRINTF(_T(Enterthedatatobedisplayedontheserver:n);_getts(szData);#if UNICODE&_UNICODETransferData(WM_DISPLAY_TEXT,szData,STRLEN(szData)*2);#elseTransferData(WM_DISPLAY_TEXT,szData,STRLEN(szData);#endifif(TCMP(szSort,_T(4)=0)TPRINTF(_T(Quitingtheserverandtheclient:n);TransferData(WM_QUIT_SERVER,szData,STRLEN(szData);break; return 0;接收方是这样实现的:/WMCOPYServer.cpp:Defines the entry point for the console application.#includestdafx.h#includeWMCOPYHDRS.hVoid Arrange_Ascen(_TCHAR*pString)for(int iCount=0;iCountSTRLEN(pString)*2;iCount+) for(int jCount=iCount+1;jCount=STRLEN(pString)-1;jCount+) if(pStringjCountpStringiCount)_TCHARszTemp=pStringjCount;pStringjCount=pStringiCount;pStringiCount=szTemp;Void Arrange_Descen(_TCHAR*pString) for(int iCount=0;iCountSTRLEN(pString)*2;iCount+) for(int jCount=iCount+1;jCountpStringiCount)_TCHARszTemp=pStringjCount;pStringjCount=pStringiCount;pStringiCount=szTemp;LRESULT CALLBACK WMCOPYWNDPROC(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAMl Param) LPWM_DATASTRUCTUREl pMsg;if(uMsg=WM_COPYDATA) lpMsg=(LPWM_DATASTRUCTURE)(COPYDATASTRUCT*)lParam)-lpData;#if_UNICODE&UNICODESETSTRLEN(lpMsg-Data,(lpMsg-cbSize)*2);#elseSETSTRLEN(lpMsg-Data,(lpMsg-cbSize);#endifswitch(lpMsg-iMessage) case WM_DISPLAY_TEXT:TPRINTF(_T(n);TPRINTF(lpMsg-Data);break;Case WM_ASCENDING: Arrange_Ascen(lpMsg-Data);TPRINTF(_T(nServer sorted the data in ascending order successfullyn);TPRINTF(lpMsg-Data);break;Case WM_DESCENDING: Arrange_Descen(lpMsg-Data);TPRINTF(_T(nServer sorted the data in descending order successfullyn);TPRINTF(lpMsg-Data);break;Case WM_QUIT_SERVER: PostQuitMessage(0);break;Else return DefWindowProc(hwnd,uMsg,wParam,lParam);Int main(intargc,char*argv)MSG iMsg;BOOL bVal;WNDCLASSWM Class;WMClass.style=CS_GLOBALCLASS;WMClass.lpfnWndProc=WMCOPYWNDPROC;SETCONSOLETITLE(WINSERVERNAME);if(FINDWINDOW(SERVERWNDCLASSNAME,NULL)!=NULL)TPRINTF(_T(WMCOPYSERVER:Server is already running!n);elseWMClass.hInstance=GETMODULEHANDLE;WMClass.lpszClassName=SERVERWNDCLASSNAME;WMClass.cbClsExtra=0;WMClass.cbWndExtra=0;WMClass.hbrBackground=NULL;WMClass.hCursor=NULL;WMClass.hIcon=NULL;WMClass.lpszMenuName=NULL;if(RegisterClass(&WMClass)=0) TPRINTF(_T(WMCOPYSERVER:classregisterationfailed!n);elseif(CreateWindow(LPCTSTR)SERVERWNDCLASSNAME,NULL,0,0,0,0,0,NULL,NULL,WMClass.hInstance,NULL)=NULL)TPRINTF(_T(nWMCOPYSERVER:Cantcreatewindow!);elseTPRINTF(_T(nWMCOPYSERVER:Serverlaunchedsuccessfully!);while(bVal=GetMessage(&iMsg,NULL,0,0)&(bVal!=-1) DispatchMessage(&iMsg);TPRINTF(_T(nWMCOPYSERVER:PressEntertoquit.); Return getchar();5.4 使用邮槽实现进程间通信MailSlot邮槽早在Windows95和WindowsNT3.1时代就被操作系统引入了,但是大多数的Windows开发者,并不了解MailSlot到底是什么意思,甚至会把它和基于SMTP和POP3协议的E-mail联系起来。MailSlot是Win32进程间通信可用的方式之一,它和E-mail一点关系都没有。邮槽在很多方面类似于命名管道,由于后者比较灵活,所以得到了广泛的应用。邮槽的通信是在服务程序和客户程序之间完成的。服务程序负责创建邮槽,客户程序只能打开邮槽,向邮槽中写入内容,但是客户程序本身不能读取邮槽中的信息,只有创建邮槽的服务程序才能读取邮槽中的内容,因而这是一种单向的通信方式,也是它固有的局限性。实际应用中,为了达到双向通信的目的,往往会采用两个邮槽,每一个进程既充当客户端又充当服务器端。这时邮槽就相当于一个消息队列,到来的信息被追加到消息队列的尾部,服务器端会按照顺序去读取消息队列中的消息。消息在没有读取之前,它将一直被保存在消息队列中,不会丢失。阅读后,它将被从消息队列中摘除。邮槽相对于命名管道也有一个优点,那就是广播消息。也就是说,客户端发出的消息既可以发送到本机进程的邮槽中,也可以发送到远程主机的邮槽中,比如网络邻居的中另外一个计算机。发送的目标可以指定一个计算机名或者域服务器的名称。当指定域服务器名称的时候,传输的信息不能超过400B。一般发送的消息长度由创建邮槽的服务进程决定,显然每一个进程都不希望提供的邮槽缓冲区溢出。值得注意的是,邮槽中的消息是有序的。邮槽是采用数据报来实现消息广播的,也就是说,对于客户端进程而言,数据报不会提供任何机制来保证和证实消息肯定被服务进程接收。就像广播电台发出的无线电波可能会因为高山和大厦的阻碍和干扰信号的影响,不能保证被收听者接收到一样。的确存在一些情况会阻碍数据报能够达到目的地。而命名管道就不同了,它只和一方进行通信,就像打电话一样,很容易知道消息是否接收到。使用邮槽通信需要下面几个方面的工作:1.创建邮槽服务器记住邮槽服务器就是创建邮槽的进程,只有邮槽服务器(或者说是继承邮槽句柄的进程)才有资格来读取邮槽中的消息,用户千万不要异想天开地去设想创建远程邮槽,远程邮槽不会得到操作系统的任何支持,是不可能实现的。创建邮槽需要调用CreateMailSlot函数,这个函数需要一个参数来标识邮槽的名称,以便客户程序能够用来建立和这个邮槽的连接。同时还需要指定消息的最大长度以及阅读消息的超时值和一个指向安全属性的结构指针。邮槽的名称不会区分大小写,消息最大长度如果为0,则表示消息的内容几乎没有限制,可以长达64KB。阅读消息的超时值意味着服务程序会在该时间内等候消息的到来。如果该值为零,意味着服务程序如果检测到队列中没有消息,将会立即返回。同样如果指定了MAILSLOT_WAIT_FOREVER参数,则表示服务程序在消息到来被接收之前不会返回。其中邮槽的名称定义支持下列格式:.mailslotname本地邮槽命名computernamemailslotname远程计算机邮槽domainnamemailslotname位于指定域服务器上的邮槽*mailslotname位于主域服务器上的邮槽LPSTRg_lpszSlotName=.mailslotsample_mailslot;HANDLEg_hSlot1=NULL;g_hSlot1=CreateMailslot(g_lpszSlotName,0,/没有程度限制MAILSLOT_WAIT_FOREVER,/永久等待(LPSECURITY_ATTRIBUTES)NULL);/无安全属性if(hSlot1=INVALID_HANDLE_VALUE)/*创建失败,进行错误处理*/2.发送邮槽消息操作邮槽和操作文件是一样的。客户端可以使用CreateFile打开存在的邮槽,通过WriteFile或者WriteFileEx向邮槽中写入内容。最后调用CloseHandle关闭邮槽即可。再重申一遍,客户端永远不要尝试调用ReadFile函数去读取刚刚写入的内容。LPSTR lpszMessage=Message for sample_mails lot in primary domain.;BOOL fResult;HANDLE hFile;DWORD cbWritten;hFile=CreateFile(*mailslotsample_mailslot,GENERIC_WRITE,FILE_SHARE_READ,/在写邮槽时需要这个标志(LPSECURITY_ATTRIBUTES)NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);if(hFile=INVALID_HANDLE_VALUE) Return FALSE; /错误处理fResult=WriteFile(hFile,lpszMessage,(DWORD)lstrlen(lpszMessage)+1,/包含空字符&cbWritten,(LPOVERLAPPED)NULL);if(!fResult) /*写入失败处理*/ CloseHandle(hFile);3.阅读邮槽消息Extern HANDLE g_hSlot1; DWORD cbMessage,cMessage,cbRead;BOOL fResult; LPSTR lpszBuffer; CHAR achID80; DWORD cAllMessages; HANDLE hEvent; OVERLAPPED ov;cbMessage=cMessage=cbRead=0; hEvent=CreateEvent(NULL,FALSE,FALSE,ExampleSlot);if(NULL=hEvent) return FALSE;ov.Offset=0; ov.OffsetHigh=0;ov.hEvent=hEvent;fResult=GetMailslotInfo(g_hSlot1,/*mailslot句柄*/(LPDWORD)NULL,/*不指定最大消息长度*/&cbMessage,/*下一条消息长度*/ &cMessage,/*消息个数*/ (LPDWORD)NULL);/无阅读超时if(!fResult) /*错误处理,返回*/if(cbMessage=MAILSLOT_NO_MESSAGE) /*消息队列为空,返回*/ cAllMessages=cMessage; while(cMessage!=0)/接收所有消息 /创建一个消息号字符串wsprintf(LPSTR)achID,nMessage#%dof%dn,cAllMessages-cMessage+1,cAllMessages);lpszBuffer=(LPSTR)GlobalAlloc(GPTR,lstrlen(LPSTR)achID)+cbMessage); /为消息分配内存if(NULL=lpszBuffer) return FALSE;lpszBuffer0=0;fResult=ReadFile(hSlot1,lpszBuffer,cbMessage,&cbRead,&ov);if(!fResult) GlobalFree(HGLOBAL)lpszBuffer); /读取失败处理lstrcat(lpszBuffer,(LPSTR)achID);MessageBox(NULL,lpszBuffer,ContentsofMailslot,MB_OK); /*显示消息*/ GlobalFree(HGLOBAL)lpszBuffer);fResult=GetMailslotInfo(g_hSlot1,/*mailslot句柄*/(LPDWORD)NULL,/*不指定最大消息长度*/&cbMessage,/*下一条消息长度*/&cMessage,/*消息个数*/ (LPDWORD)NULL);/无阅读超时if(!fResult) /*错误处理*/通过上面的描述来看,使用邮槽还是比较简单的,它可以作为进程间数据共享的又一种替代方案。一般地,如果不需要把消息广播给多个计算机,或者需要发送较长的消息,这时最好采用命名管道。相反,如果程序需要广播消息,或者需要单向通信,邮槽可能是最好的选择。5.5 使用剪贴板实现共享剪贴板也是一种特殊的内存对象,这个对象对所有进程都是可见的。任何进程都可以采用正确的格式使用剪贴板数据,所以剪贴板可以扩大共享的范围,它可以实现在当前所有桌面应用程序间实现共享。然而系统只支持一个剪贴板对象,任何对它的改写都会造成原有剪贴板内容的丢失。多种剪贴板工具,只是把前面的剪贴板内容转储到文件或者内存中。对剪贴板的操作是通过一组剪贴板函数实现的。下面的例子实现了复制到剪贴板,粘贴剪贴板内容的操作。剪贴板支持一种名为CF_HTML的格式,这个格式可以实现把HTML格式的网页复制到一个支持格式的文件编辑器中。CF_HTML基本是基于文本格式存储的,但是它包含了描述符和上下文标记符。例5-4实现把HTML文本放进剪贴板中。/调用CallHtml(Thisisatest);Void CopyHTML(char*html)char*buf=newchar400+strlen(html); /为HTML头创建临时缓冲区if(!buf) return; /得到HTML格式IDstaticintcfid=0;if(!cfid)cfid=RegisterClipboardFormat(HTMLFormat);/为HTML头创建一个模板字符串strcpy(buf,Version:0.9rnStartHTML:00000000rnEndHTML:00000000rnStartFragment:00000000rnEndFragment:00000000rnrnrn);/追加HTML文本strcat(buf,html);strcat(buf,rn);strcat(buf,rnrn);/计算长度和偏移量char*ptr=strstr(buf,StartHTML);wsprintf(ptr+10,%08u,strstr(buf,)-buf);*(ptr+10+8)=r;ptr=strstr(buf,EndHTML);wsprintf(ptr+8,%08u,strlen(buf);*(ptr+8+8)=r;ptr=strstr(buf,StartFragment);wsprintf(ptr+14,%08u,strstr(buf,!-StartFrag)-buf);*(ptr+14+8)=r;ptr=strstr(buf,EndFragment);wsprintf(ptr+12,%08u,strstr(buf,!-EndFrag)-buf);*(ptr+12+8)=r;/送入剪贴板if(OpenClipboard(0)EmptyClipboard();HGLOBALhText=GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE,strlen(buf)+4);/在全局内存中放入字符串char*ptr=(char*)GlobalLock(hText);strcpy(ptr,buf);GlobalUnlock(hText);:SetClipboardData(cfid,hText);CloseClipboard();GlobalFree(hText);deletebuf; /释放内存调用举例:char*html=Thisisatestentry1entry2;CopyHTML(html);使用HTML格式粘贴可以这样实现:Void UTF8ToHtml(BYTE*utf8,DWORD dwSize,CHAR*ptr)int code;BYTE*end=utf8+dwSize;while(utf8end) code=0;if(*utf8&0xF0)=0xF0) code=(*utf8)&0x0F)18)|(*(utf8+1)&0x7F)12)|(*(utf8+2)&0x7F)6)|(*(utf8+3)&0x7F); utf8+=3;Else if(*utf8&0xE0)=0xE0)code=(*utf8)&0x1F)12)|(*(utf8+1)&0x7F)6)|(*(utf8+2)&0x7F);utf8+=2;Else if(*utf8&0xC0)=0xC0) code=(*utf8)&0x3F)6)|(*(utf8+1)&0x7F);utf8+=1;if(code=0) *ptr=*utf8;else chars10;switch(code)case 160: strcpy(s,&); break;Case 34: strcpy(s,&);break;Case 36: strcpy(s,&);break;Case 60: strcpy(s,&); break;default: sprintf(s,code); break;strcpy(ptr,s); ptr+=strlen(s)-1; utf8+;ptr+; *ptr=0; LRESU

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论