• Articles
  • Named Pipes for data exchange in Windows
Published by
May 20, 2009 (last update: May 20, 2009)

Named Pipes for data exchange in Windows Vista

Score: 3.1/5 (30 votes)
*****
Author:
Yuri Maxiutenko,
Software Developer of Apriorit Inc.

This article is devoted to the question about working with services and applications in Windows Vista. In particular we’ll consider how to organize the data exchange between the service and applications. There are a number of different approaches but we will talk about named pipes.

If we talk about big volumes of data in exchange process beteen the application and service in Windows Vista we can use named pipe technology. We must mention that the code below is provided in C++. The classes for working with the named pipes in C# were also introduced but only since .NET Framework 3.5. If you want to know how to use these new .NET tools for working with the named pipes you can read, for example, this article:
http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/23dc2951-8b59-48e4-89fe-d2b435db48c6
Let’s suppose that an application periodically needs to send some number of unsigned int type to the service.
In this case we can open the named pipe on the service side and then monitor its state in the separated thread to read and process data when they come. So we create the pipe DataPipe in the service code:
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;
}

We also create the function to check the thread state and perform reading if required:
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);
	}
}

And finally start the separate thread with ThreadFunction() function:
1
2
3
unsigned int id = 0;
HANDLE pipeHandle = CreatePipe();
::CloseHandle((HANDLE)::_beginthreadex(0, 0, ThreadFunction, (void*) pipeHandle, 0, &id));


Now we go to the application side and organize sending of data to the service via named pipe.
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;
}


To learn this question deeper and a lot of features of developing for Windows Vista we also recommend the book by Michael Howard, David LeBlanc - Writing Secure Code for Windows Vista (Microsoft Press, 2007).