作者
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)。