From 6a4edfc6b6db15b72f049198a97c2c10f856e4ea Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Fri, 28 Mar 2025 12:05:02 +0000 Subject: [PATCH] [idd] helper: added new helper service As the IDD itself runs in a WUMDF sandbox, it doesn't have enough access to perform interactive operations such as moving the cursor. This helper service communicates with the IDD over a named pipe, so that we can perform these things, as well as in the future provide a configuration GUI. --- .gitignore | 6 +- idd/{LGIdd => LGCommon}/CDebug.cpp | 8 +- idd/{LGIdd => LGCommon}/CDebug.h | 3 +- idd/LGCommon/PipeMsg.h | 43 ++ idd/LGIdd.sln | 26 ++ idd/LGIdd/CIndirectDeviceContext.cpp | 12 +- idd/LGIdd/CPipeServer.cpp | 163 ++++++++ idd/LGIdd/CPipeServer.h | 59 +++ idd/LGIdd/Driver.cpp | 19 +- idd/LGIdd/LGIdd.inf | Bin 3490 -> 4286 bytes idd/LGIdd/LGIdd.vcxproj | 17 +- idd/LGIddHelper/CPipeClient.cpp | 231 +++++++++++ idd/LGIddHelper/CPipeClient.h | 60 +++ idd/LGIddHelper/LGIddHelper.vcxproj | 188 +++++++++ idd/LGIddHelper/LGIddHelper.vcxproj.filters | 29 ++ idd/LGIddHelper/VersionInfo.h | 2 + idd/LGIddHelper/main.cpp | 421 ++++++++++++++++++++ idd/LGIddHelper/packages.config | 4 + 18 files changed, 1267 insertions(+), 24 deletions(-) rename idd/{LGIdd => LGCommon}/CDebug.cpp (96%) rename idd/{LGIdd => LGCommon}/CDebug.h (98%) create mode 100644 idd/LGCommon/PipeMsg.h create mode 100644 idd/LGIdd/CPipeServer.cpp create mode 100644 idd/LGIdd/CPipeServer.h create mode 100644 idd/LGIddHelper/CPipeClient.cpp create mode 100644 idd/LGIddHelper/CPipeClient.h create mode 100644 idd/LGIddHelper/LGIddHelper.vcxproj create mode 100644 idd/LGIddHelper/LGIddHelper.vcxproj.filters create mode 100644 idd/LGIddHelper/VersionInfo.h create mode 100644 idd/LGIddHelper/main.cpp create mode 100644 idd/LGIddHelper/packages.config diff --git a/.gitignore b/.gitignore index 2fc64dc7..7194c9bf 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,7 @@ __pycache__ *.user idd/Debug idd/x64 -idd/LGIdd/x64 -idd/LGIdd/Debug -idd/LGIdd/VersionInfo.h +idd/*/x64 +idd/*/Debug +idd/*/VersionInfo.h idd/packages diff --git a/idd/LGIdd/CDebug.cpp b/idd/LGCommon/CDebug.cpp similarity index 96% rename from idd/LGIdd/CDebug.cpp rename to idd/LGCommon/CDebug.cpp index 73ec19a6..34bc2f8c 100644 --- a/idd/LGIdd/CDebug.cpp +++ b/idd/LGCommon/CDebug.cpp @@ -28,7 +28,7 @@ CDebug g_debug; -CDebug::CDebug() +void CDebug::Init(const char * name) { // don't redirect the debug output if running under a debugger if (IsDebuggerPresent()) @@ -44,13 +44,13 @@ CDebug::CDebug() } std::string folder = tempPath; - std::string baseName = "looking-glass-idd"; + std::string baseName = name; std::string ext = ".txt"; std::string logFile = folder + baseName + ext; //rotate out old logs - DeleteFileA((folder + baseName + ".5" + ext).c_str()); - for (int i = 4; i >= 0; --i) + DeleteFileA((folder + baseName + ".4" + ext).c_str()); + for (int i = 3; i >= 0; --i) { std::string oldPath; std::string newPath; diff --git a/idd/LGIdd/CDebug.h b/idd/LGCommon/CDebug.h similarity index 98% rename from idd/LGIdd/CDebug.h rename to idd/LGCommon/CDebug.h index 2a5b88d2..ade0e8bb 100644 --- a/idd/LGIdd/CDebug.h +++ b/idd/LGCommon/CDebug.h @@ -21,7 +21,6 @@ #pragma once #include -#include #include class CDebug @@ -31,7 +30,6 @@ class CDebug void Write(const char * line); public: - CDebug(); enum Level { @@ -46,6 +44,7 @@ class CDebug LEVEL_MAX }; + void Init(const char * name); void Log(CDebug::Level level, const char * function, int line, const char * fmt, ...); void LogHR(CDebug::Level level, HRESULT hr, const char* function, int line, const char* fmt, ...); diff --git a/idd/LGCommon/PipeMsg.h b/idd/LGCommon/PipeMsg.h new file mode 100644 index 00000000..043aa2ad --- /dev/null +++ b/idd/LGCommon/PipeMsg.h @@ -0,0 +1,43 @@ +/** + * Looking Glass + * Copyright © 2017-2025 The Looking Glass Authors + * https://looking-glass.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#pragma once + +#include + +#define LG_PIPE_NAME "\\\\.\\pipe\\LookingGlassIDD" + +struct LGPipeMsg +{ + unsigned size; + enum + { + SETCURSORPOS + } + type; + union + { + struct + { + uint32_t x; + uint32_t y; + } + curorPos; + }; +}; \ No newline at end of file diff --git a/idd/LGIdd.sln b/idd/LGIdd.sln index 8097eb66..d819e628 100644 --- a/idd/LGIdd.sln +++ b/idd/LGIdd.sln @@ -5,11 +5,21 @@ VisualStudioVersion = 17.5.33530.505 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LGIdd", "LGIdd\LGIdd.vcxproj", "{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}" ProjectSection(ProjectDependencies) = postProject + {0045D7AD-3F26-4B87-81CB-78D18839596D} = {0045D7AD-3F26-4B87-81CB-78D18839596D} {FFCC376C-4D98-4B50-B431-E1BBC9C67E65} = {FFCC376C-4D98-4B50-B431-E1BBC9C67E65} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LGMP", "..\repos\LGMP\LGMP.vcxproj", "{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LGIddHelper", "LGIddHelper\LGIddHelper.vcxproj", "{0045D7AD-3F26-4B87-81CB-78D18839596D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LGCommon", "LGCommon", "{ACB90E34-01CA-4B86-813B-3D20904994C6}" + ProjectSection(SolutionItems) = preProject + LGCommon\CDebug.cpp = LGCommon\CDebug.cpp + LGCommon\CDebug.h = LGCommon\CDebug.h + LGCommon\PipeMsg.h = LGCommon\PipeMsg.h + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM @@ -60,6 +70,22 @@ Global {FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|x86.ActiveCfg = Release|Win32 {FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|x86.Build.0 = Release|Win32 {FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|x86.Deploy.0 = Release|Win32 + {0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|ARM.ActiveCfg = Debug|x64 + {0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|ARM.Build.0 = Debug|x64 + {0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|ARM64.ActiveCfg = Debug|x64 + {0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|ARM64.Build.0 = Debug|x64 + {0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|x64.ActiveCfg = Debug|x64 + {0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|x64.Build.0 = Debug|x64 + {0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|x86.ActiveCfg = Debug|Win32 + {0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|x86.Build.0 = Debug|Win32 + {0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|ARM.ActiveCfg = Release|x64 + {0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|ARM.Build.0 = Release|x64 + {0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|ARM64.ActiveCfg = Release|x64 + {0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|ARM64.Build.0 = Release|x64 + {0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|x64.ActiveCfg = Release|x64 + {0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|x64.Build.0 = Release|x64 + {0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|x86.ActiveCfg = Release|Win32 + {0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/idd/LGIdd/CIndirectDeviceContext.cpp b/idd/LGIdd/CIndirectDeviceContext.cpp index 0e14e492..255be894 100644 --- a/idd/LGIdd/CIndirectDeviceContext.cpp +++ b/idd/LGIdd/CIndirectDeviceContext.cpp @@ -22,6 +22,7 @@ #include "CIndirectMonitorContext.h" #include "CPlatformInfo.h" +#include "CPipeServer.h" #include "CDebug.h" #include "VersionInfo.h" @@ -114,7 +115,7 @@ void CIndirectDeviceContext::InitAdapter() factory->Release(); auto * wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(m_adapter); - wrapper->context = this; + wrapper->context = this; } static const BYTE EDID[] = @@ -172,6 +173,11 @@ void CIndirectDeviceContext::FinishInit(UINT connectorIndex) IDARG_OUT_MONITORARRIVAL out; status = IddCxMonitorArrival(m_monitor, &out); + if (FAILED(status)) + { + DEBUG_ERROR_HR(status, "IddCxMonitorArrival Failed"); + return; + } } bool CIndirectDeviceContext::SetupLGMP(size_t alignSize) @@ -386,8 +392,8 @@ void CIndirectDeviceContext::LGMPTimer() { case KVMFR_MESSAGE_SETCURSORPOS: { - KVMFRSetCursorPos *sp = (KVMFRSetCursorPos *)msg; - SetCursorPos(sp->x, sp->y); + KVMFRSetCursorPos* sp = (KVMFRSetCursorPos*)msg; + g_pipe.SetCursorPos(sp->x, sp->y); break; } } diff --git a/idd/LGIdd/CPipeServer.cpp b/idd/LGIdd/CPipeServer.cpp new file mode 100644 index 00000000..05d62022 --- /dev/null +++ b/idd/LGIdd/CPipeServer.cpp @@ -0,0 +1,163 @@ +/** + * Looking Glass + * Copyright © 2017-2025 The Looking Glass Authors + * https://looking-glass.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "CPipeServer.h" +#include "CDebug.h" + +CPipeServer g_pipe; + +bool CPipeServer::Init() +{ + _DeInit(); + + m_pipe.Attach(CreateNamedPipeA( + LG_PIPE_NAME, + PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + 1, + 1024, + 1024, + 0, + NULL)); + + if (!m_pipe.IsValid()) + { + DEBUG_ERROR_HR(GetLastError(), "Failed to create the named pipe"); + return false; + } + + m_running = true; + m_thread.Attach(CreateThread( + NULL, + 0, + _pipeThread, + (LPVOID)this, + 0, + NULL)); + + if (!m_thread.IsValid()) + { + DEBUG_ERROR_HR(GetLastError(), "Failed to create the pipe thread"); + return false; + } + + DEBUG_TRACE("Pipe Initialized"); + return true; +} + +void CPipeServer::_DeInit() +{ + m_running = false; + m_connected = false; + + if (m_thread.IsValid()) + { + CancelSynchronousIo(m_thread.Get()); + WaitForSingleObject(m_thread.Get(), INFINITE); + m_thread.Close(); + } + + if (m_pipe.IsValid()) + { + FlushFileBuffers(m_pipe.Get()); + m_pipe.Close(); + } +} + +void CPipeServer::DeInit() +{ + DEBUG_TRACE("Pipe Stopping"); + _DeInit(); + DEBUG_TRACE("Pipe Stopped"); +} + +void CPipeServer::Thread() +{ + DEBUG_TRACE("Pipe thread started"); + while(m_running) + { + m_connected = false; + bool result = ConnectNamedPipe(m_pipe.Get(), NULL); + DWORD err = GetLastError(); + if (!result && err != ERROR_PIPE_CONNECTED) + { + // if graceful shutdown + if ((err == ERROR_OPERATION_ABORTED && !m_running) || + err == ERROR_NO_DATA) + break; + + // if timeout + if (err == ERROR_SEM_TIMEOUT) + continue; + + DEBUG_FATAL_HR(err, "Error connecting to the named pipe"); + break; + } + + DEBUG_TRACE("Client connected"); + + m_connected = true; + while (m_running && m_connected) + { + //TODO: Read messages from the client + Sleep(1000); + } + + DEBUG_TRACE("Client disconnected"); + DisconnectNamedPipe(m_pipe.Get()); + } + + m_running = false; + m_connected = false; + DEBUG_TRACE("Pipe thread shutdown"); +} + +void CPipeServer::WriteMsg(LGPipeMsg & msg) +{ + DWORD written; + if (!WriteFile(m_pipe.Get(), &msg, sizeof(msg), &written, NULL)) + { + DWORD err = GetLastError(); + if (err == ERROR_BROKEN_PIPE || err == ERROR_NO_DATA) + { + DEBUG_WARN_HR(err, "Client disconnected, failed to write"); + m_connected = false; + return; + } + + DEBUG_WARN_HR(err, "WriteFile failed on the pipe"); + return; + } + + FlushFileBuffers(m_pipe.Get()); +} + +void CPipeServer::SetCursorPos(uint32_t x, uint32_t y) +{ + if (!m_connected) + return; + + LGPipeMsg msg; + msg.size = sizeof(msg); + msg.type = LGPipeMsg::SETCURSORPOS; + msg.curorPos.x = x; + msg.curorPos.y = y; + WriteMsg(msg); +} \ No newline at end of file diff --git a/idd/LGIdd/CPipeServer.h b/idd/LGIdd/CPipeServer.h new file mode 100644 index 00000000..67a18d1a --- /dev/null +++ b/idd/LGIdd/CPipeServer.h @@ -0,0 +1,59 @@ +/** + * Looking Glass + * Copyright © 2017-2025 The Looking Glass Authors + * https://looking-glass.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#pragma once + +#include +#include +#include +#include + +#include "PipeMsg.h" + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace Microsoft::WRL::Wrappers::HandleTraits; + +class CPipeServer +{ + private: + HandleT m_pipe; + HandleT m_thread; + + bool m_running = false; + bool m_connected = false; + + void _DeInit(); + + static DWORD WINAPI _pipeThread(LPVOID lpParam) { ((CPipeServer*)lpParam)->Thread(); return 0; } + void Thread(); + + void WriteMsg(LGPipeMsg & msg); + + public: + ~CPipeServer() { DeInit(); } + + bool Init(); + void DeInit(); + + void SetCursorPos(uint32_t x, uint32_t y); +}; + +extern CPipeServer g_pipe; \ No newline at end of file diff --git a/idd/LGIdd/Driver.cpp b/idd/LGIdd/Driver.cpp index 5f621c1e..938588d7 100644 --- a/idd/LGIdd/Driver.cpp +++ b/idd/LGIdd/Driver.cpp @@ -24,21 +24,30 @@ #include "CDebug.h" #include "CPlatformInfo.h" #include "VersionInfo.h" +#include "CPipeServer.h" NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) { + g_debug.Init("looking-glass-idd"); DEBUG_INFO("Looking Glass IDD Driver (" LG_VERSION_STR ")"); - WDF_DRIVER_CONFIG config; - NTSTATUS status; - WDF_OBJECT_ATTRIBUTES attributes; - + NTSTATUS status = STATUS_SUCCESS; #if UMDF_VERSION_MAJOR == 2 && UMDF_VERSION_MINOR == 0 WPP_INIT_TRACING(MYDRIVER_TRACING_ID); #else WPP_INIT_TRACING(DriverObject, RegistryPath); #endif + if (!g_pipe.Init()) + { + status = STATUS_UNSUCCESSFUL; + DEBUG_ERROR("Failed to setup IPC pipe"); + goto fail; + } + + WDF_DRIVER_CONFIG config; + WDF_OBJECT_ATTRIBUTES attributes; + TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry"); WDF_OBJECT_ATTRIBUTES_INIT(&attributes); @@ -82,6 +91,8 @@ VOID LGIddEvtDriverContextCleanup(_In_ WDFOBJECT DriverObject) TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry"); + g_pipe.DeInit(); + #if UMDF_VERSION_MAJOR == 2 && UMDF_VERSION_MINOR == 0 WPP_CLEANUP(); #else diff --git a/idd/LGIdd/LGIdd.inf b/idd/LGIdd/LGIdd.inf index bc7681be325211d7e4d5b88e1f9f00607ae3dfa5..47de49de34a9491a4c305d4abca6e2cfc4cb2bc5 100644 GIT binary patch delta 580 zcmaJ;!Ab&A6g@d%GdeAzvPD?aLL!owjFxV4BBgfO2goEPnAtdJuA*n0ZL2Du3#oK$Pf`|FM?AT>0{J!9u5y~yqm~XA3(z2E$b5^|kXO&I;D+^p)viW5 zxS%5raV?^i8#Q*1`+B?yaZQBEb#8ZOXCHie*WgL@47I4vE^9uklX}Lg-PyW1LtzUV zbxc0BWq^`a5b9Ht_e4p@I*_utvK(_~^osIseb0Vp=^Ka9ODdC?Fqam}PkLSgdt=Fi L$1yQ-`LG9n+Awfu delta 58 zcmV-A0LA~lA)*_wG6S<%19$_oOb0*$laL8U13dsBli>*)lLQJRll%)blU@oQlVS|G QlcWPMvuq6(0+Zek;2?n$FaQ7m diff --git a/idd/LGIdd/LGIdd.vcxproj b/idd/LGIdd/LGIdd.vcxproj index e1437f81..629cf94e 100644 --- a/idd/LGIdd/LGIdd.vcxproj +++ b/idd/LGIdd/LGIdd.vcxproj @@ -40,6 +40,7 @@ + @@ -48,15 +49,16 @@ + - + @@ -64,10 +66,10 @@ + - @@ -99,12 +101,10 @@ Universal - WindowsUserModeDriver10.0 DynamicLibrary Universal - WindowsUserModeDriver10.0 DynamicLibrary Universal @@ -164,6 +164,7 @@ 25 25 <_NT_TARGET_VERSION>0xA000005 + WindowsUserModeDriver10.0 Windows10 @@ -176,6 +177,7 @@ 25 25 <_NT_TARGET_VERSION>0xA000005 + WindowsUserModeDriver10.0 Windows10 @@ -268,7 +270,7 @@ true trace.h /EHsc /D_ATL_NO_WIN_SUPPORT /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=9 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions) - $(ProjectDir)..\..\repos\LGMP\lgmp\include;$(ProjectDir)..\..\vendor;$(ProjectDir)..\..\common\include;%(AdditionalIncludeDirectories) + $(SolutionDir)LGCommon;$(SolutionDir)..\repos\LGMP\lgmp\include;$(SolutionDir)..\vendor;$(SolutionDir)..\common\include;%(AdditionalIncludeDirectories) %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib;d3d12.lib @@ -283,7 +285,7 @@ true trace.h /EHsc /D_ATL_NO_WIN_SUPPORT /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=9 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions) - $(ProjectDir)..\..\repos\LGMP\lgmp\include;$(ProjectDir)..\..\vendor;$(ProjectDir)..\..\common\include;%(AdditionalIncludeDirectories) + $(SolutionDir)LGCommon;$(SolutionDir)..\repos\LGMP\lgmp\include;$(SolutionDir)..\vendor;$(SolutionDir)..\common\include;%(AdditionalIncludeDirectories) %(AdditionalDependencies);OneCoreUAP.lib;avrt.lib;d3d12.lib @@ -330,11 +332,10 @@ - + 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}. - diff --git a/idd/LGIddHelper/CPipeClient.cpp b/idd/LGIddHelper/CPipeClient.cpp new file mode 100644 index 00000000..84a1427f --- /dev/null +++ b/idd/LGIddHelper/CPipeClient.cpp @@ -0,0 +1,231 @@ +/** + * Looking Glass + * Copyright © 2017-2025 The Looking Glass Authors + * https://looking-glass.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "CPipeClient.h" +#include "CDebug.h" + +#include +#include + +CPipeClient g_pipe; + +bool CPipeClient::Init() +{ + DeInit(); + if (!IsLGIddDeviceAttached()) + { + DEBUG_ERROR("Looking Glass Indirect Display Device not found"); + return false; + } + + m_running = true; + m_thread.Attach(CreateThread( + NULL, + 0, + _pipeThread, + (LPVOID)this, + 0, + NULL)); + + if (!m_thread.IsValid()) + { + DEBUG_ERROR_HR(GetLastError(), "Failed to create the pipe thread"); + return false; + } + + return true; +} + +void CPipeClient::DeInit() +{ + m_connected = false; + if (m_thread.IsValid()) + { + m_running = false; + WaitForSingleObject(m_thread.Get(), INFINITE); + m_thread.Close(); + } + + if (m_pipe.IsValid()) + { + FlushFileBuffers(m_pipe.Get()); + m_pipe.Close(); + } +} + +bool CPipeClient::IsLGIddDeviceAttached() +{ + HDEVINFO hDevInfo = SetupDiGetClassDevs( + NULL, + NULL, + NULL, + DIGCF_ALLCLASSES | DIGCF_PRESENT + ); + + if (hDevInfo == INVALID_HANDLE_VALUE) + return false; + + SP_DEVINFO_DATA DeviceInfoData; + DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + bool found = false; + + for (DWORD i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData); i++) + { + DWORD DataT; + TCHAR buffer[1024]; + DWORD buffersize = 0; + + if (!SetupDiGetDeviceRegistryProperty( + hDevInfo, + &DeviceInfoData, + SPDRP_HARDWAREID, + &DataT, + (PBYTE)buffer, + sizeof(buffer), + &buffersize)) + continue; + + for (LPCTSTR p = buffer; *p; p += _tcslen(p) + 1) + if (_tcsicmp(p, _T("Root\\LGIdd")) == 0) + { + found = true; + break; + } + + if (found) + break; + } + + SetupDiDestroyDeviceInfoList(hDevInfo); + return found; +} + +/* APIs like SetCursorPos are applied to the desktop our thread is + * attached to. If the user switches to the secure desktop (UAC, etc) + * then these functions will not work, so call this first to ensure + * the call is effective */ +void CPipeClient::SetActiveDesktop() +{ + HDESK desktop = NULL; + desktop = OpenInputDesktop(0, FALSE, GENERIC_READ); + if (!desktop) + DEBUG_ERROR_HR(GetLastError(), "OpenInputDesktop Failed"); + else + { + if (!SetThreadDesktop(desktop)) + DEBUG_ERROR_HR(GetLastError(), "SetThreadDesktop Failed"); + CloseDesktop(desktop); + } +} + +void CPipeClient::WriteMsg(const LGPipeMsg& msg) +{ + DWORD written; + if (!WriteFile(m_pipe.Get(), &msg, sizeof(msg), &written, NULL)) + { + DWORD err = GetLastError(); + if (err == ERROR_BROKEN_PIPE) + { + DEBUG_WARN_HR(err, "Client disconnected, failed to write"); + m_connected = false; + return; + } + + DEBUG_WARN_HR(err, "WriteFile failed on the pipe"); + return; + } + + FlushFileBuffers(m_pipe.Get()); +} + +void CPipeClient::Thread() +{ + DEBUG_INFO("Pipe thread started"); + while (m_running) + { + if (!WaitNamedPipeA(LG_PIPE_NAME, 5000)) + { + if (!IsLGIddDeviceAttached()) + { + m_running = false; + DEBUG_ERROR("Device is no longer available, shutting down"); + break; + } + continue; + } + + m_pipe.Attach(CreateFileA( + LG_PIPE_NAME, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL + )); + + if (!m_pipe.IsValid()) + { + DEBUG_ERROR_HR(GetLastError(), "Failed to open the named pipe"); + continue; + } + + m_connected = true; + DEBUG_INFO("Pipe connected"); + while (m_running && m_connected) + { + LGPipeMsg msg; + DWORD bytesRead; + if (!ReadFile(m_pipe.Get(), &msg, sizeof(msg), &bytesRead, NULL)) + { + DEBUG_ERROR_HR(GetLastError(), "ReadFile Failed"); + break; + } + + if (bytesRead != sizeof(msg) || msg.size != sizeof(msg)) + { + DEBUG_ERROR("Corrupted data"); + break; + } + + switch (msg.type) + { + case LGPipeMsg::SETCURSORPOS: + HandleSetCursorPos(msg); + break; + + default: + DEBUG_ERROR("Unknown message type %d", msg.type); + break; + } + } + + m_pipe.Close(); + m_connected = false; + DEBUG_INFO("Pipe closed"); + } + DEBUG_INFO("Pipe thread shutdown"); +} + +void CPipeClient::HandleSetCursorPos(const LGPipeMsg& msg) +{ + SetActiveDesktop(); + SetCursorPos(msg.curorPos.x, msg.curorPos.y); +} \ No newline at end of file diff --git a/idd/LGIddHelper/CPipeClient.h b/idd/LGIddHelper/CPipeClient.h new file mode 100644 index 00000000..8dbe9959 --- /dev/null +++ b/idd/LGIddHelper/CPipeClient.h @@ -0,0 +1,60 @@ +/** + * Looking Glass + * Copyright © 2017-2025 The Looking Glass Authors + * https://looking-glass.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#pragma once + +#include +#include +#include + +#include "PipeMsg.h" + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace Microsoft::WRL::Wrappers::HandleTraits; + +class CPipeClient +{ +private: + HandleT m_pipe; + HandleT m_thread; + bool m_running = false; + bool m_connected = false; + + static DWORD WINAPI _pipeThread(LPVOID lpParam) { ((CPipeClient*)lpParam)->Thread(); return 0; } + void Thread(); + + void WriteMsg(const LGPipeMsg& msg); + + void SetActiveDesktop(); + + void HandleSetCursorPos(const LGPipeMsg& msg); + +public: + ~CPipeClient() { DeInit(); } + + static bool IsLGIddDeviceAttached(); + + bool Init(); + void DeInit(); + bool IsRunning() { return m_running; } +}; + +extern CPipeClient g_pipe; \ No newline at end of file diff --git a/idd/LGIddHelper/LGIddHelper.vcxproj b/idd/LGIddHelper/LGIddHelper.vcxproj new file mode 100644 index 00000000..0ab4233d --- /dev/null +++ b/idd/LGIddHelper/LGIddHelper.vcxproj @@ -0,0 +1,188 @@ + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {0045d7ad-3f26-4b87-81cb-78d18839596d} + LGIddHelper + 10.0.22621.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + NotSet + + + Application + false + v143 + true + NotSet + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(SolutionDir)LGCommon;%(AdditionalIncludeDirectories) + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(SolutionDir)LGCommon;%(AdditionalIncludeDirectories) + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + Default + $(SolutionDir)LGCommon;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Windows + true + %(AdditionalDependencies);Rpcrt4.lib;Userenv.lib;setupapi.lib + + + if not exist "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd" mkdir "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd" +copy /Y "$(TargetPath)" "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd\" + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Default + Default + $(SolutionDir)LGCommon;%(AdditionalIncludeDirectories) + MultiThreaded + + + Windows + true + true + true + %(AdditionalDependencies);Rpcrt4.lib;Userenv.lib;setupapi.lib + + + if not exist "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd" mkdir "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd" +copy /Y "$(TargetPath)" "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd\" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $([System.DateTime]::Now.ToString("yyyy")) + + GenerateVersionInfo; + $(BuildDependsOn); + + + + + 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}. + + + + \ No newline at end of file diff --git a/idd/LGIddHelper/LGIddHelper.vcxproj.filters b/idd/LGIddHelper/LGIddHelper.vcxproj.filters new file mode 100644 index 00000000..c694b2d5 --- /dev/null +++ b/idd/LGIddHelper/LGIddHelper.vcxproj.filters @@ -0,0 +1,29 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + + + + + + \ No newline at end of file diff --git a/idd/LGIddHelper/VersionInfo.h b/idd/LGIddHelper/VersionInfo.h new file mode 100644 index 00000000..4e2eb9dc --- /dev/null +++ b/idd/LGIddHelper/VersionInfo.h @@ -0,0 +1,2 @@ +#define LG_VERSION_STR "B7-25-gbf59e45118+" +#define LG_CURRENT_YEAR 2025 diff --git a/idd/LGIddHelper/main.cpp b/idd/LGIddHelper/main.cpp new file mode 100644 index 00000000..04794f95 --- /dev/null +++ b/idd/LGIddHelper/main.cpp @@ -0,0 +1,421 @@ +#include +#include +#include + +#include +#include + +using namespace Microsoft::WRL::Wrappers; +using namespace Microsoft::WRL::Wrappers::HandleTraits; + +#include "CDebug.h" +#include "VersionInfo.h" +#include "CPipeClient.h" + +#define SVCNAME "Looking Glass (IDD Helper)" + +static SERVICE_STATUS_HANDLE l_svcStatusHandle; +static SERVICE_STATUS l_svcStatus; +static HandleT l_svcStopEvent; + +bool HandleService(); +static void WINAPI SvcMain(DWORD dwArgc, LPTSTR* lpszArgv); +static void WINAPI SvcCtrlHandler(DWORD dwControl); +static void ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint); + +static std::string l_executable; +static HandleT l_process; +static HandleT l_exitEvent; +static std::string l_exitEventName; + +static void Launch(); + +LRESULT CALLBACK DummyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) +{ + g_debug.Init("looking-glass-iddhelper"); + DEBUG_INFO("Looking Glass IDD Helper (" LG_VERSION_STR ")"); + + char buffer[MAX_PATH]; + DWORD result = GetModuleFileNameA(NULL, buffer, MAX_PATH); + if (result == 0) + { + DEBUG_ERROR("Failed to get the executable path"); + return EXIT_FAILURE; + } + l_executable = buffer; + + int argc = 0; + LPWSTR * wargv = CommandLineToArgvW(GetCommandLineW(), &argc); + std::vector args; + args.reserve(argc); + for (int i = 0; i < argc; ++i) + { + size_t len = wcslen(wargv[i]); + size_t bufSize = (len + 1) * 2; + std::vector buffer(bufSize); + + size_t converted = 0; + errno_t err = wcstombs_s(&converted, buffer.data(), bufSize, wargv[i], bufSize - 1); + if (err != 0) + { + DEBUG_ERROR("Conversion failed"); + return EXIT_FAILURE; + } + + args.emplace_back(buffer.data()); + } + LocalFree(wargv); + + if (argc == 1) + { + if (!HandleService()) + return EXIT_FAILURE; + return EXIT_SUCCESS; + } + + // child process + if (argc != 2) + { + // the one and only value we should see is the exit event name + DEBUG_ERROR("Invalid invocation"); + return EXIT_FAILURE; + } + + l_exitEvent.Attach(OpenEvent(SYNCHRONIZE, FALSE, args[1].c_str())); + if (!l_exitEvent.IsValid()) + { + DEBUG_ERROR_HR(GetLastError(), "Failed to open the exit event: %s", args[1].c_str()); + return EXIT_FAILURE; + } + + WNDCLASSEX wx = {}; + wx.cbSize = sizeof(WNDCLASSEX); + wx.lpfnWndProc = DummyWndProc; + wx.hInstance = hInstance; + wx.lpszClassName = "DUMMY_CLASS"; + wx.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wx.hIconSm = LoadIcon(NULL, IDI_APPLICATION); + wx.hCursor = LoadCursor(NULL, IDC_ARROW); + wx.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE; + ATOM aclass; + if (!(aclass = RegisterClassEx(&wx))) + { + DEBUG_ERROR("Failed to register message window class"); + return EXIT_FAILURE; + } + + HWND msgWnd = CreateWindowExA(0, MAKEINTATOM(aclass), NULL, + 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); + + bool running = g_pipe.Init(); + while (running) + { + switch (MsgWaitForMultipleObjects(1, l_exitEvent.GetAddressOf(), + FALSE, INFINITE, QS_ALLINPUT)) + { + case WAIT_OBJECT_0: + running = false; + break; + + case WAIT_OBJECT_0 + 1: + { + MSG msg; + if (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + break; + } + + case WAIT_FAILED: + DEBUG_ERROR_HR(GetLastError(), "MsgWaitForMultipleObjects Failed"); + running = false; + break; + } + } + g_pipe.DeInit(); + + DestroyWindow(msgWnd); + return EXIT_SUCCESS; +} + +bool HandleService() +{ + SERVICE_TABLE_ENTRY DispatchTable[] = + { + { (char *)SVCNAME, SvcMain }, + { NULL, NULL } + }; + + if (StartServiceCtrlDispatcher(DispatchTable) == FALSE) + { + DEBUG_ERROR_HR(GetLastError(), "StartServiceCtrlDispatcher Failed"); + return false; + } + + return true; +} + +static void WINAPI SvcCtrlHandler(DWORD dwControl) +{ + switch (dwControl) + { + case SERVICE_CONTROL_STOP: + ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0); + SetEvent(l_svcStopEvent.Get()); + return; + + default: + break; + } + + ReportSvcStatus(l_svcStatus.dwCurrentState, NO_ERROR, 0); +} + +static void WINAPI SvcMain(DWORD dwArgc, LPTSTR* lpszArgv) +{ + l_svcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + l_svcStatus.dwWin32ExitCode = 0; + + l_svcStatusHandle = RegisterServiceCtrlHandler(SVCNAME, SvcCtrlHandler); + if (!l_svcStatusHandle) + { + DEBUG_ERROR_HR(GetLastError(), "RegisterServiceCtrlHandler Failed"); + return; + } + + if (!CPipeClient::IsLGIddDeviceAttached()) + { + DEBUG_INFO("Looking Glass Indirect Display Device not found, not starting."); + ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0); + return; + } + + ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 0); + l_svcStopEvent.Attach(CreateEvent(NULL, TRUE, FALSE, NULL)); + if (!l_svcStopEvent.IsValid()) + { + DEBUG_ERROR_HR(GetLastError(), "CreateEvent Failed"); + ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0); + return; + } + + UUID uuid; + RPC_CSTR uuidStr; + RPC_STATUS status = UuidCreate(&uuid); + if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY && status != RPC_S_UUID_NO_ADDRESS) + { + DEBUG_ERROR("UuidCreate Failed: 0x%x", status); + ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0); + return; + } + + if (UuidToString(&uuid, &uuidStr) == RPC_S_OK) + { + l_exitEventName = "Global\\"; + l_exitEventName += (const char *)uuidStr; + RpcStringFree(&uuidStr); + + l_exitEvent.Attach(CreateEvent(NULL, FALSE, FALSE, l_exitEventName.c_str())); + if (!l_exitEvent.IsValid()) + { + DEBUG_ERROR_HR(GetLastError(), "CreateEvent Failed"); + ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0); + return; + } + } + + ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0); + bool running = true; + while (running) + { + ULONGLONG launchTime = 0ULL; + + DWORD interactiveSession = WTSGetActiveConsoleSessionId(); + if (interactiveSession != 0 && interactiveSession != 0xFFFFFFFF) + { + if (!CPipeClient::IsLGIddDeviceAttached()) + { + DEBUG_INFO("Looking Glass Indirect Display Device has gone away"); + ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0); + return; + } + + Launch(); + launchTime = GetTickCount64(); + } + + HANDLE waitOn[] = { l_svcStopEvent.Get(), l_process.Get()}; + DWORD count = 2; + DWORD duration = INFINITE; + + if (!l_process.IsValid()) + { + count = 1; + duration = 1000; + } + + switch (WaitForMultipleObjects(count, waitOn, FALSE, duration)) + { + // stop requested by the service manager + case WAIT_OBJECT_0: + running = false; + break; + + // child application exited + case WAIT_OBJECT_0 + 1: + { + DWORD code; + if (!GetExitCodeProcess(l_process.Get(), &code)) + { + DEBUG_ERROR_HR(GetLastError(), "GetExitCodeProcess Failed"); + break; + } + + DEBUG_INFO("Child process exited with code 0x%lx", code); + l_process.Close(); + break; + } + + case WAIT_FAILED: + DEBUG_ERROR_HR(GetLastError(), "Failed to WaitForMultipleObjects"); + running = false; + break; + } + + if (!running) + break; + + Sleep(1000); + } + + SetEvent(l_exitEvent.Get()); + ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0); +} + +static void ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint) +{ + static DWORD dwCheckPoint = 0; + l_svcStatus.dwCurrentState = dwCurrentState; + l_svcStatus.dwWin32ExitCode = dwWin32ExitCode; + l_svcStatus.dwWaitHint = dwWaitHint; + + if (dwCurrentState == SERVICE_START_PENDING) + l_svcStatus.dwControlsAccepted = 0; + else + l_svcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + + if ((dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED)) + l_svcStatus.dwCheckPoint = 0; + else + l_svcStatus.dwCheckPoint = ++dwCheckPoint; + + SetServiceStatus(l_svcStatusHandle, &l_svcStatus); +} + +//static void + +static bool EnablePriv(const char * name) +{ + return true; +} + +static void DisablePriv(const char * name) +{ + +} + +static void Launch() +{ + l_process.Close(); + + HandleT sysToken; + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE | + TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_ADJUST_DEFAULT, + sysToken.GetAddressOf())) + { + DEBUG_ERROR_HR(GetLastError(), "OpenProcessToken failed"); + return; + } + + HandleT token; + if (!DuplicateTokenEx(sysToken.Get(), 0, NULL, SecurityAnonymous, + TokenPrimary, token.GetAddressOf())) + { + DEBUG_ERROR_HR(GetLastError(), "DuplicateTokenEx failed"); + return; + } + + DWORD origSessionID, targetSessionID, returnedLen; + GetTokenInformation(token.Get(), TokenSessionId, &origSessionID, + sizeof(origSessionID), &returnedLen); + + targetSessionID = WTSGetActiveConsoleSessionId(); + if (origSessionID != targetSessionID) + { + if (!SetTokenInformation(token.Get(), TokenSessionId, + &targetSessionID, sizeof(targetSessionID))) + { + DEBUG_ERROR_HR(GetLastError(), "SetTokenInformation failed"); + return; + } + } + + LPVOID env = NULL; + if (!CreateEnvironmentBlock(&env, token.Get(), TRUE)) + { + DEBUG_ERROR_HR(GetLastError(), "CreateEnvironmentBlock failed"); + return; + } + + if (!EnablePriv(SE_INCREASE_QUOTA_NAME)) + { + DEBUG_ERROR("Failed to enable %s", SE_INCREASE_QUOTA_NAME); + return; + } + + PROCESS_INFORMATION pi = {0}; + STARTUPINFO si = {0}; + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_SHOW; + si.lpDesktop = (LPSTR)"WinSta0\\Default"; + + char * cmdLine = NULL; + char cmdBuf[128]; + if (l_exitEvent.IsValid()) + { + snprintf(cmdBuf, sizeof(cmdBuf), "LGIddHelper.exe %s", + l_exitEventName.c_str()); + cmdLine = cmdBuf; + } + + if (!CreateProcessAsUserA( + token.Get(), + l_executable.c_str(), + cmdLine, + NULL, + NULL, + FALSE, + DETACHED_PROCESS | HIGH_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT, + env, + NULL, + &si, + &pi + )) + { + DEBUG_ERROR_HR(GetLastError(), "CreateProcessAsUser failed"); + return; + } + + DisablePriv(SE_INCREASE_QUOTA_NAME); + + l_process.Attach(pi.hProcess); + CloseHandle(pi.hThread); +} \ No newline at end of file diff --git a/idd/LGIddHelper/packages.config b/idd/LGIddHelper/packages.config new file mode 100644 index 00000000..7c2bfcf6 --- /dev/null +++ b/idd/LGIddHelper/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file