• 文章
  • 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)。