Ø 剪貼板
Ø 匿名管道
Ø 命名管道
Ø 郵槽
1. 剪貼板:
剪貼板其實是系統管理的一個內存區域,當一個程序發生拷貝的時候,將是該內存區域得到填充,使用粘貼的時候是重該區域取出數據,然后顯示的對應窗口上。
將指定內容賦值到剪貼板上:
a. 打開剪貼板:OpenClipboard,注意:一旦打開了剪貼版,其它運用程序將無法修改剪貼板,直到調用了CloseClipboard。
b. 清空剪貼板:EmptyClipboard,清空剪切板,並將所有權交付給打開剪貼板的運用程序
c. 為即將拷貝的內容分配內存空間:GlobalAlloc,第一個參數指示分配內存的類型,重要的有兩類,GMEM_FIXED:Allocates fixed memory. The return value is a pointer;GMEM_MOVEABLE:Allocates movable memory. In Win32, memory blocks are never moved in physical memory, but they can be moved within the default heap.The return value is a handle to the memory object. To translate the handle into a pointer, use the GlobalLock function. This flag cannot be combined with the GMEM_FIXED flag.
本例中采用GMEM_MOVEABLE,其返回值是一個指向內存對象的句柄。
d. 將句柄轉換為指針:GlobalLock,將指定內存塊鎖定。
The internal data structures for each memory object include a lock count that is initially zero. For movable memory objects, GlobalLock increments the count by one, and the GlobalUnlock function decrements the count by one. For each call that a process makes to GlobalLock for an object, it must eventually call GlobalUnlock.Locked memory will not be moved or discarded, unless the memory object is reallocated by using the GlobalReAlloc function. The memory block of a locked memory object remains locked until its lock count is decremented to zero, at which time it can be moved or discarded. 18:26有翻譯
e. 將字符串的內容拷貝到可移動堆中:strcpy
f. 釋放內存塊鎖定:GlobalUnlock
g. 放置數據:SetClipboardData, The SetClipboardData function places data on the clipboard in a specified clipboard format. The window must be the current clipboard owner, and the application must have called the OpenClipboard function.
(本例程序沒有采用)SetClipboardData的第一個參數可以是指定的格式或NULL,如果是NULL,則采用的是延遲提交的技術,所謂延遲提交表示的是為了避免下面這種情況:當一個拷貝數據到剪貼板的動作發生時,直到下一個從剪貼板上取出數據的過程中,數據一直占用着內存空間,造成了資源浪費。為了改善這種情況,延遲提交技術采用SetClipboardData調用一個空的內存區塊,當下一個從剪貼板取出數據的動作發生時,自動發送一個WM_RENERFORMAT消息,剪貼板的所有者程序再次調用具有實際內存區塊參數的SetClipboardData方法,發生實際剪貼動作。第二次調用前不用再調用OpenClipboard方法。
h. 關閉剪貼板:CloseClipboard
實現代碼如下:
if(OpenClipboard()) //打開剪貼板
{
CString str;
HANDLE hClip; //剪貼板句柄
char* pBuf;
EmptyClipboard();
GetDlgItemText(IDC_EDIT_SEND,str);
//分配內存的長度一般是字符串的長度加1用來存放空字符,否則系統將自動覆蓋掉現有字符串的最后一位用來存放空字符,空字符作為結尾標識
hClip=GlobalAlloc(GMEM_MOVEABLE,str.GetLength()+1);
pBuf=(char*)GlobalLock(hClip); //將句柄轉換為指針,返回內存對象地址並加鎖,如果GlobalAlloc參數是GMEM_FIXED,則這樣不需要這樣的轉換。 GMEM_FIXED鎖計數總為零,該語句將增長Lock數
strcpy(pBuf,str); //為分配好的內存空間填充想賦的值
GlobalUnlock(hClip); //如果GlobalAlloc參數是GMEM_FIXED,則不起作用。該語句將減少Lock數,如果Lock數為0,則指定動態內存區域將可被移動和拋棄
SetClipboardData(CF_TEXT,hClip); //以指定格式存放數據,不完成指定格式轉換,不能完成粘貼
CloseClipboard();
}
從剪貼板上提取數據:
具體代碼如下:
if(OpenClipboard())
{ GMEM_FIXED
if(IsClipboardFormatAvailable(CF_TEXT)) //指定格式數據在接貼板上是否存在
{
HANDLE hClip=GetClipboardData(CF_TEXT); //從剪貼板上得到數據,且拿到了數據塊的句柄
char* pBuf;
pBuf=(char*)GlobalLock(hClip);
GlobalUnlock(hClip);
SetDlgItemText(IDC_EDIT_RECV,pBuf);
}
CloseClipboard();
}
2. what is the pipes
A pipe is a section of shared memory that processes use for communication. The process that creates a pipe is the pipe server. A process that connects to a pipe is a pipe client. One process writes information to the pipe, then the other process reads the information from the pipe. This overview describes how to create, manage, and use pipes.
3. 匿名管道
創建父進程:
a. CreatePipe:其中第三個參數代表安全屬性結構體SECURITY_ATTRIBUTES
的指針,在前幾章的運用中,都是運用了NULL,代表返回的安全句柄不可以被子進程所繼承。但在本運用中,涉及到的是匿名管道。匿名管道就是父子進程之間的通信,所以結構體必須設置相應的值。子進程要想獲得匿名管道的讀寫句柄,只能從父進程繼承而來。一旦子進程有了繼承而來的讀寫句柄,就可以和父進程進行通信了。對於機構體SECURITY_ATTRIBUTES
,最重要的是第三個參數
bInheritHandle,表示
Specifies whether the returned handle is inherited when a new process is created. If this member is TRUE, the new process inherits the handle.
b. CreateProcess:如果創建管道成功,則創建子進程,並將管道的讀寫句柄傳遞給子進程。
1.MFC單文檔程序菜單中增加創建管道,讀取數據,寫入數據三項
2.View類中增加成員變量
3. CreateProcess具見 00:51:30
倒數第三個參數 [in] Pointer to a STARTUPINFO structure that specifies how the main window for the new process
should appear 指向STARTUPINFO結構體的一個指針,用來指定新的進程它的主窗口如何出現
最后一個參數指向PROCESS_INFORMATION結構體的一個指針的返回值,用來接收新的進程的標識信息
PROCESS_INFORMATION
The PROCESS_INFORMATION structure is filled in by the CreateProcess function with information about a newly created process and its primary thread.
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
} PROCESS_INFORMATION;
Members
hProcess //新創建進程的句柄
A handle to the newly created process. The handle is used to specify the process in all functions that perform operations on the process object.
hThread //新創建進程主線程的句柄
A handle to the primary thread of the newly created process. The handle is used to specify the thread in all functions that perform operations on the thread object.
dwProcessId //全局進程標識符,如果進程結束操作系統可能會將標識分配給其他進程,我們調用標識的時候要確保進程在運行
A global process identifier that can be used to identify a process. The value is valid from the time the process is created until the time the process is terminated.
dwThreadId //全局線程標識符
A global thread identifiers that can be used to identify a thread. The value is valid from the time the thread is created until the time the thread is terminated.
創建匿名管道具體代碼:
SECURITY_ATTRIBUTES sa;
//總共就三個參數
sa.bInheritHandle=TRUE; //表示可被子進程所繼承
sa.lpSecurityDescriptor=NULL; //安全描述符號一般都設置成NULL,即默認描述符
sa.nLength=sizeof(SECURITY_ATTRIBUTES); //管道長度
if(!CreatePipe(&hRead,&hWrite,&sa,0))
{
MessageBox("創建匿名函數失敗!");
return;
}
//管道創建成功后,接着創建子進程,並將讀寫句柄傳遞給子進程
STARTUPINFO sui;
PROCESS_INFORMATION pi;
//調用ZeroMemory方法將該結構體中的所有成員都置為0,這是因為這個結構體的成員很多,如果開始的時候沒有置為0的話,那它的值是隨機的,將這樣的結構體傳給CreateProcess,可能會影響到執行的結果。
ZeroMemory(&sui,sizeof(STARTUPINFO));
sui.cb=sizeof(STARTUPINFO); //設置結構體的大小
sui.dwFlags=STARTF_USESTDHANDLES; //該標識表示標准輸入句柄,標准輸出句柄和錯誤句柄是有用的
sui.hStdInput=hRead; //將子進程的輸入句柄設置成父進程的讀句柄
sui.hStdOutput=hWrite; //將子進程的輸出句柄設置成父進程的寫句柄
sui.hStdError=GetStdHandle(STD_ERROR_HANDLE); //得到標准錯誤句柄,是父進程的錯誤句柄,該行代碼在本程序中沒有實際的用途意義
//因為是匿名管道,是沒有名稱的管道,只有通過CreateProcess由上而下的傳遞管道操作句柄。
if(!CreateProcess("..//Child//Debug//Child.exe",NULL,NULL,NULL,
TRUE,0,NULL,NULL,&sui,&pi))
{
MessageBox("創建子進程失敗!");
CloseHandle(hRead);
CloseHandle(hWrite);
//避免在析構函數中再次關閉,析構函數采用:
//if(hRead) CloseHandle(hRead)
hRead=NULL;
hWrite=NULL;
return;
}
else
{
//創建一個新的進程的時候,系統會創建一個進程內核對象和一個線程內核對象,內核對象都有一個使用基數,初始調用的時候,都設置為1。在CreateProcess返回之前,該函數打開進程和線程的內核對象,,並將進程相關的句柄放置到結構體PROCESS_INFORMATION的hProcess和hThread中,當Process在內部打開這些對象的時候,使得每個對象的使用基數增加到2了。如果在父進程中不需要使用這兩個句柄,就將這個句柄進行關閉,使得使用基數減1。當子進程終結的時候,系統會在將使用基數減1,使得子進程的進程內核對象和線程內核對象的使用基數變為0,這樣內核對象就可以被釋放了。
CloseHandle(pi.hProcess); //關閉子進程的句柄
CloseHandle(pi.hThread); //關閉子進程中主線程的句柄
}
父進程寫匿名管道:
char *buf="hello world";
DWORD dwWrite;
if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox("匿名管道寫入數據失敗!");
return;
}
父進程讀匿名管道:
char buf[100];
DWORD dwRead;
if(!ReadFile(hRead,buf,100,&dwRead,NULL))
{
MessageBox("匿名管道讀取數據失敗!");
return;
}
MessageBox(buf);
創建子進程程序:
可以將獲取父進程的匿名管道的讀寫句柄操作放在CView類的OnInitialUpdate方法中實現,該方法是在CView完全構造后調用的第一個方法。代碼如下:
hRead=GetStdHandle(STD_INPUT_HANDLE);
hWrite=GetStdHandle(STD_OUTPUT_HANDLE);
子進程的讀寫匿名管道的代碼和父進程的一樣,這里不再累述。
4. 命名管道:
命名管道是通過網絡來完成進程間的通信,它屏蔽了底層的網絡協議細節。我們在不了解網絡協議的情況下,也可以利用命名管道來實現進程間的通信。而上述的匿名管道只能在本地機器上,且連個父子進程間通信。命名管道也具有匿名管道的功能。
命名管道服務器和客戶機的區別在於:服務器是唯一一個有權創建命名管道的進程,也只有它才能接受管道客戶機的連接請求。而客戶機只能同一個現成的命名管道服務器建立連接。
命名管道提供了兩種基本通信模式:字節模式和消息模式。在字節模式中,數據以一個連續的字節流的形式,在客戶機和服務器之間流動。而在消息模式中,客戶機和服務器則通過一系列不連續的數據單位,進行數據的收發,每次在管道上發出了一條消息后,它必須作為一條完整的消息讀入。
命名管道服務器端代碼(核心為命名管道的創建與等待客戶端的連接):
說明:CreateNamedPipe,創建命名管道,其中第一個參數管道的名稱是格式為"//./pipe/pipename", 在VC中使用的時候,因涉及到轉義符,作為字符串,應使用"////.//pipe//pipename",其中pipe不能更改,大小寫沒有區分
hPipe=CreateNamedPipe("////.//pipe//MyPipe",PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,0,1,1024,1024,0,NULL);
if(INVALID_HANDLE_VALUE==hPipe)
{
MessageBox("創建命名管道失敗!");
CloseHandle(hPipe);
hPipe=NULL;
return;
}
HANDLE hEvent;
hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
if(!hEvent)
{
MessageBox("創建事件對象失敗!");
CloseHandle(hPipe);
hPipe=NULL;
return;
}
OVERLAPPED ovlap;
//這里調用ZeroMemory和上一章的意義是一樣的,為了避免ConnectNamedPipe在調用該結構中使用的是一些不可欲知的參數值,防止有影響
ZeroMemory(&ovlap,sizeof(OVERLAPPED));
ovlap.hEvent=hEvent;
說明:等待客戶端連接到一個命名管道實例。If hNamedPipe was created with FILE_FLAG_OVERLAPPED and lpOverlapped is not NULL, the OVERLAPPED structure pointed to by lpOverlapped must contain a handle to a manual-reset event object (which the server can create by using the CreateEvent function).這是為什么上面需要申明一個自動的事件的對象。
if(!ConnectNamedPipe(hPipe,&ovlap))
{
if(ERROR_IO_PENDING!=GetLastError())
{
MessageBox("等待客戶端的連接失敗!");
CloseHandle(hPipe);
CloseHandle(hEvent);
hPipe=NULL;
}
}
//等待事件狀態有效,如果當前無效,INFINITE參數表明則一直等待下去
if(WAIT_FAILED==WaitForSingleObject(hEvent,INFINITE))
{
MessageBox("等待對象失敗!");
CloseHandle(hPipe);
CloseHandle(hEvent);
hPipe=NULL;
}
CloseHandle(hEvent); //說明已經有客戶端連接到了
命名管道的讀寫和上一章是類似的,這里就省略掉了
命名管道的客戶端實現(核心為命名管道的連接):
//WaitNamePipe的參數NMPWAIT_WAIT_FOREVER一直等待下去,直到等待到可用的連接,當然也可以設置超時的時間,但前提是所有的程序里所有的命名管道的超時時間必須一樣
if(!WaitNamedPipe("////.//pipe//MyPipe",NMPWAIT_WAIT_FOREVER))
{
MessageBox("當前沒有可用的命名管道實例");
return;
}
//打開命名管道,建立連接
hPipe=CreateFile("////127.0.0.1//pipe//MyPipe",GENERIC_READ|GENERIC_WRITE, 0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(INVALID_HANDLE_VALUE==hPipe)
{
MessageBox("打開命名管道失敗!");
hPipe=NULL;
}
注意:程序的運行方式為先點擊服務端的創建命名管道,然后點擊客戶端的連接管道,再點擊服務端的發送數據,再在客戶端點擊接收數據。
5. 郵槽:
Ø 郵槽是基於廣播通信體系設計出來的,它采用無連接的不可靠的數據傳輸。
Ø 郵槽是一種單向通信機制,創建郵槽的服務器進程讀取數據,打開郵槽的客戶機進程寫入數據。
Ø 為保證郵槽在各種Windows平台下都能夠正常工作,我們傳輸消息的時候,應將消息的長度限制在424字節以下。
服務器端的代碼實現:
HANDLE hMailslot;
hMailslot=CreateMailslot("////.//mailslot//MyMailSlot",0,MAILSLOT_WAIT_FOREVER,NULL);
if(INVALID_HANDLE_VALUE==hMailslot)
{
MessageBox("創建郵槽失敗!");
return;
}
char buf[100];
DWORD dwRead;
if(!ReadFile(hMailslot,buf,100,&dwRead,NULL))
{
MessageBox("讀取數據失敗!");
CloseHandle(hMailslot);
hMailslot=NULL;
return;
}
MessageBox(buf);
CloseHandle(hMailslot);
客戶端的代碼實現:
HANDLE hMailslot;
//因為郵槽的客戶是負責寫入數據,所以訪問方式只需要是寫,但對於服務器端而言,是讀取數據,所以客戶端共享方式設置為讀FILE_SHARE_READ
hMailslot=CreateFile("////.//mailslot//MyMailSlot",GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(INVALID_HANDLE_VALUE==hMailslot)
{
MessageBox("打開郵槽失敗!");
return;
}
char buf[]="this is test!";
DWORD dwWrite;
if(!WriteFile(hMailslot,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox("寫入數據失敗");
CloseHandle(hMailslot);
return;
}
CloseHandle(hMailslot);
注意:程序的運行方式為先點擊服務器端的接收數據以此創建一個郵槽,然后在點擊客戶端的發送數據,最終服務器端會接收到數據。