mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 10:46:33 +02:00
Deprecate ATL based IPC wrapper library (#2248)
* Deprecate ATL based IPC wrapper library * C# projects now use named pipe server implementations from two_way_pipe_message through the interop C++/Cli library. * Added Unit testing to interop library
This commit is contained in:
committed by
GitHub
parent
81551104ce
commit
63d989cab4
@@ -86,11 +86,14 @@
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\keyboard_layout.h" />
|
||||
<ClInclude Include="..\keyboard_layout_impl.h" />
|
||||
<ClInclude Include="..\two_way_pipe_message_ipc.h" />
|
||||
<ClInclude Include="..\two_way_pipe_message_ipc_impl.h" />
|
||||
<ClInclude Include="framework.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\keyboard_layout.cpp" />
|
||||
<ClCompile Include="..\two_way_pipe_message_ipc.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
|
||||
@@ -27,6 +27,12 @@
|
||||
<ClInclude Include="..\keyboard_layout_impl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\two_way_pipe_message_ipc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\two_way_pipe_message_ipc_impl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
@@ -35,5 +41,8 @@
|
||||
<ClCompile Include="..\keyboard_layout.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\two_way_pipe_message_ipc.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -126,6 +126,7 @@
|
||||
<ClInclude Include="notifications.h" />
|
||||
<ClInclude Include="shared_constants.h" />
|
||||
<ClInclude Include="timeutil.h" />
|
||||
<ClInclude Include="two_way_pipe_message_ipc.h" />
|
||||
<ClInclude Include="VersionHelper.h" />
|
||||
<ClInclude Include="window_helpers.h" />
|
||||
<ClInclude Include="icon_helpers.h" />
|
||||
@@ -140,7 +141,7 @@
|
||||
<ClInclude Include="common.h" />
|
||||
<ClInclude Include="Telemetry\ProjectTelemetry.h" />
|
||||
<ClInclude Include="Telemetry\TraceLoggingDefines.h" />
|
||||
<ClInclude Include="two_way_pipe_message_ipc.h" />
|
||||
<ClInclude Include="two_way_pipe_message_ipc_impl.h" />
|
||||
<ClInclude Include="version.h" />
|
||||
<ClInclude Include="windows_colors.h" />
|
||||
<ClInclude Include="winstore.h" />
|
||||
@@ -167,6 +168,7 @@
|
||||
<ClCompile Include="tasklist_positions.cpp" />
|
||||
<ClCompile Include="common.cpp" />
|
||||
<ClCompile Include="version.cpp" />
|
||||
<ClCompile Include="two_way_pipe_message_ipc.cpp" />
|
||||
<ClCompile Include="VersionHelper.cpp" />
|
||||
<ClCompile Include="windows_colors.cpp" />
|
||||
<ClCompile Include="window_helpers.cpp" />
|
||||
|
||||
@@ -51,9 +51,6 @@
|
||||
<ClInclude Include="Telemetry\TraceLoggingDefines.h">
|
||||
<Filter>Header Files\Telemetry</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="two_way_pipe_message_ipc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="async_message_queue.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -108,6 +105,12 @@
|
||||
<ClInclude Include="keyboard_layout.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="two_way_pipe_message_ipc_impl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="two_way_pipe_message_ipc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="d2d_svg.cpp">
|
||||
@@ -174,6 +177,9 @@
|
||||
<ClCompile Include="version.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="two_way_pipe_message_ipc.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
||||
20
src/common/interop-tests/Properties/AssemblyInfo.cs
Normal file
20
src/common/interop-tests/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("interop-tests")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("interop-tests")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2020")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
[assembly: Guid("437ad818-3f1f-4ca5-a79b-25233a157026")]
|
||||
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
55
src/common/interop-tests/UnitTest1.cs
Normal file
55
src/common/interop-tests/UnitTest1.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System.IO.Pipes;
|
||||
using interop;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Text;
|
||||
|
||||
namespace interop_tests
|
||||
{
|
||||
[TestClass]
|
||||
public class UnitTest1
|
||||
{
|
||||
private string SERVER_SIDE = "\\\\.\\pipe\\serverside";
|
||||
private string CLIENT_SIDE = "\\\\.\\pipe\\clientside";
|
||||
|
||||
private TwoWayPipeMessageIPCManaged clientPipe;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
clientPipe = new TwoWayPipeMessageIPCManaged(CLIENT_SIDE, SERVER_SIDE, null);
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void Cleanup()
|
||||
{
|
||||
clientPipe.End();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestSend()
|
||||
{
|
||||
var testString = "This string is a test\n";
|
||||
var reset = new AutoResetEvent(false);
|
||||
|
||||
var serverPipe = new TwoWayPipeMessageIPCManaged(
|
||||
SERVER_SIDE,
|
||||
CLIENT_SIDE,
|
||||
(string msg) =>
|
||||
{
|
||||
Assert.AreEqual(testString, msg);
|
||||
reset.Set();
|
||||
});
|
||||
serverPipe.Start();
|
||||
clientPipe.Start();
|
||||
|
||||
clientPipe.Send(testString);
|
||||
reset.WaitOne();
|
||||
|
||||
serverPipe.End();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
77
src/common/interop-tests/interop-tests.csproj
Normal file
77
src/common/interop-tests/interop-tests.csproj
Normal file
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\packages\MSTest.TestAdapter.2.1.0\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\..\..\packages\MSTest.TestAdapter.2.1.0\build\net45\MSTest.TestAdapter.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{437AD818-3F1F-4CA5-A79B-25233A157026}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>interop_tests</RootNamespace>
|
||||
<AssemblyName>interop-tests</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
|
||||
<IsCodedUITest>False</IsCodedUITest>
|
||||
<TestProjectType>UnitTest</TestProjectType>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\MSTest.TestFramework.2.1.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\MSTest.TestFramework.2.1.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="UnitTest1.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\interop\interop.vcxproj">
|
||||
<Project>{f055103b-f80b-4d0c-bf48-057c55620033}</Project>
|
||||
<Name>interop</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\MSTest.TestAdapter.2.1.0\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\MSTest.TestAdapter.2.1.0\build\net45\MSTest.TestAdapter.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\MSTest.TestAdapter.2.1.0\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\MSTest.TestAdapter.2.1.0\build\net45\MSTest.TestAdapter.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\..\..\packages\MSTest.TestAdapter.2.1.0\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\..\..\packages\MSTest.TestAdapter.2.1.0\build\net45\MSTest.TestAdapter.targets')" />
|
||||
</Project>
|
||||
5
src/common/interop-tests/packages.config
Normal file
5
src/common/interop-tests/packages.config
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="MSTest.TestAdapter" version="2.1.0" targetFramework="net472" />
|
||||
<package id="MSTest.TestFramework" version="2.1.0" targetFramework="net472" />
|
||||
</packages>
|
||||
@@ -1,12 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <common\keyboard_layout.h>
|
||||
#include <common\two_way_pipe_message_ipc.h>
|
||||
#include <msclr\marshal.h>
|
||||
#include <msclr\marshal_cppstd.h>
|
||||
#include <functional>
|
||||
|
||||
using namespace System;
|
||||
using namespace System::Runtime::InteropServices;
|
||||
|
||||
//https://docs.microsoft.com/en-us/cpp/dotnet/how-to-wrap-native-class-for-use-by-csharp?view=vs-2019
|
||||
namespace interop
|
||||
{
|
||||
public ref class LayoutMapManaged
|
||||
public
|
||||
ref class LayoutMapManaged
|
||||
{
|
||||
public:
|
||||
LayoutMapManaged() :
|
||||
@@ -17,12 +24,11 @@ public ref class LayoutMapManaged
|
||||
delete _map;
|
||||
}
|
||||
|
||||
String ^ GetKeyName(DWORD key)
|
||||
{
|
||||
String ^ GetKeyName(DWORD key) {
|
||||
return gcnew String(_map->GetKeyName(key).c_str());
|
||||
}
|
||||
|
||||
void Updatelayout()
|
||||
void Updatelayout()
|
||||
{
|
||||
_map->UpdateLayout();
|
||||
}
|
||||
@@ -36,5 +42,65 @@ public ref class LayoutMapManaged
|
||||
private:
|
||||
LayoutMap* _map;
|
||||
};
|
||||
}
|
||||
|
||||
public
|
||||
ref class TwoWayPipeMessageIPCManaged
|
||||
{
|
||||
public:
|
||||
delegate void ReadCallback(String ^ message);
|
||||
|
||||
TwoWayPipeMessageIPCManaged(String ^ inputPipeName, String ^ outputPipeName, ReadCallback ^ callback)
|
||||
{
|
||||
_wrapperCallback = gcnew InternalReadCallback(this, &TwoWayPipeMessageIPCManaged::ReadCallbackHelper);
|
||||
_callback = callback;
|
||||
|
||||
TwoWayPipeMessageIPC::callback_function cb = nullptr;
|
||||
if (callback != nullptr)
|
||||
{
|
||||
cb = (TwoWayPipeMessageIPC::callback_function)(void*)Marshal::GetFunctionPointerForDelegate(_wrapperCallback);
|
||||
}
|
||||
_pipe = new TwoWayPipeMessageIPC(
|
||||
msclr::interop::marshal_as<std::wstring>(inputPipeName),
|
||||
msclr::interop::marshal_as<std::wstring>(outputPipeName),
|
||||
cb);
|
||||
}
|
||||
|
||||
~TwoWayPipeMessageIPCManaged()
|
||||
{
|
||||
delete _pipe;
|
||||
}
|
||||
|
||||
void Send(String ^ msg)
|
||||
{
|
||||
_pipe->send(msclr::interop::marshal_as<std::wstring>(msg));
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
_pipe->start(nullptr);
|
||||
}
|
||||
|
||||
void End()
|
||||
{
|
||||
_pipe->end();
|
||||
}
|
||||
|
||||
protected:
|
||||
!TwoWayPipeMessageIPCManaged()
|
||||
{
|
||||
delete _pipe;
|
||||
}
|
||||
|
||||
private:
|
||||
delegate void InternalReadCallback(const std::wstring& msg);
|
||||
|
||||
TwoWayPipeMessageIPC* _pipe;
|
||||
ReadCallback ^ _callback;
|
||||
InternalReadCallback ^ _wrapperCallback;
|
||||
|
||||
void ReadCallbackHelper(const std::wstring& msg)
|
||||
{
|
||||
_callback(gcnew String(msg.c_str()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
498
src/common/two_way_pipe_message_ipc.cpp
Normal file
498
src/common/two_way_pipe_message_ipc.cpp
Normal file
@@ -0,0 +1,498 @@
|
||||
#include "pch.h"
|
||||
#include "two_way_pipe_message_ipc_impl.h"
|
||||
|
||||
#pragma comment(lib, "advapi32.lib")
|
||||
|
||||
TwoWayPipeMessageIPC::TwoWayPipeMessageIPC(
|
||||
std::wstring _input_pipe_name,
|
||||
std::wstring _output_pipe_name,
|
||||
callback_function p_func) :
|
||||
impl(new TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl(
|
||||
_input_pipe_name,
|
||||
_output_pipe_name,
|
||||
p_func))
|
||||
{
|
||||
}
|
||||
|
||||
TwoWayPipeMessageIPC::~TwoWayPipeMessageIPC()
|
||||
{
|
||||
delete impl;
|
||||
}
|
||||
|
||||
void TwoWayPipeMessageIPC::send(std::wstring msg)
|
||||
{
|
||||
impl->send(msg);
|
||||
}
|
||||
|
||||
void TwoWayPipeMessageIPC::start(HANDLE _restricted_pipe_token)
|
||||
{
|
||||
impl->start(_restricted_pipe_token);
|
||||
}
|
||||
|
||||
void TwoWayPipeMessageIPC::end()
|
||||
{
|
||||
impl->end();
|
||||
}
|
||||
|
||||
|
||||
TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::TwoWayPipeMessageIPCImpl(
|
||||
std::wstring _input_pipe_name,
|
||||
std::wstring _output_pipe_name,
|
||||
callback_function p_func)
|
||||
{
|
||||
input_pipe_name = _input_pipe_name;
|
||||
output_pipe_name = _output_pipe_name;
|
||||
dispatch_inc_message_function = p_func;
|
||||
}
|
||||
|
||||
void TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::send(std::wstring msg)
|
||||
{
|
||||
output_queue.queue_message(msg);
|
||||
}
|
||||
|
||||
void TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::start(HANDLE _restricted_pipe_token)
|
||||
{
|
||||
output_queue_thread = std::thread(&TwoWayPipeMessageIPCImpl::consume_output_queue_thread, this);
|
||||
input_queue_thread = std::thread(&TwoWayPipeMessageIPCImpl::consume_input_queue_thread, this);
|
||||
input_pipe_thread = std::thread(&TwoWayPipeMessageIPCImpl::start_named_pipe_server, this, _restricted_pipe_token);
|
||||
}
|
||||
|
||||
void TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::end()
|
||||
{
|
||||
closed = true;
|
||||
input_queue.interrupt();
|
||||
input_queue_thread.join();
|
||||
output_queue.interrupt();
|
||||
output_queue_thread.join();
|
||||
pipe_connect_handle_mutex.lock();
|
||||
if (current_connect_pipe_handle != NULL)
|
||||
{
|
||||
//Cancels the Pipe currently waiting for a connection.
|
||||
CancelIoEx(current_connect_pipe_handle, NULL);
|
||||
}
|
||||
pipe_connect_handle_mutex.unlock();
|
||||
input_pipe_thread.join();
|
||||
}
|
||||
|
||||
void TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::send_pipe_message(std::wstring message)
|
||||
{
|
||||
// Adapted from https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipe-client
|
||||
HANDLE output_pipe_handle;
|
||||
const wchar_t* message_send = message.c_str();
|
||||
BOOL fSuccess = FALSE;
|
||||
DWORD cbToWrite, cbWritten, dwMode;
|
||||
const wchar_t* lpszPipename = output_pipe_name.c_str();
|
||||
|
||||
// Try to open a named pipe; wait for it, if necessary.
|
||||
|
||||
while (1)
|
||||
{
|
||||
output_pipe_handle = CreateFile(
|
||||
lpszPipename, // pipe name
|
||||
GENERIC_READ | // read and write access
|
||||
GENERIC_WRITE,
|
||||
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 (output_pipe_handle != INVALID_HANDLE_VALUE)
|
||||
break;
|
||||
|
||||
// Exit if an error other than ERROR_PIPE_BUSY occurs.
|
||||
DWORD curr_error = 0;
|
||||
if ((curr_error = GetLastError()) != ERROR_PIPE_BUSY)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// All pipe instances are busy, so wait for 20 seconds.
|
||||
|
||||
if (!WaitNamedPipe(lpszPipename, 20000))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
dwMode = PIPE_READMODE_MESSAGE;
|
||||
fSuccess = SetNamedPipeHandleState(
|
||||
output_pipe_handle, // pipe handle
|
||||
&dwMode, // new pipe mode
|
||||
NULL, // don't set maximum bytes
|
||||
NULL); // don't set maximum time
|
||||
if (!fSuccess)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Send a message to the pipe server.
|
||||
|
||||
cbToWrite = (lstrlen(message_send)) * sizeof(WCHAR); // no need to send final '\0'. Pipe is in message mode.
|
||||
|
||||
fSuccess = WriteFile(
|
||||
output_pipe_handle, // pipe handle
|
||||
message_send, // message
|
||||
cbToWrite, // message length
|
||||
&cbWritten, // bytes written
|
||||
NULL); // not overlapped
|
||||
if (!fSuccess)
|
||||
{
|
||||
return;
|
||||
}
|
||||
CloseHandle(output_pipe_handle);
|
||||
return;
|
||||
}
|
||||
|
||||
void TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::consume_output_queue_thread()
|
||||
{
|
||||
while (!closed)
|
||||
{
|
||||
std::wstring message = output_queue.pop_message();
|
||||
if (message.length() == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
send_pipe_message(message);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::GetLogonSID(HANDLE hToken, PSID* ppsid)
|
||||
{
|
||||
// From https://docs.microsoft.com/en-us/previous-versions/aa446670(v=vs.85)
|
||||
BOOL bSuccess = FALSE;
|
||||
DWORD dwIndex;
|
||||
DWORD dwLength = 0;
|
||||
PTOKEN_GROUPS ptg = NULL;
|
||||
|
||||
// Verify the parameter passed in is not NULL.
|
||||
if (NULL == ppsid)
|
||||
goto Cleanup;
|
||||
|
||||
// Get required buffer size and allocate the TOKEN_GROUPS buffer.
|
||||
|
||||
if (!GetTokenInformation(
|
||||
hToken, // handle to the access token
|
||||
TokenGroups, // get information about the token's groups
|
||||
(LPVOID)ptg, // pointer to TOKEN_GROUPS buffer
|
||||
0, // size of buffer
|
||||
&dwLength // receives required buffer size
|
||||
))
|
||||
{
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||
goto Cleanup;
|
||||
|
||||
ptg = (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(),
|
||||
HEAP_ZERO_MEMORY,
|
||||
dwLength);
|
||||
|
||||
if (ptg == NULL)
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
// Get the token group information from the access token.
|
||||
|
||||
if (!GetTokenInformation(
|
||||
hToken, // handle to the access token
|
||||
TokenGroups, // get information about the token's groups
|
||||
(LPVOID)ptg, // pointer to TOKEN_GROUPS buffer
|
||||
dwLength, // size of buffer
|
||||
&dwLength // receives required buffer size
|
||||
))
|
||||
{
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
// Loop through the groups to find the logon SID.
|
||||
|
||||
for (dwIndex = 0; dwIndex < ptg->GroupCount; dwIndex++)
|
||||
if ((ptg->Groups[dwIndex].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID)
|
||||
{
|
||||
// Found the logon SID; make a copy of it.
|
||||
|
||||
dwLength = GetLengthSid(ptg->Groups[dwIndex].Sid);
|
||||
*ppsid = (PSID)HeapAlloc(GetProcessHeap(),
|
||||
HEAP_ZERO_MEMORY,
|
||||
dwLength);
|
||||
if (*ppsid == NULL)
|
||||
goto Cleanup;
|
||||
if (!CopySid(dwLength, *ppsid, ptg->Groups[dwIndex].Sid))
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, (LPVOID)*ppsid);
|
||||
goto Cleanup;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
bSuccess = TRUE;
|
||||
|
||||
Cleanup:
|
||||
|
||||
// Free the buffer for the token groups.
|
||||
|
||||
if (ptg != NULL)
|
||||
HeapFree(GetProcessHeap(), 0, (LPVOID)ptg);
|
||||
|
||||
return bSuccess;
|
||||
}
|
||||
|
||||
VOID TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::FreeLogonSID(PSID* ppsid)
|
||||
{
|
||||
// From https://docs.microsoft.com/en-us/previous-versions/aa446670(v=vs.85)
|
||||
HeapFree(GetProcessHeap(), 0, (LPVOID)*ppsid);
|
||||
}
|
||||
|
||||
int TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::change_pipe_security_allow_restricted_token(HANDLE handle, HANDLE token)
|
||||
{
|
||||
PACL old_dacl, new_dacl;
|
||||
PSECURITY_DESCRIPTOR sd;
|
||||
EXPLICIT_ACCESS ea;
|
||||
PSID user_restricted;
|
||||
int error;
|
||||
|
||||
if (!GetLogonSID(token, &user_restricted))
|
||||
{
|
||||
error = 5; // No access error.
|
||||
goto Ldone;
|
||||
}
|
||||
|
||||
if (GetSecurityInfo(handle,
|
||||
SE_KERNEL_OBJECT,
|
||||
DACL_SECURITY_INFORMATION,
|
||||
NULL,
|
||||
NULL,
|
||||
&old_dacl,
|
||||
NULL,
|
||||
&sd))
|
||||
{
|
||||
error = GetLastError();
|
||||
goto Lclean_sid;
|
||||
}
|
||||
|
||||
memset(&ea, 0, sizeof(EXPLICIT_ACCESS));
|
||||
ea.grfAccessPermissions |= GENERIC_READ | FILE_WRITE_ATTRIBUTES;
|
||||
ea.grfAccessPermissions |= GENERIC_WRITE | FILE_READ_ATTRIBUTES;
|
||||
ea.grfAccessPermissions |= SYNCHRONIZE;
|
||||
ea.grfAccessMode = SET_ACCESS;
|
||||
ea.grfInheritance = NO_INHERITANCE;
|
||||
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
||||
ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
|
||||
ea.Trustee.ptstrName = (LPTSTR)user_restricted;
|
||||
|
||||
if (SetEntriesInAcl(1, &ea, old_dacl, &new_dacl))
|
||||
{
|
||||
error = GetLastError();
|
||||
goto Lclean_sd;
|
||||
}
|
||||
|
||||
if (SetSecurityInfo(handle,
|
||||
SE_KERNEL_OBJECT,
|
||||
DACL_SECURITY_INFORMATION,
|
||||
NULL,
|
||||
NULL,
|
||||
new_dacl,
|
||||
NULL))
|
||||
{
|
||||
error = GetLastError();
|
||||
goto Lclean_dacl;
|
||||
}
|
||||
|
||||
error = 0;
|
||||
|
||||
Lclean_dacl:
|
||||
LocalFree((HLOCAL)new_dacl);
|
||||
Lclean_sd:
|
||||
LocalFree((HLOCAL)sd);
|
||||
Lclean_sid:
|
||||
FreeLogonSID(&user_restricted);
|
||||
Ldone:
|
||||
return error;
|
||||
}
|
||||
|
||||
HANDLE TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::create_medium_integrity_token()
|
||||
{
|
||||
HANDLE restricted_token_handle;
|
||||
SAFER_LEVEL_HANDLE level_handle = NULL;
|
||||
DWORD sid_size = SECURITY_MAX_SID_SIZE;
|
||||
BYTE medium_sid[SECURITY_MAX_SID_SIZE];
|
||||
if (!SaferCreateLevel(SAFER_SCOPEID_USER, SAFER_LEVELID_NORMALUSER, SAFER_LEVEL_OPEN, &level_handle, NULL))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (!SaferComputeTokenFromLevel(level_handle, NULL, &restricted_token_handle, 0, NULL))
|
||||
{
|
||||
SaferCloseLevel(level_handle);
|
||||
return NULL;
|
||||
}
|
||||
SaferCloseLevel(level_handle);
|
||||
|
||||
if (!CreateWellKnownSid(WinMediumLabelSid, nullptr, medium_sid, &sid_size))
|
||||
{
|
||||
CloseHandle(restricted_token_handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TOKEN_MANDATORY_LABEL integrity_level = { 0 };
|
||||
integrity_level.Label.Attributes = SE_GROUP_INTEGRITY;
|
||||
integrity_level.Label.Sid = reinterpret_cast<PSID>(medium_sid);
|
||||
|
||||
if (!SetTokenInformation(restricted_token_handle, TokenIntegrityLevel, &integrity_level, sizeof(integrity_level)))
|
||||
{
|
||||
CloseHandle(restricted_token_handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return restricted_token_handle;
|
||||
}
|
||||
|
||||
void TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::handle_pipe_connection(HANDLE input_pipe_handle)
|
||||
{
|
||||
//Adapted from https://docs.microsoft.com/en-us/windows/win32/ipc/multithreaded-pipe-server
|
||||
HANDLE hHeap = GetProcessHeap();
|
||||
uint8_t* pchRequest = (uint8_t*)HeapAlloc(hHeap, 0, BUFSIZE * sizeof(uint8_t));
|
||||
|
||||
DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
|
||||
BOOL fSuccess = FALSE;
|
||||
|
||||
// Do some extra error checking since the app will keep running even if this thread fails.
|
||||
std::list<std::vector<uint8_t>> message_parts;
|
||||
|
||||
if (input_pipe_handle == NULL)
|
||||
{
|
||||
if (pchRequest != NULL)
|
||||
HeapFree(hHeap, 0, pchRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pchRequest == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop until done reading
|
||||
do
|
||||
{
|
||||
// Read client requests from the pipe. This simplistic code only allows messages
|
||||
// up to BUFSIZE characters in length.
|
||||
ZeroMemory(pchRequest, BUFSIZE * sizeof(uint8_t));
|
||||
fSuccess = ReadFile(
|
||||
input_pipe_handle, // handle to pipe
|
||||
pchRequest, // buffer to receive data
|
||||
BUFSIZE * sizeof(uint8_t), // size of buffer
|
||||
&cbBytesRead, // number of bytes read
|
||||
NULL); // not overlapped I/O
|
||||
|
||||
if (!fSuccess && GetLastError() != ERROR_MORE_DATA)
|
||||
{
|
||||
break;
|
||||
}
|
||||
std::vector<uint8_t> part_vector;
|
||||
part_vector.reserve(cbBytesRead);
|
||||
std::copy(pchRequest, pchRequest + cbBytesRead, std::back_inserter(part_vector));
|
||||
message_parts.push_back(part_vector);
|
||||
} while (!fSuccess);
|
||||
|
||||
if (fSuccess)
|
||||
{
|
||||
// Reconstruct the total_message.
|
||||
std::vector<uint8_t> reconstructed_message;
|
||||
size_t total_size = 0;
|
||||
for (auto& part_vector : message_parts)
|
||||
{
|
||||
total_size += part_vector.size();
|
||||
}
|
||||
reconstructed_message.reserve(total_size);
|
||||
for (auto& part_vector : message_parts)
|
||||
{
|
||||
std::move(part_vector.begin(), part_vector.end(), std::back_inserter(reconstructed_message));
|
||||
}
|
||||
std::wstring unicode_msg;
|
||||
unicode_msg.assign(reinterpret_cast<std::wstring::const_pointer>(reconstructed_message.data()), reconstructed_message.size() / sizeof(std::wstring::value_type));
|
||||
input_queue.queue_message(unicode_msg);
|
||||
}
|
||||
|
||||
// Flush the pipe to allow the client to read the pipe's contents
|
||||
// before disconnecting. Then disconnect the pipe, and close the
|
||||
// handle to this pipe instance.
|
||||
|
||||
FlushFileBuffers(input_pipe_handle);
|
||||
DisconnectNamedPipe(input_pipe_handle);
|
||||
CloseHandle(input_pipe_handle);
|
||||
|
||||
HeapFree(hHeap, 0, pchRequest);
|
||||
|
||||
printf("InstanceThread exitting.\n");
|
||||
}
|
||||
|
||||
void TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::start_named_pipe_server(HANDLE token)
|
||||
{
|
||||
// Adapted from https://docs.microsoft.com/en-us/windows/win32/ipc/multithreaded-pipe-server
|
||||
const wchar_t* pipe_name = input_pipe_name.c_str();
|
||||
BOOL connected = FALSE;
|
||||
HANDLE connect_pipe_handle = INVALID_HANDLE_VALUE;
|
||||
while (!closed)
|
||||
{
|
||||
{
|
||||
std::unique_lock lock(pipe_connect_handle_mutex);
|
||||
connect_pipe_handle = CreateNamedPipe(
|
||||
pipe_name,
|
||||
PIPE_ACCESS_DUPLEX |
|
||||
WRITE_DAC,
|
||||
PIPE_TYPE_MESSAGE |
|
||||
PIPE_READMODE_MESSAGE |
|
||||
PIPE_WAIT,
|
||||
PIPE_UNLIMITED_INSTANCES,
|
||||
BUFSIZE,
|
||||
BUFSIZE,
|
||||
0,
|
||||
NULL);
|
||||
|
||||
if (connect_pipe_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (token != NULL)
|
||||
{
|
||||
int err = change_pipe_security_allow_restricted_token(connect_pipe_handle, token);
|
||||
}
|
||||
current_connect_pipe_handle = connect_pipe_handle;
|
||||
}
|
||||
connected = ConnectNamedPipe(connect_pipe_handle, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
|
||||
{
|
||||
std::unique_lock lock(pipe_connect_handle_mutex);
|
||||
current_connect_pipe_handle = NULL;
|
||||
}
|
||||
if (connected)
|
||||
{
|
||||
std::thread(&TwoWayPipeMessageIPCImpl::handle_pipe_connection, this, connect_pipe_handle).detach();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Client could not connect.
|
||||
CloseHandle(connect_pipe_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl::consume_input_queue_thread()
|
||||
{
|
||||
while (!closed)
|
||||
{
|
||||
outgoing_message = L"";
|
||||
std::wstring message = input_queue.pop_message();
|
||||
if (message.length() == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if callback method exists first before trying to call it.
|
||||
// otherwise just store the response message in a variable.
|
||||
if (dispatch_inc_message_function != nullptr)
|
||||
{
|
||||
dispatch_inc_message_function(message);
|
||||
}
|
||||
outgoing_message = message;
|
||||
}
|
||||
}
|
||||
@@ -1,485 +1,18 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include "async_message_queue.h"
|
||||
#include <WinSafer.h>
|
||||
#include <Sddl.h>
|
||||
#include <accctrl.h>
|
||||
#include <aclapi.h>
|
||||
#include <list>
|
||||
|
||||
class TwoWayPipeMessageIPC
|
||||
{
|
||||
public:
|
||||
typedef void (*callback_function)(const std::wstring&);
|
||||
void send(std::wstring msg)
|
||||
{
|
||||
output_queue.queue_message(msg);
|
||||
}
|
||||
TwoWayPipeMessageIPC(std::wstring _input_pipe_name, std::wstring _output_pipe_name, callback_function p_func)
|
||||
{
|
||||
input_pipe_name = _input_pipe_name;
|
||||
output_pipe_name = _output_pipe_name;
|
||||
dispatch_inc_message_function = p_func;
|
||||
}
|
||||
void start(HANDLE _restricted_pipe_token)
|
||||
{
|
||||
output_queue_thread = std::thread(&TwoWayPipeMessageIPC::consume_output_queue_thread, this);
|
||||
input_queue_thread = std::thread(&TwoWayPipeMessageIPC::consume_input_queue_thread, this);
|
||||
input_pipe_thread = std::thread(&TwoWayPipeMessageIPC::start_named_pipe_server, this, _restricted_pipe_token);
|
||||
}
|
||||
|
||||
void end()
|
||||
{
|
||||
closed = true;
|
||||
input_queue.interrupt();
|
||||
input_queue_thread.join();
|
||||
output_queue.interrupt();
|
||||
output_queue_thread.join();
|
||||
pipe_connect_handle_mutex.lock();
|
||||
if (current_connect_pipe_handle != NULL)
|
||||
{
|
||||
//Cancels the Pipe currently waiting for a connection.
|
||||
CancelIoEx(current_connect_pipe_handle, NULL);
|
||||
}
|
||||
pipe_connect_handle_mutex.unlock();
|
||||
input_pipe_thread.join();
|
||||
}
|
||||
TwoWayPipeMessageIPC(
|
||||
std::wstring _input_pipe_name,
|
||||
std::wstring _output_pipe_name,
|
||||
callback_function p_func);
|
||||
~TwoWayPipeMessageIPC();
|
||||
void send(std::wstring msg);
|
||||
void start(HANDLE _restricted_pipe_token);
|
||||
void end();
|
||||
|
||||
private:
|
||||
AsyncMessageQueue input_queue;
|
||||
AsyncMessageQueue output_queue;
|
||||
std::wstring output_pipe_name;
|
||||
std::wstring input_pipe_name;
|
||||
std::thread input_queue_thread;
|
||||
std::thread output_queue_thread;
|
||||
std::thread input_pipe_thread;
|
||||
std::mutex pipe_connect_handle_mutex; // For manipulating the current_connect_pipe
|
||||
std::wstring outgoing_message; // Store the updated json settings.
|
||||
|
||||
HANDLE current_connect_pipe_handle = NULL;
|
||||
bool closed = false;
|
||||
TwoWayPipeMessageIPC::callback_function dispatch_inc_message_function;
|
||||
const DWORD BUFSIZE = 1024;
|
||||
|
||||
void send_pipe_message(std::wstring message)
|
||||
{
|
||||
// Adapted from https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipe-client
|
||||
HANDLE output_pipe_handle;
|
||||
const wchar_t* message_send = message.c_str();
|
||||
BOOL fSuccess = FALSE;
|
||||
DWORD cbToWrite, cbWritten, dwMode;
|
||||
const wchar_t* lpszPipename = output_pipe_name.c_str();
|
||||
|
||||
// Try to open a named pipe; wait for it, if necessary.
|
||||
|
||||
while (1)
|
||||
{
|
||||
output_pipe_handle = CreateFile(
|
||||
lpszPipename, // pipe name
|
||||
GENERIC_READ | // read and write access
|
||||
GENERIC_WRITE,
|
||||
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 (output_pipe_handle != INVALID_HANDLE_VALUE)
|
||||
break;
|
||||
|
||||
// Exit if an error other than ERROR_PIPE_BUSY occurs.
|
||||
DWORD curr_error = 0;
|
||||
if ((curr_error = GetLastError()) != ERROR_PIPE_BUSY)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// All pipe instances are busy, so wait for 20 seconds.
|
||||
|
||||
if (!WaitNamedPipe(lpszPipename, 20000))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
dwMode = PIPE_READMODE_MESSAGE;
|
||||
fSuccess = SetNamedPipeHandleState(
|
||||
output_pipe_handle, // pipe handle
|
||||
&dwMode, // new pipe mode
|
||||
NULL, // don't set maximum bytes
|
||||
NULL); // don't set maximum time
|
||||
if (!fSuccess)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Send a message to the pipe server.
|
||||
|
||||
cbToWrite = (lstrlen(message_send)) * sizeof(WCHAR); // no need to send final '\0'. Pipe is in message mode.
|
||||
|
||||
fSuccess = WriteFile(
|
||||
output_pipe_handle, // pipe handle
|
||||
message_send, // message
|
||||
cbToWrite, // message length
|
||||
&cbWritten, // bytes written
|
||||
NULL); // not overlapped
|
||||
if (!fSuccess)
|
||||
{
|
||||
return;
|
||||
}
|
||||
CloseHandle(output_pipe_handle);
|
||||
return;
|
||||
}
|
||||
void consume_output_queue_thread()
|
||||
{
|
||||
while (!closed)
|
||||
{
|
||||
std::wstring message = output_queue.pop_message();
|
||||
if (message.length() == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
send_pipe_message(message);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL GetLogonSID(HANDLE hToken, PSID* ppsid)
|
||||
{
|
||||
// From https://docs.microsoft.com/en-us/previous-versions/aa446670(v=vs.85)
|
||||
BOOL bSuccess = FALSE;
|
||||
DWORD dwIndex;
|
||||
DWORD dwLength = 0;
|
||||
PTOKEN_GROUPS ptg = NULL;
|
||||
|
||||
// Verify the parameter passed in is not NULL.
|
||||
if (NULL == ppsid)
|
||||
goto Cleanup;
|
||||
|
||||
// Get required buffer size and allocate the TOKEN_GROUPS buffer.
|
||||
|
||||
if (!GetTokenInformation(
|
||||
hToken, // handle to the access token
|
||||
TokenGroups, // get information about the token's groups
|
||||
(LPVOID)ptg, // pointer to TOKEN_GROUPS buffer
|
||||
0, // size of buffer
|
||||
&dwLength // receives required buffer size
|
||||
))
|
||||
{
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||
goto Cleanup;
|
||||
|
||||
ptg = (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(),
|
||||
HEAP_ZERO_MEMORY,
|
||||
dwLength);
|
||||
|
||||
if (ptg == NULL)
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
// Get the token group information from the access token.
|
||||
|
||||
if (!GetTokenInformation(
|
||||
hToken, // handle to the access token
|
||||
TokenGroups, // get information about the token's groups
|
||||
(LPVOID)ptg, // pointer to TOKEN_GROUPS buffer
|
||||
dwLength, // size of buffer
|
||||
&dwLength // receives required buffer size
|
||||
))
|
||||
{
|
||||
goto Cleanup;
|
||||
}
|
||||
|
||||
// Loop through the groups to find the logon SID.
|
||||
|
||||
for (dwIndex = 0; dwIndex < ptg->GroupCount; dwIndex++)
|
||||
if ((ptg->Groups[dwIndex].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID)
|
||||
{
|
||||
// Found the logon SID; make a copy of it.
|
||||
|
||||
dwLength = GetLengthSid(ptg->Groups[dwIndex].Sid);
|
||||
*ppsid = (PSID)HeapAlloc(GetProcessHeap(),
|
||||
HEAP_ZERO_MEMORY,
|
||||
dwLength);
|
||||
if (*ppsid == NULL)
|
||||
goto Cleanup;
|
||||
if (!CopySid(dwLength, *ppsid, ptg->Groups[dwIndex].Sid))
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, (LPVOID)*ppsid);
|
||||
goto Cleanup;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
bSuccess = TRUE;
|
||||
|
||||
Cleanup:
|
||||
|
||||
// Free the buffer for the token groups.
|
||||
|
||||
if (ptg != NULL)
|
||||
HeapFree(GetProcessHeap(), 0, (LPVOID)ptg);
|
||||
|
||||
return bSuccess;
|
||||
}
|
||||
|
||||
VOID FreeLogonSID(PSID* ppsid)
|
||||
{
|
||||
// From https://docs.microsoft.com/en-us/previous-versions/aa446670(v=vs.85)
|
||||
HeapFree(GetProcessHeap(), 0, (LPVOID)*ppsid);
|
||||
}
|
||||
|
||||
int change_pipe_security_allow_restricted_token(HANDLE handle, HANDLE token)
|
||||
{
|
||||
PACL old_dacl, new_dacl;
|
||||
PSECURITY_DESCRIPTOR sd;
|
||||
EXPLICIT_ACCESS ea;
|
||||
PSID user_restricted;
|
||||
int error;
|
||||
|
||||
if (!GetLogonSID(token, &user_restricted))
|
||||
{
|
||||
error = 5; // No access error.
|
||||
goto Ldone;
|
||||
}
|
||||
|
||||
if (GetSecurityInfo(handle,
|
||||
SE_KERNEL_OBJECT,
|
||||
DACL_SECURITY_INFORMATION,
|
||||
NULL,
|
||||
NULL,
|
||||
&old_dacl,
|
||||
NULL,
|
||||
&sd))
|
||||
{
|
||||
error = GetLastError();
|
||||
goto Lclean_sid;
|
||||
}
|
||||
|
||||
memset(&ea, 0, sizeof(EXPLICIT_ACCESS));
|
||||
ea.grfAccessPermissions |= GENERIC_READ | FILE_WRITE_ATTRIBUTES;
|
||||
ea.grfAccessPermissions |= GENERIC_WRITE | FILE_READ_ATTRIBUTES;
|
||||
ea.grfAccessPermissions |= SYNCHRONIZE;
|
||||
ea.grfAccessMode = SET_ACCESS;
|
||||
ea.grfInheritance = NO_INHERITANCE;
|
||||
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
||||
ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
|
||||
ea.Trustee.ptstrName = (LPTSTR)user_restricted;
|
||||
|
||||
if (SetEntriesInAcl(1, &ea, old_dacl, &new_dacl))
|
||||
{
|
||||
error = GetLastError();
|
||||
goto Lclean_sd;
|
||||
}
|
||||
|
||||
if (SetSecurityInfo(handle,
|
||||
SE_KERNEL_OBJECT,
|
||||
DACL_SECURITY_INFORMATION,
|
||||
NULL,
|
||||
NULL,
|
||||
new_dacl,
|
||||
NULL))
|
||||
{
|
||||
error = GetLastError();
|
||||
goto Lclean_dacl;
|
||||
}
|
||||
|
||||
error = 0;
|
||||
|
||||
Lclean_dacl:
|
||||
LocalFree((HLOCAL)new_dacl);
|
||||
Lclean_sd:
|
||||
LocalFree((HLOCAL)sd);
|
||||
Lclean_sid:
|
||||
FreeLogonSID(&user_restricted);
|
||||
Ldone:
|
||||
return error;
|
||||
}
|
||||
|
||||
HANDLE create_medium_integrity_token()
|
||||
{
|
||||
HANDLE restricted_token_handle;
|
||||
SAFER_LEVEL_HANDLE level_handle = NULL;
|
||||
DWORD sid_size = SECURITY_MAX_SID_SIZE;
|
||||
BYTE medium_sid[SECURITY_MAX_SID_SIZE];
|
||||
if (!SaferCreateLevel(SAFER_SCOPEID_USER, SAFER_LEVELID_NORMALUSER, SAFER_LEVEL_OPEN, &level_handle, NULL))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (!SaferComputeTokenFromLevel(level_handle, NULL, &restricted_token_handle, 0, NULL))
|
||||
{
|
||||
SaferCloseLevel(level_handle);
|
||||
return NULL;
|
||||
}
|
||||
SaferCloseLevel(level_handle);
|
||||
|
||||
if (!CreateWellKnownSid(WinMediumLabelSid, nullptr, medium_sid, &sid_size))
|
||||
{
|
||||
CloseHandle(restricted_token_handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TOKEN_MANDATORY_LABEL integrity_level = { 0 };
|
||||
integrity_level.Label.Attributes = SE_GROUP_INTEGRITY;
|
||||
integrity_level.Label.Sid = reinterpret_cast<PSID>(medium_sid);
|
||||
|
||||
if (!SetTokenInformation(restricted_token_handle, TokenIntegrityLevel, &integrity_level, sizeof(integrity_level)))
|
||||
{
|
||||
CloseHandle(restricted_token_handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return restricted_token_handle;
|
||||
}
|
||||
|
||||
void handle_pipe_connection(HANDLE input_pipe_handle)
|
||||
{
|
||||
//Adapted from https://docs.microsoft.com/en-us/windows/win32/ipc/multithreaded-pipe-server
|
||||
HANDLE hHeap = GetProcessHeap();
|
||||
uint8_t* pchRequest = (uint8_t*)HeapAlloc(hHeap, 0, BUFSIZE * sizeof(uint8_t));
|
||||
|
||||
DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
|
||||
BOOL fSuccess = FALSE;
|
||||
|
||||
// Do some extra error checking since the app will keep running even if this thread fails.
|
||||
std::list<std::vector<uint8_t>> message_parts;
|
||||
|
||||
if (input_pipe_handle == NULL)
|
||||
{
|
||||
if (pchRequest != NULL)
|
||||
HeapFree(hHeap, 0, pchRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pchRequest == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop until done reading
|
||||
do
|
||||
{
|
||||
// Read client requests from the pipe. This simplistic code only allows messages
|
||||
// up to BUFSIZE characters in length.
|
||||
ZeroMemory(pchRequest, BUFSIZE * sizeof(uint8_t));
|
||||
fSuccess = ReadFile(
|
||||
input_pipe_handle, // handle to pipe
|
||||
pchRequest, // buffer to receive data
|
||||
BUFSIZE * sizeof(uint8_t), // size of buffer
|
||||
&cbBytesRead, // number of bytes read
|
||||
NULL); // not overlapped I/O
|
||||
|
||||
if (!fSuccess && GetLastError() != ERROR_MORE_DATA)
|
||||
{
|
||||
break;
|
||||
}
|
||||
std::vector<uint8_t> part_vector;
|
||||
part_vector.reserve(cbBytesRead);
|
||||
std::copy(pchRequest, pchRequest + cbBytesRead, std::back_inserter(part_vector));
|
||||
message_parts.push_back(part_vector);
|
||||
} while (!fSuccess);
|
||||
|
||||
if (fSuccess)
|
||||
{
|
||||
// Reconstruct the total_message.
|
||||
std::vector<uint8_t> reconstructed_message;
|
||||
size_t total_size = 0;
|
||||
for (auto& part_vector : message_parts)
|
||||
{
|
||||
total_size += part_vector.size();
|
||||
}
|
||||
reconstructed_message.reserve(total_size);
|
||||
for (auto& part_vector : message_parts)
|
||||
{
|
||||
std::move(part_vector.begin(), part_vector.end(), std::back_inserter(reconstructed_message));
|
||||
}
|
||||
std::wstring unicode_msg;
|
||||
unicode_msg.assign(reinterpret_cast<std::wstring::const_pointer>(reconstructed_message.data()), reconstructed_message.size() / sizeof(std::wstring::value_type));
|
||||
input_queue.queue_message(unicode_msg);
|
||||
}
|
||||
|
||||
// Flush the pipe to allow the client to read the pipe's contents
|
||||
// before disconnecting. Then disconnect the pipe, and close the
|
||||
// handle to this pipe instance.
|
||||
|
||||
FlushFileBuffers(input_pipe_handle);
|
||||
DisconnectNamedPipe(input_pipe_handle);
|
||||
CloseHandle(input_pipe_handle);
|
||||
|
||||
HeapFree(hHeap, 0, pchRequest);
|
||||
|
||||
printf("InstanceThread exitting.\n");
|
||||
}
|
||||
|
||||
void start_named_pipe_server(HANDLE token)
|
||||
{
|
||||
// Adapted from https://docs.microsoft.com/en-us/windows/win32/ipc/multithreaded-pipe-server
|
||||
const wchar_t* pipe_name = input_pipe_name.c_str();
|
||||
BOOL connected = FALSE;
|
||||
HANDLE connect_pipe_handle = INVALID_HANDLE_VALUE;
|
||||
while (!closed)
|
||||
{
|
||||
{
|
||||
std::unique_lock lock(pipe_connect_handle_mutex);
|
||||
connect_pipe_handle = CreateNamedPipe(
|
||||
pipe_name,
|
||||
PIPE_ACCESS_DUPLEX |
|
||||
WRITE_DAC,
|
||||
PIPE_TYPE_MESSAGE |
|
||||
PIPE_READMODE_MESSAGE |
|
||||
PIPE_WAIT,
|
||||
PIPE_UNLIMITED_INSTANCES,
|
||||
BUFSIZE,
|
||||
BUFSIZE,
|
||||
0,
|
||||
NULL);
|
||||
|
||||
if (connect_pipe_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (token != NULL)
|
||||
{
|
||||
int err = change_pipe_security_allow_restricted_token(connect_pipe_handle, token);
|
||||
}
|
||||
current_connect_pipe_handle = connect_pipe_handle;
|
||||
}
|
||||
connected = ConnectNamedPipe(connect_pipe_handle, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
|
||||
{
|
||||
std::unique_lock lock(pipe_connect_handle_mutex);
|
||||
current_connect_pipe_handle = NULL;
|
||||
}
|
||||
if (connected)
|
||||
{
|
||||
std::thread(&TwoWayPipeMessageIPC::handle_pipe_connection, this, connect_pipe_handle).detach();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Client could not connect.
|
||||
CloseHandle(connect_pipe_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void consume_input_queue_thread()
|
||||
{
|
||||
while (!closed)
|
||||
{
|
||||
outgoing_message = L"";
|
||||
std::wstring message = input_queue.pop_message();
|
||||
if (message.length() == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if callback method exists first before trying to call it.
|
||||
// otherwise just store the response message in a variable.
|
||||
if (dispatch_inc_message_function != nullptr)
|
||||
{
|
||||
dispatch_inc_message_function(message);
|
||||
}
|
||||
outgoing_message = message;
|
||||
}
|
||||
}
|
||||
};
|
||||
class TwoWayPipeMessageIPCImpl;
|
||||
TwoWayPipeMessageIPCImpl* impl;
|
||||
};
|
||||
43
src/common/two_way_pipe_message_ipc_impl.h
Normal file
43
src/common/two_way_pipe_message_ipc_impl.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include "async_message_queue.h"
|
||||
#include <WinSafer.h>
|
||||
#include <accctrl.h>
|
||||
#include <aclapi.h>
|
||||
#include <list>
|
||||
#include "two_way_pipe_message_ipc.h"
|
||||
|
||||
class TwoWayPipeMessageIPC::TwoWayPipeMessageIPCImpl
|
||||
{
|
||||
public:
|
||||
void send(std::wstring msg);
|
||||
TwoWayPipeMessageIPCImpl(std::wstring _input_pipe_name, std::wstring _output_pipe_name, callback_function p_func);
|
||||
void start(HANDLE _restricted_pipe_token);
|
||||
void end();
|
||||
|
||||
private:
|
||||
AsyncMessageQueue input_queue;
|
||||
AsyncMessageQueue output_queue;
|
||||
std::wstring output_pipe_name;
|
||||
std::wstring input_pipe_name;
|
||||
std::thread input_queue_thread;
|
||||
std::thread output_queue_thread;
|
||||
std::thread input_pipe_thread;
|
||||
std::mutex pipe_connect_handle_mutex; // For manipulating the current_connect_pipe
|
||||
std::wstring outgoing_message; // Store the updated json settings.
|
||||
|
||||
HANDLE current_connect_pipe_handle = NULL;
|
||||
bool closed = false;
|
||||
TwoWayPipeMessageIPC::callback_function dispatch_inc_message_function;
|
||||
const DWORD BUFSIZE = 1024;
|
||||
|
||||
void send_pipe_message(std::wstring message);
|
||||
void consume_output_queue_thread();
|
||||
BOOL GetLogonSID(HANDLE hToken, PSID* ppsid);
|
||||
VOID FreeLogonSID(PSID* ppsid);
|
||||
int change_pipe_security_allow_restricted_token(HANDLE handle, HANDLE token);
|
||||
HANDLE create_medium_integrity_token();
|
||||
void handle_pipe_connection(HANDLE input_pipe_handle);
|
||||
void start_named_pipe_server(HANDLE token);
|
||||
void consume_input_queue_thread();
|
||||
};
|
||||
Reference in New Issue
Block a user