feat: initiale Version 0.1.0 des MSTSC Script Hooks
- Native x64 MSTSC-DVC-Erweiterung für lokalen Programmstart - Automatischer Script-/Programmstart bei RDP-Verbindungsaufbau - Separater Programmstart über Trigger innerhalb der RDP-Sitzung - Bidirektionaler DVC zur Ermittlung des lokalen Client-Hostnamens - SIOMIN-Trigger zur Anbindung lokaler Sidexis-Installationen - SIOMIN-Datensatzlängen und Hostname-Felder bytegenau angepasst - Temporäre SIOMIN-Einträge nach erfolgreicher Übernahme bereinigt - Native Win32-Konfigurationsoberfläche ohne .NET-Abhängigkeit - Mehrfachstartschutz und optionaler unsichtbarer Programmstart - Debug- und Plugin-Logging ergänzt - RemoteVDDS-Anwendungsfall integriert - Installer/Registrierung des MSTSC-Plugins über Client-Konfigurator - Techniker-, Entwickler- und Projektdokumentation ergänzt - Proprietäre Lizenz für Copyright Patrick Gniza hinzugefügt
This commit is contained in:
+57
-11
@@ -1,33 +1,45 @@
|
||||
#include "../Shared/TriggerProtocol.h"
|
||||
#include "../Shared/DvcServerIo.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <wtsapi32.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <cwchar>
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr DWORD kAckTimeoutMs = 5000;
|
||||
|
||||
enum class ExitCode : int
|
||||
{
|
||||
Success = 0,
|
||||
ChannelOpenFailed = 10,
|
||||
WriteFailed = 11,
|
||||
PartialWrite = 12
|
||||
PartialWrite = 12,
|
||||
AckReadFailed = 13,
|
||||
StartRejected = 14,
|
||||
InvalidAck = 15
|
||||
};
|
||||
|
||||
void DebugError(const wchar_t* step, DWORD error)
|
||||
{
|
||||
wchar_t buffer[256]{};
|
||||
swprintf_s(buffer, _countof(buffer), L"PlandentRdpClientTrigger: %s fehlgeschlagen, Win32=%lu\n", step, error);
|
||||
swprintf_s(
|
||||
buffer,
|
||||
(sizeof(buffer) / sizeof(buffer[0])),
|
||||
L"PlandentRdpClientTrigger: %s fehlgeschlagen, Win32=%lu\n",
|
||||
step,
|
||||
error);
|
||||
OutputDebugStringW(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
int APIENTRY wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
|
||||
{
|
||||
// WTS_CURRENT_SESSION stellt sicher, dass ausschließlich der Client der
|
||||
// RDP-Sitzung angesprochen wird, in der diese EXE ausgeführt wird.
|
||||
// WTS_CURRENT_SESSION stellt sicher, dass ausschliesslich der Client der
|
||||
// RDP-Sitzung angesprochen wird, in der diese EXE ausgefuehrt wird.
|
||||
HANDLE channel = WTSVirtualChannelOpenEx(
|
||||
WTS_CURRENT_SESSION,
|
||||
const_cast<LPSTR>(hook::protocol::kChannelName),
|
||||
@@ -40,26 +52,60 @@ int APIENTRY wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
|
||||
}
|
||||
|
||||
ULONG bytesWritten = 0;
|
||||
BOOL written = WTSVirtualChannelWrite(
|
||||
const BOOL written = WTSVirtualChannelWrite(
|
||||
channel,
|
||||
const_cast<PCHAR>(hook::protocol::kStartMessage),
|
||||
static_cast<ULONG>(hook::protocol::kStartMessageLength),
|
||||
&bytesWritten);
|
||||
|
||||
DWORD writeError = written ? ERROR_SUCCESS : GetLastError();
|
||||
WTSVirtualChannelClose(channel);
|
||||
|
||||
if (!written)
|
||||
{
|
||||
DebugError(L"WTSVirtualChannelWrite", writeError);
|
||||
const DWORD error = GetLastError();
|
||||
WTSVirtualChannelClose(channel);
|
||||
DebugError(L"WTSVirtualChannelWrite", error);
|
||||
return static_cast<int>(ExitCode::WriteFailed);
|
||||
}
|
||||
|
||||
if (bytesWritten != static_cast<ULONG>(hook::protocol::kStartMessageLength))
|
||||
{
|
||||
WTSVirtualChannelClose(channel);
|
||||
OutputDebugStringW(L"PlandentRdpClientTrigger: Trigger-Nachricht wurde nur teilweise geschrieben.\n");
|
||||
return static_cast<int>(ExitCode::PartialWrite);
|
||||
}
|
||||
|
||||
return static_cast<int>(ExitCode::Success);
|
||||
// Wichtig: den DVC nicht direkt nach WTSVirtualChannelWrite schliessen.
|
||||
// Der Client quittiert START erst nach Verarbeitung. Dadurch bleibt der
|
||||
// Kanal lange genug bestehen, bis OnDataReceived die Nachricht erhalten hat.
|
||||
std::vector<char> response;
|
||||
DWORD readError = ERROR_SUCCESS;
|
||||
const bool readOk = hook::dvc::ReadMessage(channel, kAckTimeoutMs, response, readError);
|
||||
WTSVirtualChannelClose(channel);
|
||||
|
||||
if (!readOk)
|
||||
{
|
||||
DebugError(L"START-Quittung lesen", readError);
|
||||
return static_cast<int>(ExitCode::AckReadFailed);
|
||||
}
|
||||
|
||||
if (response.size() == hook::protocol::kStartOkResponseLength &&
|
||||
std::memcmp(
|
||||
response.data(),
|
||||
hook::protocol::kStartOkResponse,
|
||||
hook::protocol::kStartOkResponseLength) == 0)
|
||||
{
|
||||
return static_cast<int>(ExitCode::Success);
|
||||
}
|
||||
|
||||
if (response.size() == hook::protocol::kStartFailedResponseLength &&
|
||||
std::memcmp(
|
||||
response.data(),
|
||||
hook::protocol::kStartFailedResponse,
|
||||
hook::protocol::kStartFailedResponseLength) == 0)
|
||||
{
|
||||
OutputDebugStringW(L"PlandentRdpClientTrigger: Client hat den Programmstart abgelehnt/als fehlgeschlagen gemeldet.\n");
|
||||
return static_cast<int>(ExitCode::StartRejected);
|
||||
}
|
||||
|
||||
OutputDebugStringW(L"PlandentRdpClientTrigger: Ungueltige START-Quittung empfangen.\n");
|
||||
return static_cast<int>(ExitCode::InvalidAck);
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
<PreprocessorDefinitions>WIN32_LEAN_AND_MEAN;NOMINMAX;UNICODE;_UNICODE;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
@@ -70,6 +71,7 @@
|
||||
<PreprocessorDefinitions>WIN32_LEAN_AND_MEAN;NOMINMAX;UNICODE;_UNICODE;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
@@ -85,6 +87,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\Shared\TriggerProtocol.h" />
|
||||
<ClInclude Include="..\Shared\DvcServerIo.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets" />
|
||||
|
||||
@@ -9,5 +9,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\Shared\TriggerProtocol.h"><Filter>Header Files</Filter></ClInclude>
|
||||
<ClInclude Include="..\Shared\DvcServerIo.h"><Filter>Header Files</Filter></ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user