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