• 文章
  • Windows 中的命名管道用於資料交換
釋出
2009年5月20日 (最後更新:2009年5月20日)

Windows Vista 中的命名管道用於資料交換

評分:3.1/5 (30 票)
*****
作者
Yuri Maxiutenko,
Apriorit Inc. 軟體開發人員

本文主要介紹在 Windows Vista 中與服務和應用程式協同工作的問題。特別是,我們將探討如何組織服務和應用程式之間的資料交換。有多種方法可以實現這一點,但我們將重點介紹命名管道。

如果我們在 Windows Vista 中,應用程式和服務之間需要交換大量資料,我們可以使用命名管道技術。需要指出的是,下面的程式碼是用 C++ 編寫的。雖然 C# 中也引入了用於處理命名管道的類,但僅限於 .NET Framework 3.5 及更高版本。如果您想了解如何使用這些新的 .NET 工具來處理命名管道,可以閱讀例如這篇文章
http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/23dc2951-8b59-48e4-89fe-d2b435db48c6
假設一個應用程式需要定期向服務傳送一些無符號整數型別的資料。
在這種情況下,我們可以在服務端開啟命名管道,然後在單獨的執行緒中監視其狀態,以便在資料到達時進行讀取和處理。因此,我們在服務程式碼中建立 DataPipe 管道
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
HANDLE CreatePipe()
{
	SECURITY_ATTRIBUTES sa;
	sa.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR)malloc(SECURITY_DESCRIPTOR_MIN_LENGTH);
	if (!InitializeSecurityDescriptor(sa.lpSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION))
	{
		DWORD er = ::GetLastError();
	}
	if (!SetSecurityDescriptorDacl(sa.lpSecurityDescriptor, TRUE, (PACL)0, FALSE))
	{
		DWORD er = ::GetLastError();
	}
	sa.nLength = sizeof sa;
	sa.bInheritHandle = TRUE;

// To know the maximal size of the received data for reading from the      // pipe buffer

	union maxSize
	{
		UINT   _1;
	};
	HANDLE hPipe = ::CreateNamedPipe((LPSTR)"\\\\.\\pipe\\DataPipe", PIPE_ACCESS_INBOUND, 
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, sizeof maxSize, sizeof maxSize,
 NMPWAIT_USE_DEFAULT_WAIT, &sa);
	if (hPipe == INVALID_HANDLE_VALUE)
	{
		DWORD dwError = ::GetLastError();
	}
	return hPipe;
}

我們還建立一個函式來檢查執行緒狀態並在需要時執行讀取操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
unsigned int __stdcall ThreadFunction(HANDLE& hPipe)
{
	while (true)
	{
		BOOL  bResult = ::ConnectNamedPipe(hPipe, 0);
		DWORD dwError = GetLastError();

		if (bResult || dwError == ERROR_PIPE_CONNECTED)
		{
			BYTE  buffer[sizeof UINT] = {0};
			DWORD read = 0;

			UINT   uMessage = 0;

			if (!(::ReadFile(hPipe, &buffer, sizeof UINT, &read, 0)))
			{
				unsigned int error = GetLastError();
			}
			else
			{
				uMessage = *((UINT*)&buffer[0]);
				// The processing of the received data
			}
			::DisconnectNamedPipe(hPipe);
		}
		else
		{

		}
		::Sleep(0);
	}
}

最後,我們透過 ThreadFunction() 函式啟動一個單獨的執行緒
1
2
3
unsigned int id = 0;
HANDLE pipeHandle = CreatePipe();
::CloseHandle((HANDLE)::_beginthreadex(0, 0, ThreadFunction, (void*) pipeHandle, 0, &id));


現在我們轉向應用程式端,並組織透過命名管道向服務傳送資料。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
SendDataToService(UINT message)
{
	HANDLE hPipe = INVALID_HANDLE_VALUE;
	DWORD  dwError = 0;
	while (true) 
	{ 
		hPipe = ::CreateFile((LPSTR)"\\\\.\\pipe\\DataPipe", GENERIC_WRITE, 0, 0, 
OPEN_EXISTING, 0, 0);
		dwError = GetLastError();
		if (hPipe != INVALID_HANDLE_VALUE)
{
break;
} 
			
		// If any error except the ERROR_PIPE_BUSY has occurred,
            // we should return FALSE. 
		if (dwError != ERROR_PIPE_BUSY) 
		{
			return FALSE;
		}
		// The named pipe is busy. Let’s wait for 20 seconds. 
		if (!WaitNamedPipe((LPSTR)"\\\\.\\pipe\\DataPipe", 20000)) 
		{ 
			dwError = GetLastError();
			return FALSE;
		} 
	} 
	DWORD dwRead = 0;
	if (!(WriteFile(hPipe, (LPVOID)&message, sizeof UINT, &dwRead, 0)))
	{
		CloseHandle(hPipe);
		return FALSE;
	}
	CloseHandle(hPipe);
	::Sleep(0);
	return TRUE;
}


為了更深入地瞭解此問題以及 Windows Vista 開發的許多功能,我們還推薦 Michael Howard 和 David LeBlanc 的著作“Writing Secure Code for Windows Vista”(Microsoft Press,2007)。