From 4e7b7c0059d76498d1c24f348dbf6d5799203fe0 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Wed, 10 Jan 2024 23:53:18 -0800 Subject: Add pipeutil to dutil Plus a couple small clean-ups in a couple of dutil files. --- src/libs/dutil/WixToolset.DUtil/dutil.vcxproj | 2 + .../dutil/WixToolset.DUtil/dutil.vcxproj.filters | 7 +- src/libs/dutil/WixToolset.DUtil/fileutil.cpp | 8 +- src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h | 1 + src/libs/dutil/WixToolset.DUtil/inc/pipeutil.h | 104 ++++++ src/libs/dutil/WixToolset.DUtil/perfutil.cpp | 10 + src/libs/dutil/WixToolset.DUtil/pipeutil.cpp | 347 +++++++++++++++++++++ src/libs/dutil/WixToolset.DUtil/precomp.h | 1 + .../dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj | 1 + .../DUtilUnitTest/DUtilUnitTest.vcxproj.filters | 6 +- src/libs/dutil/test/DUtilUnitTest/PipeUtilTest.cpp | 89 ++++++ src/libs/dutil/test/DUtilUnitTest/precomp.h | 1 + 12 files changed, 570 insertions(+), 7 deletions(-) create mode 100644 src/libs/dutil/WixToolset.DUtil/inc/pipeutil.h create mode 100644 src/libs/dutil/WixToolset.DUtil/pipeutil.cpp create mode 100644 src/libs/dutil/test/DUtilUnitTest/PipeUtilTest.cpp diff --git a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj index 736acd46..c973b141 100644 --- a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj +++ b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj @@ -87,6 +87,7 @@ + @@ -152,6 +153,7 @@ + diff --git a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters index c1095651..470fd3d0 100644 --- a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters +++ b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters @@ -216,6 +216,9 @@ Source Files + + Source Files + @@ -398,11 +401,13 @@ Header Files + + Header Files + Header Files - \ No newline at end of file diff --git a/src/libs/dutil/WixToolset.DUtil/fileutil.cpp b/src/libs/dutil/WixToolset.DUtil/fileutil.cpp index a9369ea1..31802d29 100644 --- a/src/libs/dutil/WixToolset.DUtil/fileutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/fileutil.cpp @@ -414,8 +414,8 @@ extern "C" HRESULT DAPI FileSetPointer( Assert(INVALID_HANDLE_VALUE != hFile); HRESULT hr = S_OK; - LARGE_INTEGER liMove; - LARGE_INTEGER liNewPosition; + LARGE_INTEGER liMove = { }; + LARGE_INTEGER liNewPosition = { }; liMove.QuadPart = dw64Move; if (!::SetFilePointerEx(hFile, liMove, &liNewPosition, dwMoveMethod)) @@ -843,7 +843,7 @@ extern "C" HRESULT DAPI FileCopyUsingHandles( { HRESULT hr = S_OK; DWORD64 cbTotalCopied = 0; - BYTE rgbData[4 * 1024]; + BYTE rgbData[4 * 1024] = { }; DWORD cbRead = 0; do @@ -887,7 +887,7 @@ extern "C" HRESULT DAPI FileCopyUsingHandlesWithProgress( { HRESULT hr = S_OK; DWORD64 cbTotalCopied = 0; - BYTE rgbData[64 * 1024]; + BYTE rgbData[64 * 1024] = { }; DWORD cbRead = 0; LARGE_INTEGER liSourceSize = { }; diff --git a/src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h b/src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h index cf10f910..42c7a554 100644 --- a/src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h +++ b/src/libs/dutil/WixToolset.DUtil/inc/dutilsources.h @@ -37,6 +37,7 @@ typedef enum DUTIL_SOURCE DUTIL_SOURCE_OSUTIL, DUTIL_SOURCE_PATHUTIL, DUTIL_SOURCE_PERFUTIL, + DUTIL_SOURCE_PIPEUTIL, DUTIL_SOURCE_POLCUTIL, DUTIL_SOURCE_PROCUTIL, DUTIL_SOURCE_REGUTIL, diff --git a/src/libs/dutil/WixToolset.DUtil/inc/pipeutil.h b/src/libs/dutil/WixToolset.DUtil/inc/pipeutil.h new file mode 100644 index 00000000..d16d768c --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/inc/pipeutil.h @@ -0,0 +1,104 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +#define ReleasePipeHandle(h) if (h != INVALID_HANDLE_VALUE) { ::CloseHandle(h); } +#define ReleasePipeMessage(pMsg) if (pMsg) { PipeFreeMessage(pMsg); } + + +// constants + +static const DWORD PIPE_WAIT_FOR_CONNECTION = 100; // wait a 10th of a second, +static const DWORD PIPE_RETRY_FOR_CONNECTION = 1800; // for up to 3 minutes. + + +// structs + +typedef struct _PIPE_MESSAGE +{ + DWORD dwMessageType; + DWORD cbData; + + BOOL fAllocatedData; + LPVOID pvData; +} PIPE_MESSAGE; + + +// functions + +/******************************************************************* + PipeClientConnect - Called from the client process to connect back + to the pipe provided by the server process. + +*******************************************************************/ +DAPI_(HRESULT) PipeClientConnect( + __in_z LPCWSTR wzPipeName, + __out HANDLE* phPipe +); + +/******************************************************************* + PipeCreate - create a duplex byte-mode named pipe compatible for use + with the other pipeutil functions. + +*******************************************************************/ +DAPI_(HRESULT) PipeCreate( + __in LPCWSTR wzName, + __in_opt LPSECURITY_ATTRIBUTES psa, + __out HANDLE* phPipe +); + +/******************************************************************* + PipeOpen - opens an exist named pipe compatible for use with the other + pipeutil functions. + +*******************************************************************/ +DAPI_(HRESULT) PipeOpen( + __in_z LPCWSTR wzName, + __out HANDLE* phPipe +); + +/******************************************************************* + PipeReadMessage - reads a message from the pipe. Free with + PipeFreeMessage(). + +*******************************************************************/ +DAPI_(HRESULT) PipeReadMessage( + __in HANDLE hPipe, + __in PIPE_MESSAGE* pMsg +); + +/******************************************************************* + PipeWriteMessage - writes a message to the pipe. + +*******************************************************************/ +DAPI_(HRESULT) PipeWriteMessage( + __in HANDLE hPipe, + __in DWORD dwMessageType, + __in_bcount_opt(cbData) LPVOID pvData, + __in SIZE_T cbData +); + +/******************************************************************* + PipeFreeMessage - frees any memory allocated in PipeReadMessage. + +*******************************************************************/ +DAPI_(void) PipeFreeMessage( + __in PIPE_MESSAGE* pMsg +); + +/******************************************************************* + PipeServerWaitForClientConnect - Called from the server process to + wait for a client to connect back to the provided pipe. + +*******************************************************************/ +DAPI_(HRESULT) PipeServerWaitForClientConnect( + __in HANDLE hPipe +); + +#ifdef __cplusplus +} +#endif diff --git a/src/libs/dutil/WixToolset.DUtil/perfutil.cpp b/src/libs/dutil/WixToolset.DUtil/perfutil.cpp index bc138d34..f556ce79 100644 --- a/src/libs/dutil/WixToolset.DUtil/perfutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/perfutil.cpp @@ -39,7 +39,9 @@ extern "C" void DAPI PerfInitialize( vdFrequency = 1000; // ticks are measured in milliseconds } else + { vdFrequency = static_cast(liFrequency.QuadPart); + } } @@ -57,15 +59,23 @@ extern "C" void DAPI PerfClickTime( LARGE_INTEGER* pli = pliElapsed; if (!pli) // if elapsed time time was not requested, reset the start time + { pli = &liStart; + } if (vfHighPerformanceCounter) + { ::QueryPerformanceCounter(pli); + } else + { pli->QuadPart = ::GetTickCount(); + } if (pliElapsed) + { pliElapsed->QuadPart -= liStart.QuadPart; + } } diff --git a/src/libs/dutil/WixToolset.DUtil/pipeutil.cpp b/src/libs/dutil/WixToolset.DUtil/pipeutil.cpp new file mode 100644 index 00000000..4aa69d56 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/pipeutil.cpp @@ -0,0 +1,347 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +static const DWORD PIPE_64KB = 64 * 1024; +static const LPCWSTR PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls"; + + +// Exit macros +#define PipeExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PIPEUTIL, x, s, __VA_ARGS__) +#define PipeExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PIPEUTIL, x, s, __VA_ARGS__) +#define PipeExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PIPEUTIL, x, s, __VA_ARGS__) +#define PipeExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PIPEUTIL, x, s, __VA_ARGS__) +#define PipeExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PIPEUTIL, x, s, __VA_ARGS__) +#define PipeExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PIPEUTIL, x, s, __VA_ARGS__) +#define PipeExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PIPEUTIL, p, x, e, s, __VA_ARGS__) +#define PipeExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PIPEUTIL, p, x, s, __VA_ARGS__) +#define PipeExitOnNullDebugTrace(p, x, e, s, ...) PipeExitOnNullDebugTraceSource(DUTIL_SOURCE_PIPEUTIL, p, x, e, s, __VA_ARGS__) +#define PipeExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PIPEUTIL, p, x, s, __VA_ARGS__) +#define PipeExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PIPEUTIL, e, x, s, __VA_ARGS__) +#define PipeExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PIPEUTIL, g, x, s, __VA_ARGS__) + + +static HRESULT AllocatePipeMessage( + __in DWORD dwMessageType, + __in_bcount_opt(cbData) LPVOID pvData, + __in SIZE_T cbData, + __out_bcount(cb) LPVOID* ppvMessage, + __out SIZE_T* pcbMessage +); + + +DAPI_(HRESULT) PipeClientConnect( + __in_z LPCWSTR wzPipeName, + __out HANDLE* phPipe +) +{ + HRESULT hr = S_OK; + LPWSTR sczPipeName = NULL; + HANDLE hPipe = INVALID_HANDLE_VALUE; + + // Try to connect to the parent. + hr = StrAllocFormatted(&sczPipeName, PIPE_NAME_FORMAT_STRING, wzPipeName); + PipeExitOnFailure(hr, "Failed to allocate name of pipe."); + + hr = E_UNEXPECTED; + for (DWORD cRetry = 0; FAILED(hr) && cRetry < PIPE_RETRY_FOR_CONNECTION; ++cRetry) + { + hPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (INVALID_HANDLE_VALUE == hPipe) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (E_FILENOTFOUND == hr) // if the pipe isn't created, call it a timeout waiting on the parent. + { + hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); + } + + ::Sleep(PIPE_WAIT_FOR_CONNECTION); + } + else // we have a connection, go with it. + { + hr = S_OK; + } + } + PipeExitOnRootFailure(hr, "Failed to open parent pipe: %ls", sczPipeName); + + *phPipe = hPipe; + hPipe = INVALID_HANDLE_VALUE; + +LExit: + ReleaseFileHandle(hPipe); + return hr; +} + +DAPI_(HRESULT) PipeCreate( + __in LPCWSTR wzName, + __in_opt LPSECURITY_ATTRIBUTES psa, + __out HANDLE* phPipe +) +{ + HRESULT hr = S_OK; + LPWSTR sczFullPipeName = NULL; + HANDLE hPipe = INVALID_HANDLE_VALUE; + + // Create the pipe. + hr = StrAllocFormatted(&sczFullPipeName, PIPE_NAME_FORMAT_STRING, wzName); + PipeExitOnFailure(hr, "Failed to allocate full name of pipe: %ls", wzName); + + // TODO: consider using overlapped IO to do waits on the pipe and still be able to cancel and such. + hPipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, psa); + if (INVALID_HANDLE_VALUE == hPipe) + { + PipeExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName); + } + + *phPipe = hPipe; + hPipe = INVALID_HANDLE_VALUE; + +LExit: + ReleaseFileHandle(hPipe); + ReleaseStr(sczFullPipeName); + + return hr; +} + +DAPI_(void) PipeFreeMessage( + __in PIPE_MESSAGE* pMsg +) +{ + if (pMsg->fAllocatedData) + { + ReleaseNullMem(pMsg->pvData); + pMsg->fAllocatedData = FALSE; + } +} + + +DAPI_(HRESULT) PipeOpen( + __in_z LPCWSTR wzName, + __out HANDLE* phPipe +) +{ + HRESULT hr = S_OK; + LPWSTR sczPipeName = NULL; + HANDLE hPipe = INVALID_HANDLE_VALUE; + + // Try to connect to the parent. + hr = StrAllocFormatted(&sczPipeName, PIPE_NAME_FORMAT_STRING, wzName); + PipeExitOnFailure(hr, "Failed to allocate name of pipe."); + + hr = E_UNEXPECTED; + for (DWORD cRetry = 0; FAILED(hr) && cRetry < PIPE_RETRY_FOR_CONNECTION; ++cRetry) + { + hPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (INVALID_HANDLE_VALUE == hPipe) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (E_FILENOTFOUND == hr) // if the pipe isn't created, call it a timeout waiting on the parent. + { + hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); + } + + ::Sleep(PIPE_WAIT_FOR_CONNECTION); + } + else // we have a connection, go with it. + { + hr = S_OK; + } + } + PipeExitOnRootFailure(hr, "Failed to open parent pipe: %ls", sczPipeName); + + *phPipe = hPipe; + hPipe = INVALID_HANDLE_VALUE; + +LExit: + ReleaseFileHandle(hPipe); + ReleaseStr(sczPipeName); + + return hr; +} + +DAPI_(HRESULT) PipeReadMessage( + __in HANDLE hPipe, + __in PIPE_MESSAGE* pMsg +) +{ + HRESULT hr = S_OK; + BYTE pbMessageIdAndByteCount[sizeof(DWORD) + sizeof(DWORD)] = { }; + + hr = FileReadHandle(hPipe, pbMessageIdAndByteCount, sizeof(pbMessageIdAndByteCount)); + if (HRESULT_FROM_WIN32(ERROR_BROKEN_PIPE) == hr) + { + memset(pbMessageIdAndByteCount, 0, sizeof(pbMessageIdAndByteCount)); + hr = S_FALSE; + } + PipeExitOnFailure(hr, "Failed to read message from pipe."); + + pMsg->dwMessageType = *(DWORD*)(pbMessageIdAndByteCount); + pMsg->cbData = *(DWORD*)(pbMessageIdAndByteCount + sizeof(DWORD)); + if (pMsg->cbData) + { + pMsg->pvData = MemAlloc(pMsg->cbData, FALSE); + PipeExitOnNull(pMsg->pvData, hr, E_OUTOFMEMORY, "Failed to allocate data for message."); + + hr = FileReadHandle(hPipe, reinterpret_cast(pMsg->pvData), pMsg->cbData); + PipeExitOnFailure(hr, "Failed to read data for message."); + + pMsg->fAllocatedData = TRUE; + } + +LExit: + if (!pMsg->fAllocatedData && pMsg->pvData) + { + MemFree(pMsg->pvData); + } + + return hr; +} + +DAPI_(HRESULT) PipeServerWaitForClientConnect( + __in HANDLE hPipe +) +{ + HRESULT hr = S_OK; + DWORD dwPipeState = PIPE_READMODE_BYTE | PIPE_NOWAIT; + + // Temporarily make the pipe non-blocking so we will not get stuck in ::ConnectNamedPipe() forever + // if the child decides not to show up. + if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL)) + { + PipeExitWithLastError(hr, "Failed to set pipe to non-blocking."); + } + + // Loop for a while waiting for a connection from child process. + DWORD cRetry = 0; + do + { + if (!::ConnectNamedPipe(hPipe, NULL)) + { + DWORD er = ::GetLastError(); + if (ERROR_PIPE_CONNECTED == er) + { + hr = S_OK; + break; + } + else if (ERROR_PIPE_LISTENING == er) + { + if (cRetry < PIPE_RETRY_FOR_CONNECTION) + { + hr = HRESULT_FROM_WIN32(er); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); + break; + } + + ++cRetry; + ::Sleep(PIPE_WAIT_FOR_CONNECTION); + } + else + { + hr = HRESULT_FROM_WIN32(er); + break; + } + } + } while (HRESULT_FROM_WIN32(ERROR_PIPE_LISTENING) == hr); + PipeExitOnRootFailure(hr, "Failed to wait for child to connect to pipe."); + + // Put the pipe back in blocking mode. + dwPipeState = PIPE_READMODE_BYTE | PIPE_WAIT; + if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL)) + { + PipeExitWithLastError(hr, "Failed to reset pipe to blocking."); + } + +LExit: + return hr; +} + +DAPI_(HRESULT) PipeWriteMessage( + __in HANDLE hPipe, + __in DWORD dwMessageType, + __in_bcount_opt(cbData) LPVOID pvData, + __in SIZE_T cbData +) +{ +// HRESULT hr = S_OK; +// +// hr = FileWriteHandle(hPipe, reinterpret_cast(&dwMessageType), sizeof(dwMessageType)); +// PipeExitOnFailure(hr, "Failed to write message id to pipe."); +// +// hr = FileWriteHandle(hPipe, reinterpret_cast(&cbData), sizeof(cbData)); +// PipeExitOnFailure(hr, "Failed to write message data size to pipe."); +// +// if (pvData && cbData) +// { +// hr = FileWriteHandle(hPipe, reinterpret_cast(pvData), cbData); +// PipeExitOnFailure(hr, "Failed to write message data to pipe."); +// } +// +//LExit: +// return hr; + HRESULT hr = S_OK; + LPVOID pv = NULL; + SIZE_T cb = 0; + + hr = AllocatePipeMessage(dwMessageType, pvData, cbData, &pv, &cb); + ExitOnFailure(hr, "Failed to allocate message to write."); + + // Write the message. + hr = FileWriteHandle(hPipe, reinterpret_cast(pv), cb); + ExitOnFailure(hr, "Failed to write message type to pipe."); + +LExit: + ReleaseMem(pv); + return hr; +} + +static HRESULT AllocatePipeMessage( + __in DWORD dwMessageType, + __in_bcount_opt(cbData) LPVOID pvData, + __in SIZE_T cbData, + __out_bcount(cb) LPVOID* ppvMessage, + __out SIZE_T* pcbMessage +) +{ + HRESULT hr = S_OK; + LPVOID pv = NULL; + size_t cb = 0; + DWORD dwcbData = 0; + + // If no data was provided, ensure the count of bytes is zero. + if (!pvData) + { + cbData = 0; + } + else if (MAXDWORD < cbData) + { + ExitWithRootFailure(hr, E_INVALIDDATA, "Pipe message is too large."); + } + + hr = ::SizeTAdd(sizeof(dwMessageType) + sizeof(dwcbData), cbData, &cb); + ExitOnRootFailure(hr, "Failed to calculate total pipe message size"); + + dwcbData = (DWORD)cbData; + + // Allocate the message. + pv = MemAlloc(cb, FALSE); + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for message."); + + memcpy_s(pv, cb, &dwMessageType, sizeof(dwMessageType)); + memcpy_s(static_cast(pv) + sizeof(dwMessageType), cb - sizeof(dwMessageType), &dwcbData, sizeof(dwcbData)); + if (dwcbData) + { + memcpy_s(static_cast(pv) + sizeof(dwMessageType) + sizeof(dwcbData), cb - sizeof(dwMessageType) - sizeof(dwcbData), pvData, dwcbData); + } + + *pcbMessage = cb; + *ppvMessage = pv; + pv = NULL; + +LExit: + ReleaseMem(pv); + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/precomp.h b/src/libs/dutil/WixToolset.DUtil/precomp.h index 607f7ce6..81dc7992 100644 --- a/src/libs/dutil/WixToolset.DUtil/precomp.h +++ b/src/libs/dutil/WixToolset.DUtil/precomp.h @@ -73,6 +73,7 @@ #include "osutil.h" #include "pathutil.h" #include "perfutil.h" +#include "pipeutil.h" #include "polcutil.h" #include "procutil.h" #include "queutil.h" diff --git a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj index edf8ba67..94826b8f 100644 --- a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj +++ b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj @@ -59,6 +59,7 @@ + Create diff --git a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters index d216f2af..e4972c1a 100644 --- a/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters +++ b/src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj.filters @@ -78,6 +78,9 @@ Source Files + + Source Files + @@ -94,10 +97,9 @@ - - + \ No newline at end of file diff --git a/src/libs/dutil/test/DUtilUnitTest/PipeUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/PipeUtilTest.cpp new file mode 100644 index 00000000..87a5e8f2 --- /dev/null +++ b/src/libs/dutil/test/DUtilUnitTest/PipeUtilTest.cpp @@ -0,0 +1,89 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +using namespace System; +using namespace System::Security::Principal; +using namespace Xunit; +using namespace WixInternal::TestSupport; +using namespace WixInternal::TestSupport::XunitExtensions; + +static DWORD STDAPICALLTYPE _TestPipeClientThreadProc( + __in LPVOID lpThreadParameter +); + +namespace DutilTests +{ + public ref class PipeUtil + { + public: + [Fact] + void PipeConnectWriteAndRead() + { + HRESULT hr = S_OK; + HANDLE hServerPipe = INVALID_HANDLE_VALUE; + HANDLE hClientThread = NULL; + PIPE_MESSAGE msg = { }; + DWORD dwThread = 42; + DWORD dwTestMessageId = 987654; + + try + { + hr = PipeCreate(L"DutilTest", NULL, &hServerPipe); + NativeAssert::Succeeded(hr, "Failed to create server pipe."); + + hClientThread = ::CreateThread(NULL, 0, _TestPipeClientThreadProc, &dwTestMessageId, 0, NULL); + if (hClientThread == 0) + { + NativeAssert::Fail("Failed to create client thread."); + return; + } + + hr = PipeServerWaitForClientConnect(hServerPipe); + NativeAssert::Succeeded(hr, "Failed to wait for client to connect to pipe."); + + hr = PipeReadMessage(hServerPipe, &msg); + NativeAssert::Succeeded(hr, "Failed to read message from client."); + + NativeAssert::Equal(dwTestMessageId, msg.dwMessageType); + + AppWaitForSingleObject(hClientThread, INFINITE); + + ::GetExitCodeThread(hClientThread, &dwThread); + NativeAssert::Equal((DWORD)12, dwThread); + } + finally + { + ReleasePipeMessage(&msg); + ReleaseHandle(hClientThread); + ReleasePipeHandle(hServerPipe); + } + } + }; +} + + +static DWORD STDAPICALLTYPE _TestPipeClientThreadProc( + __in LPVOID lpThreadParameter +) +{ + HRESULT hr = S_OK; + HANDLE hClientPipe = INVALID_HANDLE_VALUE; + + hr = PipeClientConnect(L"DutilTest", &hClientPipe); + if (FAILED(hr)) + { + return hr; + } + + ::Sleep(200); + + hr = PipeWriteMessage(hClientPipe, *(LPDWORD)lpThreadParameter, NULL, 0); + if (FAILED(hr)) + { + return hr; + } + + ReleasePipeHandle(hClientPipe); + return 12; +} diff --git a/src/libs/dutil/test/DUtilUnitTest/precomp.h b/src/libs/dutil/test/DUtilUnitTest/precomp.h index 92310b41..5a836074 100644 --- a/src/libs/dutil/test/DUtilUnitTest/precomp.h +++ b/src/libs/dutil/test/DUtilUnitTest/precomp.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3-55-g6feb