ваш код содержит огромное количество фундаментальных ошибок. точнее весь код - одна полная ошибка
посмотрите на фрагмент кода (в IocpThreadProc
и server_new
)
char buf[READ_BUFSIZE];
ret = ReadFileEx(svr->pipe, buf, sizeof(buf), &svr->ol, IocpThreadProc);
char buf[READ_BUFSIZE]
- это локальная переменная в функции. после выхода из функции - это становится произвольным адресом в стеке. поэтому, когда операция чтения завершена - это быстрее всего повредит ваш стек или будет неопределенным результатом. так что это ошибка. вы должны передавать не стековую память в качестве буфера чтения или не выходить из функции до завершения операции чтения
вы передаете IocpThreadProc
в качестве аргумента ReadFileEx
lpCompletionRoutine
Указатель на процедуру завершения, которая будет вызываться, когда операция чтения завершена и вызывающий поток находится в состоянии ожидания с предупреждением.
но вы никогда не ждете в состоянии готовности!
позже вы используете
BindIoCompletionCallback(svr->pipe, IocpThreadProc, 0);
но привязка файла к IOCP и использование завершения APC (lpCompletionRoutine) является взаимоисключающим. если, скажем, вы звоните BindIoCompletionCallback
раньше ReadFileEx(.., IocpThreadProc)
- вы получите ошибку ERROR_INVALID_PARAMETER
из исходного кода NtReadFile:
//
// If this file has an I/O completion port associated w/it, then
// ensure that the caller did not supply an APC routine, as the
// two are mutually exclusive methods for I/O completion
// notification.
//
if (fileObject->CompletionContext && IopApcRoutinePresent( ApcRoutine )) {
ObDereferenceObject( fileObject );
return STATUS_INVALID_PARAMETER;
}
ваш код "работает" только потому, что вы привязываете IOCP после вызова ReadFileEx(.., IocpThreadProc)
. но что произойдет, когда операция чтения будет завершена? APC (для IocpThreadProc
) будет вставлен в поток, а пакет будет поставлен в очередь IOCP. поэтому IocpThreadProc
будет вызываться дважды с одними и теми же данными для одной операции. он вызывается один раз только потому, что вы никогда не ждете в тревожном состоянии и не извлекаете APC из потока.
вы встроили OVERLAPPED
в Сервер - это ошибка. у вас должен быть уникальный OVERLAPPED
для каждого асинхронного ввода-вывода. точнее, вы должны определить собственный класс, унаследованный от OVERLAPPED
. иметь в этом классе указатель на Сервер, код операции, могут быть какие-то дополнительные данные. вам необходимо выделить эту структуру перед каждой операцией ввода-вывода и освободить ее по завершении.
GetLastError()
in IocpThreadProc
!!!
вам нужно использовать DWORD dwErrorCode
здесь, GetLastError()
нет смысла, потому что здесь, в другом потоке, вызванном, абсолютно не связанном с операцией. и поскольку это обратный вызов ядра, здесь действительно NTSTATUS
значения находятся в dwErrorCode, но не в ошибках win32. скажем, например, при завершении чтения вы можете получить STATUS_PIPE_BROKEN
, но не ERROR_BROKEN_PIPE
, но это уже большой дефект в документах MSDN
пример кода:
class __declspec(novtable) IoObject
{
friend struct UIRP;
LONG _dwRef;
public:
ULONG AddRef()
{
return InterlockedIncrement(&_dwRef);
}
ULONG Release()
{
ULONG dwRef = InterlockedDecrement(&_dwRef);
if (!dwRef)
{
delete this;
}
return dwRef;
}
protected:
IoObject()
{
_dwRef = 1;
}
virtual ~IoObject()
{
};
virtual void OnComplete(DWORD dwErrorCode, ULONG op, PVOID buf, DWORD dwNumberOfBytesTransfered) = 0;
};
struct UIRP : OVERLAPPED
{
IoObject* _obj;
PVOID _buf;
ULONG _op;
UIRP(IoObject* obj, ULONG op, PVOID buf = 0)
{
RtlZeroMemory(static_cast<OVERLAPPED*>(this), sizeof(OVERLAPPED));
_obj = obj;
obj->AddRef();
_op = op;
_buf = buf;
}
void CheckError(BOOL f)
{
if (!f)
{
DWORD dwErrorCode = RtlGetLastNtStatus();
if (dwErrorCode != STATUS_PENDING)
{
OnComplete(dwErrorCode, 0);
}
}
}
~UIRP()
{
_obj->Release();
}
static BOOL BindIoCompletion(HANDLE hObject)
{
return BindIoCompletionCallback(hObject, _OnComplete, 0);
}
private:
static void WINAPI _OnComplete(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
static_cast<UIRP*>(lpOverlapped)->OnComplete(dwErrorCode, dwNumberOfBytesTransfered);
}
void OnComplete(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered)
{
_obj->OnComplete(dwErrorCode, _op, _buf, dwNumberOfBytesTransfered);
delete this;
}
};
class __declspec(novtable) CPipe : public IoObject
{
enum {
pipe_connect, pipe_read, pipe_write
};
protected:
HANDLE _pipe;
PBYTE _buf;
ULONG _dataSize;
ULONG _bufferSize;
public:
CPipe()
{
_pipe = INVALID_HANDLE_VALUE;
_buf = 0;
_dataSize = 0;
_bufferSize = 0;
}
BOOL Create(ULONG bufferSize, PCWSTR name);
BOOL Listen();
BOOL Write(const void* data, ULONG cb);
BOOL Disconnect()
{
if (IsServer())
{
return DisconnectNamedPipe(_pipe);
}
HANDLE pipe = InterlockedExchangePointer(&_pipe, INVALID_HANDLE_VALUE);
if (pipe != INVALID_HANDLE_VALUE)
{
CloseHandle(pipe);
}
return TRUE;
}
protected:
BOOL Read();// usually never call direct
virtual BOOL OnRead(PVOID buf, ULONG cbTransferred) = 0;
virtual BOOL OnConnect() = 0;
virtual void OnDisconnect() = 0;
virtual BOOL IsServer() = 0;
virtual void OnWrite(DWORD /*dwErrorCode*/)
{
}
virtual ~CPipe()
{
HANDLE pipe = InterlockedExchangePointer(&_pipe, INVALID_HANDLE_VALUE);
if (pipe != INVALID_HANDLE_VALUE)
{
CloseHandle(pipe);
}
if (_buf)
{
delete _buf;
}
}
private:
virtual void OnComplete(DWORD dwErrorCode, ULONG op, PVOID buf, DWORD dwNumberOfBytesTransfered);
};
void CPipe::OnComplete(DWORD dwErrorCode, ULONG op, PVOID buf, DWORD dwNumberOfBytesTransfered)
{
DbgPrint("%u>%s<%p>(%x, %x, %x)\n", IsServer(), __FUNCTION__, this, dwErrorCode, op, dwNumberOfBytesTransfered);
switch (op)
{
case pipe_read:
switch(dwErrorCode)
{
case STATUS_SUCCESS:
if (OnRead(buf, dwNumberOfBytesTransfered)) Read();
break;
case STATUS_PIPE_BROKEN: // pipe handle has been closed, server must call DisconnectNamedPipe
case STATUS_CANCELLED: // CancelIo[Ex] called
Disconnect();
case STATUS_PIPE_DISCONNECTED: // server call DisconnectNamedPipe
case STATUS_INVALID_HANDLE: // we close handle
OnDisconnect();
break;
default:__debugbreak();
}
break;
case pipe_connect:
switch(dwErrorCode)
{
case STATUS_SUCCESS: // ERROR_SUCCESS
case STATUS_PIPE_CONNECTED: // ERROR_PIPE_CONNECTED
case STATUS_PIPE_CLOSING: // ERROR_NO_DATA (really client can send data before disconnect, exist sense do read)
if (OnConnect()) Read();
break;
case STATUS_PIPE_BROKEN: // server call CloseHandle before ConnectNamedPipe complete
case STATUS_PIPE_DISCONNECTED: // server call DisconnectNamedPipe before ConnectNamedPipe
case STATUS_CANCELLED: // server call CancelIo[Ex]
break;
default: __debugbreak();
}
break;
case pipe_write:
OnWrite(dwErrorCode);
LocalFree(buf);
break;
default: __debugbreak();
}
}
BOOL CPipe::Create(ULONG bufferSize, PCWSTR name)
{
if (_buf = new UCHAR[bufferSize])
{
_bufferSize = bufferSize;
}
else
{
return FALSE;
}
static WCHAR pipeprefix[] = L"\\\\?\\pipe\\";
PWSTR path = (PWSTR)alloca(wcslen(name) * sizeof(WCHAR) + sizeof(pipeprefix));
wcscat(wcscpy(path, pipeprefix), name);
BOOL bServer = IsServer();
_pipe = bServer
?
CreateNamedPipeW(path,
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
PIPE_UNLIMITED_INSTANCES,
PAGE_SIZE, PAGE_SIZE, INFINITE, NULL)
:
CreateFile(path, FILE_READ_ATTRIBUTES|FILE_READ_DATA|
FILE_WRITE_ATTRIBUTES|FILE_WRITE_DATA, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, 0);
if (_pipe == INVALID_HANDLE_VALUE || !UIRP::BindIoCompletion(_pipe))
{
return FALSE;
}
return bServer ? Listen() : OnComplete(0, pipe_connect, 0, 0), TRUE;
}
BOOL CPipe::Listen()
{
if (UIRP* irp = new UIRP(this, pipe_connect))
{
irp->CheckError(ConnectNamedPipe(_pipe, irp));
return TRUE;
}
return FALSE;
}
BOOL CPipe::Read()
{
ULONG NumberOfBytesToRead = _bufferSize - _dataSize;
if (!NumberOfBytesToRead)
{
return FALSE;
}
PVOID buf = _buf + _dataSize;
if (UIRP* irp = new UIRP(this, pipe_read, buf))
{
irp->CheckError(ReadFile(_pipe, buf, NumberOfBytesToRead, 0, irp));
return TRUE;
}
return FALSE;
}
BOOL CPipe::Write(const void* data, ULONG cb)
{
if (PVOID buf = LocalAlloc(0, cb))
{
if (UIRP* irp = new UIRP(this, pipe_write, buf))
{
memcpy(buf, data, cb);
irp->CheckError(WriteFile(_pipe, buf, cb, 0, irp));
return TRUE;
}
}
return FALSE;
}
class ServerPipe : public CPipe
{
virtual BOOL OnRead(PVOID buf, ULONG cbTransferred)
{
DbgPrint("%.*s\n", cbTransferred, buf);
char sz[256];
Write(sz, 1 + sprintf(sz, "response from %p server\n", this));
return TRUE;
}
virtual BOOL OnConnect()
{
DbgPrint("%s<%p>\n", __FUNCTION__, this);
return TRUE;
}
virtual void OnDisconnect()
{
DbgPrint("%s<%p>\n", __FUNCTION__, this);
Listen();//
}
virtual BOOL IsServer()
{
return TRUE;
}
virtual ~ServerPipe()
{
DbgPrint("%s<%p>\n", __FUNCTION__, this);
}
};
class ClientPipe : public CPipe
{
int _n;
virtual BOOL OnRead(PVOID buf, ULONG cbTransferred)
{
DbgPrint("%.*s\n", cbTransferred, buf);
if (--_n)
{
char sz[256];
Write(sz, 1 + sprintf(sz, "request[%u] from %p client\n", _n, this));
return TRUE;
}
return FALSE;
}
virtual BOOL OnConnect()
{
DbgPrint("%s<%p>\n", __FUNCTION__, this);
_n = 3;
char sz[256];
Write(sz, 1 + sprintf(sz, "hello from %p client\n", this));
return TRUE;
}
virtual void OnDisconnect()
{
DbgPrint("%s<%p>\n", __FUNCTION__, this);
}
virtual BOOL IsServer()
{
return FALSE;
}
virtual ~ClientPipe()
{
DbgPrint("%s<%p>\n", __FUNCTION__, this);
}
};
DWORD CALLBACK ClientThread(void* name)
{
int n = 2;
do
{
MessageBox(0,0,L"client",MB_ICONWARNING);
if (ClientPipe* p = new ClientPipe)
{
p->Create(PAGE_SIZE, (PCWSTR)name);
p->Release();
}
} while (--n);
return 0;
}
void pipeTest()
{
static WCHAR sname[] = L"__test_pipe__";
if (HANDLE hThread = CreateThread(0, 0, ClientThread, sname, 0, 0))
{
CloseHandle(hThread);
}
if (ServerPipe* p = new ServerPipe)
{
p->Create(PAGE_SIZE, sname);
p->Release();
}
MessageBox(0,0,0,0);
}
и около DWORD dwErrorCode
в
VOID CALLBACK FileIOCompletionRoutine(
__in DWORD dwErrorCode,
__in DWORD dwNumberOfBytesTransfered,
__in LPOVERLAPPED lpOverlapped
);
в документации BindIoCompletionCallback
есть неясность
Возвращаемое значение
Если функция завершается успешно, возвращаемое значение отличное от нуля.
Если функция не работает, возвращаемое значение равно нулю. Чтобы получить расширенную информацию об ошибке, вызовите GetLastError. Возвращенное значение представляет собой код ошибки NTSTATUS. Чтобы получить соответствующий код системной ошибки, используйте RtlNtStatusToDosError.
что имеется в виду под Возвращенное значение является кодом ошибки NTSTATUS? какое возвращаемое значение?
это DWORD dwErrorCode
в _30 _ а>
на самом деле мы передаем указатель режима ядра на IO_STATUS_BLOCK
(первые 2 члена OVERLAPPED
фактически IO_STATUS_BLOCK
). после завершения асинхронной операции - ядро заполняет IO_STATUS_BLOCK
и помещает пакет в очередь в IOCP (или APC в поток). ntdll извлекает PIO_STATUS_BLOCK
из IOCP (так что мы получили обратно указатель на наш OVERLAPPED
, переданный в API ввода-вывода) и заполняем
dwErrorCode = Iosb->Status,
dwNumberOfBytesTransfered = (ULONG)Iosb->Information,
lpOverlapped = (LPOVERLAPPED)Iosb;
система не выполняет преобразование
dwErrorCode = RtlNtStatusToDosError(Iosb->Status)
но напрямую назначить NTSTATUS
DWORD dwErrorCode
- поэтому в FileIOCompletionRoutine
мы должны сравнивать dwErrorCode
не с кодами ошибок wi32, а с кодами NTSTATUS (из "ntstatus.h"
)
поэтому мы никогда не видели ERROR_BROKEN_PIPE
или ERROR_PIPE_NOT_CONNECTED
в FileIOCompletionRoutine, но STATUS_PIPE_BROKEN
или STATUS_PIPE_DISCONNECTED
и пример кода с использованием нового потока Pool API вместо BindIoCompletionCallback
. здесь большое преимущество в том, что в IoCompletionCallback
< / a> (PTP_WIN32_IO_CALLBACK
) функция обратного вызова на месте ULONG IoResult
уже использовала ошибку win32, но не исходный NTSTATUS (IoResult = RtlNtStatusToDosError(Iosb->Status)
и примечание ULONG_PTR NumberOfBytesTransferred
(vs ULONG dwNumberOfBytesTransfered
из _ 55_ (LPOVERLAPPED_COMPLETION_ROUTINE
) функция обратного вызова и сравните ее с ULONG_PTR Information
из _ 58_.)
#define StartIo(irp, pio, f) StartThreadpoolIo(_pio); irp->CheckError(f, _pio);
class __declspec(novtable) IoObject
{
friend struct UIRP;
LONG _dwRef;
public:
ULONG AddRef()
{
return InterlockedIncrement(&_dwRef);
}
ULONG Release()
{
ULONG dwRef = InterlockedDecrement(&_dwRef);
if (!dwRef)
{
delete this;
}
return dwRef;
}
protected:
IoObject()
{
_dwRef = 1;
}
virtual ~IoObject()
{
};
virtual void OnComplete(DWORD dwErrorCode, ULONG op, PVOID buf, DWORD dwNumberOfBytesTransfered) = 0;
};
struct UIRP : OVERLAPPED
{
IoObject* _obj;
PVOID _buf;
ULONG _op;
UIRP(IoObject* obj, ULONG op, PVOID buf = 0)
{
RtlZeroMemory(static_cast<OVERLAPPED*>(this), sizeof(OVERLAPPED));
_obj = obj;
obj->AddRef();
_op = op;
_buf = buf;
}
void CheckError(BOOL f, PTP_IO pio)
{
if (!f)
{
DWORD dwErrorCode = GetLastError();
if (dwErrorCode != ERROR_IO_PENDING)
{
CancelThreadpoolIo(pio);
OnComplete(dwErrorCode, 0);
}
}
}
~UIRP()
{
_obj->Release();
}
static PTP_IO BindIoCompletion(HANDLE hObject)
{
return CreateThreadpoolIo(hObject, _IoCompletionCallback, 0, 0);
}
private:
static VOID CALLBACK _IoCompletionCallback(
__inout PTP_CALLBACK_INSTANCE /*Instance*/,
__inout_opt PVOID /*Context*/,
__inout_opt PVOID Overlapped,
__in ULONG IoResult,
__in ULONG_PTR NumberOfBytesTransferred,
__inout PTP_IO /*Io*/
)
{
static_cast<UIRP*>(Overlapped)->OnComplete(IoResult, (ULONG)NumberOfBytesTransferred);
}
void OnComplete(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered)
{
_obj->OnComplete(dwErrorCode, _op, _buf, dwNumberOfBytesTransfered);
delete this;
}
};
class __declspec(novtable) CPipe : public IoObject
{
enum {
pipe_connect, pipe_read, pipe_write
};
protected:
HANDLE _pipe;
PTP_IO _pio;
PBYTE _buf;
ULONG _dataSize;
ULONG _bufferSize;
public:
CPipe()
{
_pipe = INVALID_HANDLE_VALUE;
_buf = 0;
_dataSize = 0;
_bufferSize = 0;
_pio = 0;
}
BOOL Create(ULONG bufferSize, PCWSTR name);
BOOL Listen();
BOOL Write(const void* data, ULONG cb);
BOOL Disconnect()
{
if (IsServer())
{
return DisconnectNamedPipe(_pipe);
}
HANDLE pipe = InterlockedExchangePointer(&_pipe, INVALID_HANDLE_VALUE);
if (pipe != INVALID_HANDLE_VALUE)
{
CloseHandle(pipe);
}
return TRUE;
}
protected:
BOOL Read();// usually never call direct
virtual BOOL OnRead(PVOID buf, ULONG cbTransferred) = 0;
virtual BOOL OnConnect() = 0;
virtual void OnDisconnect() = 0;
virtual BOOL IsServer() = 0;
virtual void OnWrite(DWORD /*dwErrorCode*/)
{
}
virtual ~CPipe()
{
if (_pio)
{
CloseThreadpoolIo(_pio);
}
HANDLE pipe = InterlockedExchangePointer(&_pipe, INVALID_HANDLE_VALUE);
if (pipe != INVALID_HANDLE_VALUE)
{
CloseHandle(pipe);
}
if (_buf)
{
delete _buf;
}
}
private:
virtual void OnComplete(DWORD dwErrorCode, ULONG op, PVOID buf, DWORD dwNumberOfBytesTransfered);
};
void CPipe::OnComplete(DWORD dwErrorCode, ULONG op, PVOID buf, DWORD dwNumberOfBytesTransfered)
{
DbgPrint("%u>%s<%p>(%x, %x, %x)\n", IsServer(), __FUNCTION__, this, dwErrorCode, op, dwNumberOfBytesTransfered);
switch (op)
{
case pipe_read:
switch(dwErrorCode)
{
case ERROR_SUCCESS:
if (OnRead(buf, dwNumberOfBytesTransfered)) Read();
break;
case ERROR_BROKEN_PIPE: // pipe handle has been closed , server must call DisconnectNamedPipe
case ERROR_OPERATION_ABORTED: // CancelIo[Ex] called
Disconnect();
case ERROR_PIPE_NOT_CONNECTED: // server call DisconnectNamedPipe
case ERROR_INVALID_HANDLE: // we close handle
OnDisconnect();
break;
default:__debugbreak();
}
break;
case pipe_connect:
switch(dwErrorCode)
{
case ERROR_SUCCESS: // client just connected
case ERROR_PIPE_CONNECTED: // client already connected
case ERROR_NO_DATA: // client already connected and disconnected (really client can send data before disconnect, exist sense do read)
if (OnConnect()) Read();
break;
case ERROR_BROKEN_PIPE: // server call CloseHandle before ConnectNamedPipe complete
case ERROR_PIPE_NOT_CONNECTED: // server call DisconnectNamedPipe before ConnectNamedPipe
case ERROR_OPERATION_ABORTED: // server call CancelIo[Ex]
break;
default: __debugbreak();
}
break;
case pipe_write:
OnWrite(dwErrorCode);
LocalFree(buf);
break;
default: __debugbreak();
}
}
BOOL CPipe::Create(ULONG bufferSize, PCWSTR name)
{
if (_buf = new UCHAR[bufferSize])
{
_bufferSize = bufferSize;
}
else
{
return FALSE;
}
static WCHAR pipeprefix[] = L"\\\\?\\pipe\\";
PWSTR path = (PWSTR)alloca(wcslen(name) * sizeof(WCHAR) + sizeof(pipeprefix));
wcscat(wcscpy(path, pipeprefix), name);
BOOL bServer = IsServer();
_pipe = bServer
?
CreateNamedPipeW(path,
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
PIPE_UNLIMITED_INSTANCES,
PAGE_SIZE, PAGE_SIZE, INFINITE, NULL)
:
CreateFile(path, FILE_READ_ATTRIBUTES|FILE_READ_DATA|
FILE_WRITE_ATTRIBUTES|FILE_WRITE_DATA, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, 0);
if (_pipe == INVALID_HANDLE_VALUE || !(_pio = UIRP::BindIoCompletion(_pipe)))
{
return FALSE;
}
return bServer ? Listen() : OnComplete(0, pipe_connect, 0, 0), TRUE;
}
BOOL CPipe::Listen()
{
if (UIRP* irp = new UIRP(this, pipe_connect))
{
StartIo(irp, _pio, ConnectNamedPipe(_pipe, irp));
return TRUE;
}
return FALSE;
}
BOOL CPipe::Read()
{
ULONG NumberOfBytesToRead = _bufferSize - _dataSize;
if (!NumberOfBytesToRead)
{
return FALSE;
}
PVOID buf = _buf + _dataSize;
if (UIRP* irp = new UIRP(this, pipe_read, buf))
{
StartIo(irp, _pio, ReadFile(_pipe, buf, NumberOfBytesToRead, 0, irp));
return TRUE;
}
return FALSE;
}
BOOL CPipe::Write(const void* data, ULONG cb)
{
if (PVOID buf = LocalAlloc(0, cb))
{
if (UIRP* irp = new UIRP(this, pipe_write, buf))
{
memcpy(buf, data, cb);
StartIo(irp, _pio, WriteFile(_pipe, buf, cb, 0, irp));
return TRUE;
}
}
return FALSE;
}
class ServerPipe : public CPipe
{
virtual BOOL OnRead(PVOID buf, ULONG cbTransferred)
{
DbgPrint("%.*s\n", cbTransferred, buf);
char sz[256];
Write(sz, 1 + sprintf(sz, "response from %p server\n", this));
return TRUE;
}
virtual BOOL OnConnect()
{
DbgPrint("%s<%p>\n", __FUNCTION__, this);
return TRUE;
}
virtual void OnDisconnect()
{
DbgPrint("%s<%p>\n", __FUNCTION__, this);
Listen();//
}
virtual BOOL IsServer()
{
return TRUE;
}
virtual ~ServerPipe()
{
DbgPrint("%s<%p>\n", __FUNCTION__, this);
}
};
class ClientPipe : public CPipe
{
int _n;
virtual BOOL OnRead(PVOID buf, ULONG cbTransferred)
{
DbgPrint("%.*s\n", cbTransferred, buf);
if (--_n)
{
char sz[256];
Write(sz, 1 + sprintf(sz, "request[%u] from %p client\n", _n, this));
return TRUE;
}
return FALSE;
}
virtual BOOL OnConnect()
{
DbgPrint("%s<%p>\n", __FUNCTION__, this);
_n = 3;
char sz[256];
Write(sz, 1 + sprintf(sz, "hello from %p client\n", this));
return TRUE;
}
virtual void OnDisconnect()
{
DbgPrint("%s<%p>\n", __FUNCTION__, this);
}
virtual BOOL IsServer()
{
return FALSE;
}
virtual ~ClientPipe()
{
DbgPrint("%s<%p>\n", __FUNCTION__, this);
}
};
DWORD CALLBACK ClientThread(void* name)
{
int n = 2;
do
{
MessageBox(0,0,L"client",MB_ICONWARNING);
if (ClientPipe* p = new ClientPipe)
{
p->Create(PAGE_SIZE, (PCWSTR)name);
p->Release();
}
} while (--n);
return 0;
}
void pipeTest()
{
static WCHAR sname[] = L"__test_pipe__";
if (HANDLE hThread = CreateThread(0, 0, ClientThread, sname, 0, 0))
{
CloseHandle(hThread);
}
if (ServerPipe* p = new ServerPipe)
{
p->Create(PAGE_SIZE, sname);
p->Release();
}
MessageBox(0,0,0,0);
}
person
RbMm
schedule
25.12.2016