ATA command device IDENTIFY - windows

I am trying to identify a device using ATA_PASS_THROUGH_EX.
When I see the output buffer, it has all invalid data. Can someone help me what I am doing wrong?
#include <Windows.h>
#include <ntddscsi.h>
#include <iostream>
void main() {
WCHAR *fileName = (WCHAR * ) "\\.\PhysicalDrive0";
HANDLE handle = CreateFile(
fileName,
GENERIC_READ | GENERIC_WRITE, //IOCTL_ATA_PASS_THROUGH requires read-write
FILE_SHARE_READ,
NULL, //no security attributes
OPEN_EXISTING,
0, //flags and attributes
NULL //no template file
);
ATA_PASS_THROUGH_EX inputBuffer;
inputBuffer.Length = sizeof(ATA_PASS_THROUGH_EX);
inputBuffer.AtaFlags = ATA_FLAGS_DATA_IN;
inputBuffer.DataTransferLength = 0;
inputBuffer.DataBufferOffset = 0;
IDEREGS *ir = (IDEREGS *) inputBuffer.CurrentTaskFile;
ir->bCommandReg = 0xEC; //identify device
ir->bSectorCountReg = 1;
unsigned int inputBufferSize = sizeof(ATA_PASS_THROUGH_EX);
UINT8 outputBuffer[512];
UINT32 outputBufferSize = 512;
LPDWORD bytesReturned = 0;
DeviceIoControl( handle, IOCTL_ATA_PASS_THROUGH_DIRECT, &inputBuffer, inputBufferSize, &outputBuffer, outputBufferSize, bytesReturned, NULL);
DWORD error = GetLastError();
std::cout << outputBuffer << std::endl;
system("pause");
}
update:
When I check the error value, it is 5, which means it is an access violation. I am running in admin mode. Am I doing something wrong?
-Nick

I've done this using code that looks like this:
int foo()
{
int iRet( 0 );
// Open handle to disk.
HANDLE hDevice( ::CreateFileW( L"\\\\.\\PhysicalDrive0", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ) );
if( hDevice == INVALID_HANDLE_VALUE )
{
std::wcout << L"CreateFileW( " << sPath << L" ) failed. LastError: " << GetLastError() << std::endl;
return -1;
}
//
// Use IOCTL_ATA_PASS_THROUGH
//
std::vector< UCHAR > vBuffer( sizeof( ATA_PASS_THROUGH_EX ) + sizeof( IDENTIFY_DEVICE_DATA ), 0 );
PATA_PASS_THROUGH_EX pATARequest( reinterpret_cast< PATA_PASS_THROUGH_EX >( &vBuffer[0] ) );
pATARequest->AtaFlags = ATA_FLAGS_DATA_IN | ATA_FLAGS_DRDY_REQUIRED;
pATARequest->Length = sizeof( ATA_PASS_THROUGH_EX );
pATARequest->DataBufferOffset = sizeof( ATA_PASS_THROUGH_EX );
pATARequest->DataTransferLength = sizeof( IDENTIFY_DEVICE_DATA );
pATARequest->TimeOutValue = 2;
pATARequest->CurrentTaskFile[6] = ID_CMD;
ULONG ulBytesRead;
if( DeviceIoControl( hDevice, IOCTL_ATA_PASS_THROUGH,
&vBuffer[0], ULONG( vBuffer.size() ),
&vBuffer[0], ULONG( vBuffer.size() ),
&ulBytesRead, NULL ) == FALSE )
{
std::cout << "DeviceIoControl(IOCTL_ATA_PASS_THROUGH) failed. LastError: " << GetLastError() << std::endl;
iRet = -1;
}
else
{
// Fetch identity blob from output buffer.
PIDENTIFY_DEVICE_DATA pIdentityBlob( reinterpret_cast< PIDENTIFY_DEVICE_DATA >( &vBuffer[ sizeof( ATA_PASS_THROUGH_EX ) ] ) );
}
CloseHandle( hDevice );
return iRet;
}
Note that this must be run from an administrator account or elevated context.

Related

How find the disk sector size of a drive that's on a UNC-path

I've got the following code to get the drive sector size of a local or mapped drive:
#include <Windows.h>
#include <stdexcept>
#include <sstream>
#include <string>
#include "xhandle.h"
#include "DiskSectorSize.h"
DiskSectorSize getDiskSectorSize( char const *filename )
{
using namespace std;
DWORD dwFfnLength;
if( (dwFfnLength = GetFullPathNameA( filename, 0, nullptr, nullptr )) == 0 )
throw system_error( error_code( (int)GetLastError(), system_category() ),
(stringstream() << "can't get full path for path \"" << filename << "\"").str().c_str() );
string fullFileName( (size_t)dwFfnLength, 0 );
if( GetFullPathNameA( filename, dwFfnLength, &fullFileName[0], nullptr ) == 0 )
throw system_error( error_code( (int)GetLastError(), system_category() ),
(stringstream() << "can't get full path for path" << filename).str().c_str() );
if( dwFfnLength < 2 || toupper( fullFileName[0] ) < 'A' || toupper( fullFileName[0] ) > 'Z' || fullFileName[1] != ':' )
throw exception( "no drive letter in absolute path" );
string logicalDrive = "\\\\.\\ ";
logicalDrive.replace( logicalDrive.begin() + 4, logicalDrive.begin() + 6, fullFileName.begin(), fullFileName.begin() + 2 );
XHANDLE xhDevice( CreateFileA( logicalDrive.c_str(), 0, 0, NULL, OPEN_EXISTING, 0, NULL ), true,
"can't create device handle for disk" );
DWORD dwOutSize;
STORAGE_PROPERTY_QUERY spq;
memset( &spq, 0, sizeof spq);
spq.PropertyId = StorageAccessAlignmentProperty;
spq.QueryType = PropertyStandardQuery;
STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR saad;
memset( &saad, 0, sizeof saad );
if( !DeviceIoControl( xhDevice, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof spq, &saad, sizeof saad, &dwOutSize, NULL ) ||
dwOutSize != sizeof saad )
throw system_error( error_code( (int)GetLastError(), system_category() ),
(stringstream() << "can't detemine disk sector size for device" << fullFileName).str().c_str() );
DiskSectorSize dss;
dss.dwPhysical = saad.BytesPerPhysicalSector;
dss.dwLogical = saad.BytesPerLogicalSector;
return dss;
}
But I can't get the disk sector size of a UNC-file with at. When I wrote the above code I also found a way to do that. But today I can't remember it exactly.

Null when accessing the Access property of a Win32_Volume instance

As a way to resolve GetVolumeInformation() not reporting FILE_READ_ONLY_VOLUME for a locked SD Card without any file-system, I found that the Win32_Volume class has an Access uint16 property that:
Describes whether the media is readable. This property is inherited from CIM_StorageExtent. This can be one of the following values.
I'm interacting with WMI from a C++ program, so when reading the Access property I get a VARIANT structure with the result.
According to the docs, a VARIANT contains an union, so I should first check the vt property to determine its type. vt is a VARTYPE, which is an int according to the docs. The vt value I get after accessing the Access property is 1, which again according to the docs, is null. I can confirm this result by trying to access most of the union members, which are not set at all.
Here's a complete runnable example (run with cl /EHsc test.cpp):
#include <iostream>
#include <comdef.h>
#include <Wbemidl.h>
#pragma comment(lib, "wbemuuid.lib")
int main(int argc, char **argv) {
IWbemLocator *locator = NULL;
IWbemServices *services = NULL;
IEnumWbemClassObject* enumerator = NULL;
IWbemClassObject *classObject = NULL;
if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) return 1;
if (FAILED(CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL))) return 1;
if (FAILED(CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)&locator))) return 1;
if (FAILED(locator->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &services))) return 1;
if (FAILED(CoSetProxyBlanket(services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE))) return 1;
if (FAILED(services->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_Volume"), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &enumerator))) return 1;
while (enumerator) {
ULONG code = 0;
if (FAILED(enumerator->Next(WBEM_INFINITE, 1, &classObject, &code))) return 1;
if (code == 0) break;
VARIANT variant;
if (FAILED(classObject->Get(L"DriveLetter", 0, &variant, NULL, NULL))) return 1;
if (variant.bstrVal == NULL) {
VariantClear(&variant);
classObject->Release();
continue;
};
std::wcout << " DriveLetter : " << variant.bstrVal[0] << std::endl;
VariantClear(&variant);
if (FAILED(classObject->Get(L"Access", 0, &variant, NULL, NULL))) return 1;
std::wcout << "Access VARTYPE -> " << variant.vt << std::endl;
VariantClear(&variant);
classObject->Release();
}
enumerator->Release();
services->Release();
locator->Release();
CoUninitialize();
return 0;
}
This is an example output from my system (Windows 10 Pro x86_64):
DriveLetter : C
Access VARTYPE -> 1
DriveLetter : F
Access VARTYPE -> 1
DriveLetter : E
Access VARTYPE -> 1
I can access other string properties, like DriveLetter, just fine, which makes me think I'm doing something wrong with this particular property.
UPDATE 1: Looks like I get the same results with any uint16 property, but not with uint32, nor uint64, which seem to work just fine.

Windows: How to stop buffering of redirected Stdout using CreateProcess

I am using pipes to get redirected stdout output from a command line executable. Unfortunately I don't get any output until the process has completed. The executable outputs progress status as it runs and this is what I would like to parse.
BOOL RunCmd( char *pCmd,
char *pWorkingDir,
int nWaitSecs,
BOOL fRegImport,
DWORD *pdwExitCode )
{
BOOL fSuccess = TRUE;
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sFileSecurity;
ZeroMemory( &sFileSecurity, sizeof( sFileSecurity ) );
sFileSecurity.nLength = sizeof( sFileSecurity );
sFileSecurity.bInheritHandle = TRUE;
HANDLE hReadPipe = NULL;
HANDLE hWritePipe = NULL;
fSuccess = CreatePipe( &hReadPipe, &hWritePipe, &sFileSecurity, 0 );
SetHandleInformation( hReadPipe, HANDLE_FLAG_INHERIT, 0 );
ZeroMemory( &si, sizeof(si) );
ZeroMemory( &pi, sizeof(pi) );
si.cb = sizeof( si );
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdOutput = hWritePipe;
si.hStdError = hWritePipe;
si.wShowWindow = SW_HIDE;
int rc;
// Start the child process.
rc = CreateProcess( NULL, // No module name (use command line).
pCmd, // Command line.
NULL, // Process handle not inheritable.
NULL, // Thread handle not inheritable.
TRUE,
CREATE_NO_WINDOW,
NULL, // Use parent's environment block.
pWorkingDir, // Working folder
&si, // Pointer to STARTUPINFO structure.
&pi ); // Pointer to PROCESS_INFORMATION structure.
if( ! rc )
return FALSE;
// Wait until child process exits.
DWORD dwWaitResult;
DWORD dwTimeStart = ::GetTickCount();
DWORD dwTimeNow;
#define BUFSIZE 4096
DWORD dwRead = 0;
DWORD dwAvail;
CHAR chBuf[ BUFSIZE ];
BOOL bSuccess = TRUE;
for( ;; )
{
dwTimeNow = ::GetTickCount();
dwWaitResult = ::WaitForSingleObject( pi.hProcess, ONE_SECOND );
dwRead = 0;
for( dwAvail = 0; PeekNamedPipe( hReadPipe, 0, 0, 0, &dwAvail, 0 ) && dwAvail; dwAvail = 0 )
{
dwRead = 0;
ReadFile( hReadPipe, chBuf, min( BUFSIZE, dwAvail ), &dwRead, NULL );
if( dwRead > 0 )
{
FILE *op = fopen( "c:\\REDIR.OUT", "a" );
if( op )
{
fwrite( chBuf, 1, dwRead, op );
fclose( op );
}
}
}
if( dwWaitResult == WAIT_OBJECT_0 )
{
DWORD dwExitCode;
GetExitCodeProcess( pi.hProcess, &dwExitCode );
if( pdwExitCode )
(*pdwExitCode) = dwExitCode;
break;
}
if( dwWaitResult == WAIT_TIMEOUT )
{
if( dwTimeNow - dwTimeStart < (DWORD)( ONE_SECOND * nWaitSecs ) )
continue;
else
{
fSuccess = FALSE;
break;
}
}
fSuccess = FALSE;
break;
}
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
CloseHandle( hReadPipe );
CloseHandle( hWritePipe );
return fSuccess;
}
The PeekNamedPipe() call is called every second and dwAvail is zero every time until the process completes.
How do I get output from the process sooner? When running the process from the console, I see progress output as it goes. The process will be using "\r" in it's output to display a percentage at the start of the same line.
Note: My answer only deals with executables compiled using MSVC.
The buffering policy is coded inside Microsoft C Runtime (CRT) Library. You can learn the details here. This article suggests using console handles and manipulate console buffers to receive unbuffered output.
However, there's an undocumented feature inside Microsoft C Runtime to inherit file handles with some internal flags directly from its parent process using lpReserved2 and cbReserved2 fields of STARTUPINFO structure. You can find the details in the crt source code provided by Microsoft Visual Studio. Or search for something like posfhnd on GitHub.
We can exploit this undocumented feature to provide a pipe handle and specify FOPEN | FDEV flags to the child process, to fool the child process treat that pipe handle the same way as a FILE_TYPE_CHAR handle.
I have a working Python3 script to demonstrate this method.
A simplified C/C++ version of the answer by #youfu.
STARTUPINFO si;
int nh = 2;
si.cbReserved2 = (WORD)(sizeof(int) + (nh *
(sizeof(char) + sizeof(HANDLE))));
si.lpReserved2 = (LPBYTE) calloc(si.cbReserved2, 1);
*((UNALIGNED int *)(si.lpReserved2)) = nh;
unsigned char* posfile = (unsigned char *)(si.lpReserved2 + sizeof(int));
UNALIGNED HANDLE* posfhnd = (UNALIGNED HANDLE *)(si.lpReserved2 + sizeof(int) +
(nh * sizeof(unsigned char)));
unsigned char FOPEN = 0x01;
unsigned char FDEV = 0x40;
*posfile = FOPEN | FDEV;
posfile++;
*posfile = FOPEN | FDEV;
*posfhnd = child_stdin_rd;
posfhnd++;
*posfhnd = child_stdout_wr;
-Suraj

How to use the GetLastError function in a Win32 Console Application?

I am trying to open a Registry Key using the RegOpenKeyEx function from the Windows API, and have this code:
#include <windows.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
int wmain(int argc, wchar_t*argv [])
{
HKEY hKey = HKEY_CURRENT_USER;
LPCTSTR lpSubKey = L"Demo";
DWORD ulOptions = 0;
REGSAM samDesired = KEY_ALL_ACCESS;
HKEY phkResult;
long R = RegOpenKeyEx(hKey, lpSubKey, ulOptions, samDesired, &phkResult);
if (R == ERROR_SUCCESS)
{
cout << "The registry key has been opened." << endl;
}
else //How can I retrieve the standard error message using GetLastError() here?
{
}
}
How do I use the GetLastError() function to show a generic error message instead of valid any Error Message ID into the else?
Edit: I know there is a FormatMessage function but have the same problem, I don't know how to use it on my code.
The Registry functions do not use GetLastError(). They return the actual error codes directly:
long R = RegOpenKeyEx(hKey, lpSubKey, ulOptions, samDesired, &phkResult);
if (R == ERROR_SUCCESS)
{
cout << "The registry key has been created." << endl;
}
else
{
cout << "The registry key has not been created. Error: " << R << endl;
}
If you want to display a system error message, use FormatMessage() for that:
long R = RegOpenKeyEx(hKey, lpSubKey, ulOptions, samDesired, &phkResult);
if (R == ERROR_SUCCESS)
{
cout << "The registry key has been created." << endl;
}
else
{
char *pMsg = NULL;
FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL,
R,
0,
(LPSTR)&pMsg,
0,
NULL
);
cout << "The registry key has not been created. Error: (" << R << ") " << pMsg << endl;
LocalFree(pMsg);
}
Try this
HKEY hKey = HKEY_CURRENT_USER;
LPCTSTR lpSubKey = L"Demo";
DWORD ulOptions = 0;
REGSAM samDesired = KEY_ALL_ACCESS;
HKEY phkResult;
char *ErrorMsg= NULL;
long R = RegOpenKeyEx(hKey, lpSubKey, ulOptions, samDesired, &phkResult);
if (R == ERROR_SUCCESS)
{
printf("The registry key has been opened.");
}
else //How can I retrieve the standard error message using GetLastError() here?
{
FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL,
R,
0,
(LPSTR)&ErrorMsg,
0,
NULL
);
printf("Error while creating Reg key.");
}

Problem reconnecting to the named pipe

I have a named pipe server and client. (Doing this in VC++).
Server does
CreateNamedPipe
ConnectNamedPipe
WriteFile
Disconnect
Repeat from 2 to 4
Client does
CreateFile
ReadFile
The order of execution is as follows,
Server -- CreateNamedPipe
Client -- CreateFile
Server -- ConnectNamedPipe (should return immediately as the client is already connected)
Server -- WriteFile
Client -- ReadFile
Server -- DisconnectNamedPipe
Client -- CloseHandle
goto 2
This works fine for the first time. However problem occurs when client tries to connects for the second time. When the client tries to connect (CreateFile) for the second time before the server did ConnectNamedPipe (but after disconnectnamedpipe), it gets ERROR_PIPE_BUSY. It works if client calls createfile after the server calls ConnectNamedPipe.
Is there anyway that i can get client connected (CreateFile) before server called ConnectNamedPipe (after DisconnectNamedPipe)?
Server code:
pipe_handle.pipe = CreateNamedPipe(TEXT("\\\\.\\pipe\\testpipe1"),
PIPE_ACCESS_OUTBOUND |
FILE_FLAG_OVERLAPPED, // read/write access
PIPE_TYPE_MESSAGE | // message type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFFER_SIZE, // output buffer size
BUFFER_SIZE, // input buffer size
2000, // client time-out
NULL);
if (pipe_handle.pipe == INVALID_HANDLE_VALUE) {
std::cout << "Error while creating pipe" << std::endl;
return -1;
}
std::cout <<"Connecting to named pipe" << std::endl;
std::cout<< "Somebody connected to named pipe" << std::endl;
int ac;
for (ac=0; ac<2; ac++) {
char a[25];
// Wait for some input. This helps me to start the client in other terminal.
cin >> a;
cout << "Connecting..." << endl;
ConnectNamedPipe(pipe_handle.pipe, 0);
cout << "Connect pipe returned." << endl;
// Wait for some input.
cin >> a;
string message = "Test message";
DWORD bytes_written;
if (!WriteFile(pipe_handle.pipe, message.c_str(), message.size(),
&bytes_written, NULL)) {
DWORD er = GetLastError();
char errs[200];
sprintf(errs, "Error : %ld", er);
std::cout << "Error communicating to client.";
std::cout << errs;
}
std::cout << "Written to pipe";
FlushFileBuffers(pipe_handle.pipe);
if (!DisconnectNamedPipe(pipe_handle.pipe)) {
std::cout << "Disconnect failed"<< GetLastError() << endl;
} else {
std::cout << "Disconnect successful"<<endl;
}
}
Client Code:
while (1) {
std::cout << "Returned" << std::endl;
hPipe = CreateFile(
lpszPipename, // pipe name
GENERIC_READ,
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
FILE_FLAG_OVERLAPPED, // default attributes
NULL); // no template file
// Break if the pipe handle is valid.
if (hPipe != INVALID_HANDLE_VALUE)
break;
// Exit if an error other than ERROR_PIPE_BUSY occurs.
if (GetLastError() != ERROR_PIPE_BUSY) {
std::cout<< "Could not open pipe " << GetLastError() << std::endl;
return -1;
}
// All pipe instances are busy, so wait for sometime.
if ( ! WaitNamedPipe(lpszPipename, NMPWAIT_USE_DEFAULT_WAIT)) {
std::cout<< "Could not open pipe: wait timed out." << std::endl;
}
}
OVERLAPPED ol1;
memset(&ol1, 0, sizeof(ol1));
ol1.Offset = 0;
ol1.OffsetHigh = 0;
ol1.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
HANDLE events[1];
events[0] = ol1.hEvent;
cbToWrite = (lstrlen(message)+1)*sizeof(TCHAR);
DWORD bytes_to_read = 2000;
char * buf = reinterpret_cast<char *>(malloc(bytes_to_read));
DWORD bytes_read;
std::cout << "Waiting for read" << std::endl;
bool a = ReadFile(hPipe, buf, bytes_to_read, &bytes_read, &ol1);
if ( ! fSuccess) {
std::cout << "WriteFile to pipe failed. GLE " << GetLastError() << std::endl;
}
std::cout << "Waiting for multiple objects" << std::endl;
WaitForMultipleObjects(1, events, FALSE, INFINITE);
std::cout << "multiple objects returned" << std::endl;
printf("\nMessage sent to server");
CancelIo(hPipe);
CloseHandle(hPipe);
If you get ERROR_PIPE_BUSY on the CreateFile() call in the client, you need to call WaitNamedPipe() and then retry when it returns. If you get a return of zero from WaitNamedPipe() that means it timed out without the pipe becoming available. You'll never see that happen if you pass NMPWAIT_WAIT_FOREVER as the timeout.
You also need to keep in mind that the pipe may become busy again between the time WaitNamedPipe() returns and you call CreateFile(); therefore, you need to do it in a loop. Like this:
while (true)
{
hPipe = CreateFile(pipeName,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if (hPipe == INVALID_HANDLE_VALUE)
{
if (GetLastError() == ERROR_PIPE_BUSY)
{
if (!WaitNamedPipe(pipeName, NMPWAIT_USE_DEFAULT_WAIT))
continue; // timeout, try again
}
else
return false; // error
}
else
break; // success
}
EDIT:
I simplified your code and now it works fine. Working server and client follow.
Server:
#include <windows.h>
#include <stdio.h>
int main(void)
{
HANDLE pipe;
const DWORD BUFFER_SIZE = 1024;
pipe = CreateNamedPipe("\\\\.\\pipe\\testpipe1",
PIPE_ACCESS_OUTBOUND |
FILE_FLAG_OVERLAPPED, // read/write access
PIPE_TYPE_MESSAGE | // message type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFFER_SIZE, // output buffer size
BUFFER_SIZE, // input buffer size
2000, // client time-out
NULL);
if (pipe == INVALID_HANDLE_VALUE)
{
printf("Error while creating pipe\n");
return -1;
}
printf("Connecting to named pipe\n");
int ac;
for (ac=0; ac<2; ac++)
{
// Wait for some input. This helps me to start the client in other terminal.
printf("Connecting...\n");
ConnectNamedPipe(pipe, 0);
printf("Connect pipe returned.\n");
// Wait for some input.
char * message = "Test message";
DWORD bytes_written;
if (!WriteFile(pipe, message, strlen(message)+1, &bytes_written, NULL))
{
DWORD er = GetLastError();
char errs[200];
sprintf_s(errs, "Error : %ld", er);
printf("Error communicating to client.\n");
printf(errs);
}
printf("Written to pipe\n");
FlushFileBuffers(pipe);
if (!DisconnectNamedPipe(pipe))
{
printf("Disconnect failed %d\n", GetLastError());
}
else
{
printf("Disconnect successful\n");
}
}
}
Client:
#include <windows.h>
#include <stdio.h>
int main(void)
{
HANDLE hPipe;
while (1)
{
printf("Returned\n");
hPipe = CreateFile("\\\\.\\pipe\\testpipe1",
GENERIC_READ,
0, // no sharing
NULL, // default security attributes
OPEN_EXISTING, // opens existing pipe
0, // default attributes
NULL); // no template file
// Break if the pipe handle is valid.
if (hPipe != INVALID_HANDLE_VALUE)
break;
// Exit if an error other than ERROR_PIPE_BUSY occurs.
if (GetLastError() != ERROR_PIPE_BUSY)
{
printf("Could not open pipe %d\n", GetLastError());
return -1;
}
// All pipe instances are busy, so wait for sometime.
if ( ! WaitNamedPipe("\\\\.\\pipe\\testpipe1", NMPWAIT_USE_DEFAULT_WAIT))
{
printf("Could not open pipe: wait timed out.\n");
}
}
char *message = "hello";
DWORD cbToWrite = (strlen(message)+1)*sizeof(message[0]);
DWORD bytes_to_read = 2000;
char * buf = reinterpret_cast<char *>(malloc(bytes_to_read));
DWORD bytes_read;
printf("Waiting for read\n");
bytes_read = 0;
ReadFile(hPipe, buf, bytes_to_read, &bytes_read, 0);
if (bytes_read <= 0)
{
printf("ReadFile from pipe failed. GLE \n");
}
else
printf("Read %d bytes: %s\n", bytes_read, buf);
CloseHandle(hPipe);
return 0;
}
On the Server side when you decide to break the connection you must use chain:
1) CloseHandle (Pipe);
2) DisconnectNamedPipe (Pipe);

Resources