feat: Add GeViSet file format reverse engineering specification
- Add comprehensive spec for .set file format parsing - Document binary structure, data types, and sections - Add research notes from binary analysis - Fix SetupClient password encryption (GeViAPI_EncodeString) - Add DiagnoseSetupClient tool for testing - Successfully tested: read/write 281KB config, byte-perfect round-trip - Found 64 action mappings in live server configuration Next: Full binary parser implementation for complete structure 🤖 Generated with Claude Code Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<Platforms>x86</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Serilog" Version="3.1.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GeViScopeBridge\GeViScopeBridge.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="GeViProcAPINET_4_0">
|
||||
<HintPath>C:\GEVISOFT\GeViProcAPINET_4_0.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CopyGeViSoftDLLs" AfterTargets="Build">
|
||||
<ItemGroup>
|
||||
<GeViSoftFiles Include="C:\GEVISOFT\*.dll" />
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(GeViSoftFiles)" DestinationFolder="$(OutDir)" SkipUnchangedFiles="true" />
|
||||
<Message Text="Copied GeViSoft DLLs to output directory" Importance="high" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
268
src/sdk-bridge/DiagnoseSetupClient/Program.cs
Normal file
268
src/sdk-bridge/DiagnoseSetupClient/Program.cs
Normal file
@@ -0,0 +1,268 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using GeViScopeBridge.SDK;
|
||||
|
||||
namespace DiagnoseSetupClient
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
// Configure logging
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.MinimumLevel.Debug()
|
||||
.WriteTo.Console()
|
||||
.CreateLogger();
|
||||
|
||||
try
|
||||
{
|
||||
Console.WriteLine("=== GeViSetupClient Diagnostic Tool ===\n");
|
||||
|
||||
// Get connection details from command line or interactive
|
||||
string address, username, password;
|
||||
|
||||
if (args.Length >= 3)
|
||||
{
|
||||
// Command line mode: DiagnoseSetupClient.exe <address> <username> <password>
|
||||
address = args[0];
|
||||
username = args[1];
|
||||
password = args[2];
|
||||
Console.WriteLine($"Using command-line arguments:");
|
||||
Console.WriteLine($" Address: {address}");
|
||||
Console.WriteLine($" Username: {username}");
|
||||
Console.WriteLine($" Password: {new string('*', password.Length)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Interactive mode
|
||||
Console.Write("GeViServer Address (default: localhost): ");
|
||||
address = Console.ReadLine();
|
||||
if (string.IsNullOrWhiteSpace(address))
|
||||
address = "localhost";
|
||||
|
||||
Console.Write("Username (default: admin): ");
|
||||
username = Console.ReadLine();
|
||||
if (string.IsNullOrWhiteSpace(username))
|
||||
username = "admin";
|
||||
|
||||
Console.Write("Password: ");
|
||||
password = ReadPassword();
|
||||
}
|
||||
|
||||
Console.WriteLine("\n\n1. Testing SetupClient Connection...");
|
||||
|
||||
// Try with different aliasnames
|
||||
string[] aliasnamesToTry = { "", "localhost", "GeViServer", address };
|
||||
|
||||
GeViSetupClientWrapper successfulClient = null;
|
||||
|
||||
foreach (var aliasname in aliasnamesToTry)
|
||||
{
|
||||
Console.WriteLine($"Trying with aliasname: '{aliasname}'");
|
||||
var setupClient = new GeViSetupClientWrapper(address, username, password, aliasname);
|
||||
bool connected = await setupClient.ConnectAsync();
|
||||
|
||||
if (connected)
|
||||
{
|
||||
Console.WriteLine($"✅ Connected successfully with aliasname: '{aliasname}'!\n");
|
||||
successfulClient = setupClient;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
setupClient.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
if (successfulClient == null)
|
||||
{
|
||||
Console.WriteLine("❌ Failed to connect with any aliasname");
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the successfully connected client
|
||||
using (var setupClient = successfulClient)
|
||||
{
|
||||
|
||||
// Test ping
|
||||
Console.WriteLine("2. Testing Ping...");
|
||||
bool pingResult = setupClient.SendPing();
|
||||
Console.WriteLine(pingResult ? "✅ Ping successful" : "❌ Ping failed");
|
||||
Console.WriteLine();
|
||||
|
||||
// Read setup configuration
|
||||
Console.WriteLine("3. Reading Setup Configuration...");
|
||||
byte[] setupData = await setupClient.ReadSetupAsync();
|
||||
|
||||
Console.WriteLine($"✅ Read {setupData.Length} bytes of configuration\n");
|
||||
|
||||
// Save to file for inspection
|
||||
string outputFile = Path.Combine(
|
||||
Environment.CurrentDirectory,
|
||||
$"setup_config_{DateTime.Now:yyyyMMdd_HHmmss}.dat"
|
||||
);
|
||||
|
||||
File.WriteAllBytes(outputFile, setupData);
|
||||
Console.WriteLine($"📁 Saved configuration to: {outputFile}\n");
|
||||
|
||||
// Analyze file format
|
||||
Console.WriteLine("4. Analyzing File Format...");
|
||||
AnalyzeSetupFile(setupData);
|
||||
|
||||
Console.WriteLine("\n5. Testing Write Setup (write back unchanged)...");
|
||||
|
||||
// In automated mode, skip write test by default
|
||||
string response = "n";
|
||||
if (args.Length < 3)
|
||||
{
|
||||
Console.Write("Write configuration back to server? (y/n): ");
|
||||
response = Console.ReadLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Skipping write test in automated mode (pass 4th argument 'y' to enable)");
|
||||
if (args.Length >= 4 && args[3].ToLower() == "y")
|
||||
{
|
||||
response = "y";
|
||||
}
|
||||
}
|
||||
|
||||
if (response?.ToLower() == "y")
|
||||
{
|
||||
bool writeSuccess = await setupClient.WriteSetupAsync(setupData);
|
||||
Console.WriteLine(writeSuccess
|
||||
? "✅ Configuration written successfully"
|
||||
: "❌ Failed to write configuration");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("⏭️ Skipped write test");
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("\n✅ All tests completed successfully!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"\n❌ Error: {ex.Message}");
|
||||
Console.WriteLine($"Stack trace: {ex.StackTrace}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Log.CloseAndFlush();
|
||||
}
|
||||
|
||||
// Only wait for key if in interactive mode (not automated)
|
||||
if (args.Length < 3)
|
||||
{
|
||||
Console.WriteLine("\nPress any key to exit...");
|
||||
try
|
||||
{
|
||||
Console.ReadKey();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore if console input is redirected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void AnalyzeSetupFile(byte[] data)
|
||||
{
|
||||
// Check if XML
|
||||
if (data.Length > 5)
|
||||
{
|
||||
string header = System.Text.Encoding.ASCII.GetString(data, 0, Math.Min(100, data.Length));
|
||||
|
||||
if (header.StartsWith("<?xml") || header.StartsWith("<"))
|
||||
{
|
||||
Console.WriteLine(" Format: XML");
|
||||
Console.WriteLine($" First 200 chars:\n{header}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for common text encodings
|
||||
try
|
||||
{
|
||||
string utf8Text = System.Text.Encoding.UTF8.GetString(data, 0, Math.Min(200, data.Length));
|
||||
if (IsText(utf8Text))
|
||||
{
|
||||
Console.WriteLine(" Format: Text (UTF-8)");
|
||||
Console.WriteLine($" First 200 chars:\n{utf8Text}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Binary format
|
||||
Console.WriteLine(" Format: Binary");
|
||||
Console.WriteLine(" Hex dump (first 100 bytes):");
|
||||
HexDump(data, Math.Min(100, data.Length));
|
||||
}
|
||||
|
||||
static bool IsText(string str)
|
||||
{
|
||||
foreach (char c in str)
|
||||
{
|
||||
if (char.IsControl(c) && c != '\r' && c != '\n' && c != '\t')
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void HexDump(byte[] data, int length)
|
||||
{
|
||||
for (int i = 0; i < length; i += 16)
|
||||
{
|
||||
Console.Write($" {i:X4}: ");
|
||||
|
||||
// Hex
|
||||
for (int j = 0; j < 16; j++)
|
||||
{
|
||||
if (i + j < length)
|
||||
Console.Write($"{data[i + j]:X2} ");
|
||||
else
|
||||
Console.Write(" ");
|
||||
}
|
||||
|
||||
Console.Write(" ");
|
||||
|
||||
// ASCII
|
||||
for (int j = 0; j < 16 && i + j < length; j++)
|
||||
{
|
||||
byte b = data[i + j];
|
||||
Console.Write(b >= 32 && b < 127 ? (char)b : '.');
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
static string ReadPassword()
|
||||
{
|
||||
string password = "";
|
||||
ConsoleKeyInfo key;
|
||||
|
||||
do
|
||||
{
|
||||
key = Console.ReadKey(true);
|
||||
|
||||
if (key.Key == ConsoleKey.Backspace && password.Length > 0)
|
||||
{
|
||||
password = password.Substring(0, password.Length - 1);
|
||||
Console.Write("\b \b");
|
||||
}
|
||||
else if (key.Key != ConsoleKey.Enter && key.KeyChar != '\0')
|
||||
{
|
||||
password += key.KeyChar;
|
||||
Console.Write("*");
|
||||
}
|
||||
} while (key.Key != ConsoleKey.Enter);
|
||||
|
||||
return password;
|
||||
}
|
||||
}
|
||||
}
|
||||
391
src/sdk-bridge/GeViScopeBridge/SDK/GeViSetupClientWrapper.cs
Normal file
391
src/sdk-bridge/GeViScopeBridge/SDK/GeViSetupClientWrapper.cs
Normal file
@@ -0,0 +1,391 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Serilog;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace GeViScopeBridge.SDK
|
||||
{
|
||||
/// <summary>
|
||||
/// P/Invoke wrapper for GeViAPI SetupClient functions
|
||||
/// This is what GeViSet uses to read/write configuration from/to GeViServer
|
||||
/// </summary>
|
||||
public class GeViSetupClientWrapper : IDisposable
|
||||
{
|
||||
private IntPtr _setupClientHandle = IntPtr.Zero;
|
||||
private bool _isConnected = false;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
// Connection parameters
|
||||
private readonly string _aliasname;
|
||||
private readonly string _address;
|
||||
private readonly string _username;
|
||||
private readonly string _password;
|
||||
|
||||
#region P/Invoke Declarations
|
||||
|
||||
[DllImport("GeViProcAPI.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
||||
private static extern bool GeViAPI_SetupClient_Create(
|
||||
out IntPtr setupClient,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string aliasname,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string address,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string username,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string password,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string username2,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string password2
|
||||
);
|
||||
|
||||
[DllImport("GeViProcAPI.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern bool GeViAPI_SetupClient_Connect(
|
||||
IntPtr setupClient,
|
||||
out int connectResult,
|
||||
IntPtr callback, // TGeViConnectProgress callback (can be IntPtr.Zero)
|
||||
IntPtr instance // void* instance (can be IntPtr.Zero)
|
||||
);
|
||||
|
||||
[DllImport("GeViProcAPI.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern bool GeViAPI_SetupClient_Disconnect(IntPtr setupClient);
|
||||
|
||||
[DllImport("GeViProcAPI.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern bool GeViAPI_SetupClient_Destroy(IntPtr setupClient);
|
||||
|
||||
[DllImport("GeViProcAPI.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern bool GeViAPI_SetupClient_ReadSetup(
|
||||
IntPtr setupClient,
|
||||
IntPtr hFile // File handle from CreateFile
|
||||
);
|
||||
|
||||
[DllImport("GeViProcAPI.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern bool GeViAPI_SetupClient_WriteSetup(
|
||||
IntPtr setupClient,
|
||||
IntPtr hFile // File handle from CreateFile
|
||||
);
|
||||
|
||||
[DllImport("GeViProcAPI.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern bool GeViAPI_SetupClient_SendPing(IntPtr setupClient);
|
||||
|
||||
// Windows API for file operations
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern IntPtr CreateFile(
|
||||
string lpFileName,
|
||||
uint dwDesiredAccess,
|
||||
uint dwShareMode,
|
||||
IntPtr lpSecurityAttributes,
|
||||
uint dwCreationDisposition,
|
||||
uint dwFlagsAndAttributes,
|
||||
IntPtr hTemplateFile
|
||||
);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool CloseHandle(IntPtr hObject);
|
||||
|
||||
// Password encoding function
|
||||
[DllImport("GeViProcAPI.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
|
||||
private static extern void GeViAPI_EncodeString(
|
||||
[MarshalAs(UnmanagedType.LPStr)] System.Text.StringBuilder output,
|
||||
[MarshalAs(UnmanagedType.LPStr)] string input,
|
||||
int size
|
||||
);
|
||||
|
||||
// File access constants
|
||||
private const uint GENERIC_READ = 0x80000000;
|
||||
private const uint GENERIC_WRITE = 0x40000000;
|
||||
private const uint CREATE_ALWAYS = 2;
|
||||
private const uint OPEN_EXISTING = 3;
|
||||
private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
|
||||
|
||||
#endregion
|
||||
|
||||
public GeViSetupClientWrapper(string address, string username, string password, string aliasname = "")
|
||||
{
|
||||
_address = address ?? throw new ArgumentNullException(nameof(address));
|
||||
_username = username ?? throw new ArgumentNullException(nameof(username));
|
||||
_password = password ?? throw new ArgumentNullException(nameof(password));
|
||||
_aliasname = aliasname ?? "";
|
||||
_logger = Log.ForContext<GeViSetupClientWrapper>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connect to GeViServer
|
||||
/// </summary>
|
||||
public async Task<bool> ConnectAsync()
|
||||
{
|
||||
return await Task.Run(() => Connect());
|
||||
}
|
||||
|
||||
private bool Connect()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.Information("Creating SetupClient for {Address}", _address);
|
||||
|
||||
// Encrypt password using GeViAPI_EncodeString
|
||||
// Password buffer should be at least 256 bytes according to typical SDK usage
|
||||
var encodedPassword = new System.Text.StringBuilder(256);
|
||||
GeViAPI_EncodeString(encodedPassword, _password, encodedPassword.Capacity);
|
||||
|
||||
_logger.Debug("Password encrypted for SetupClient connection");
|
||||
|
||||
// Create SetupClient with encrypted password
|
||||
bool created = GeViAPI_SetupClient_Create(
|
||||
out _setupClientHandle,
|
||||
_aliasname,
|
||||
_address,
|
||||
_username,
|
||||
encodedPassword.ToString(), // Use encrypted password
|
||||
"", // username2 (optional, for dual control)
|
||||
"" // password2 (optional)
|
||||
);
|
||||
|
||||
if (!created || _setupClientHandle == IntPtr.Zero)
|
||||
{
|
||||
_logger.Error("Failed to create SetupClient");
|
||||
return false;
|
||||
}
|
||||
|
||||
_logger.Information("SetupClient created, connecting to {Address}", _address);
|
||||
|
||||
// Connect to server
|
||||
bool connected = GeViAPI_SetupClient_Connect(
|
||||
_setupClientHandle,
|
||||
out int connectResult,
|
||||
IntPtr.Zero, // No progress callback
|
||||
IntPtr.Zero // No instance
|
||||
);
|
||||
|
||||
if (!connected || connectResult != 0)
|
||||
{
|
||||
string errorName = GetConnectResultName(connectResult);
|
||||
_logger.Error("Failed to connect SetupClient. Result: {Result} ({ErrorName})", connectResult, errorName);
|
||||
return false;
|
||||
}
|
||||
|
||||
_isConnected = true;
|
||||
_logger.Information("SetupClient connected successfully to {Address}", _address);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Exception during SetupClient connection");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read complete setup configuration from GeViServer to a file
|
||||
/// </summary>
|
||||
public async Task<byte[]> ReadSetupAsync()
|
||||
{
|
||||
return await Task.Run(() => ReadSetup());
|
||||
}
|
||||
|
||||
private byte[] ReadSetup()
|
||||
{
|
||||
if (!_isConnected || _setupClientHandle == IntPtr.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("SetupClient is not connected");
|
||||
}
|
||||
|
||||
string tempFile = Path.GetTempFileName();
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Information("Reading setup configuration from GeViServer to {TempFile}", tempFile);
|
||||
|
||||
// Create file handle for writing
|
||||
IntPtr hFile = CreateFile(
|
||||
tempFile,
|
||||
GENERIC_WRITE,
|
||||
0, // No sharing
|
||||
IntPtr.Zero,
|
||||
CREATE_ALWAYS,
|
||||
0,
|
||||
IntPtr.Zero
|
||||
);
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
int error = Marshal.GetLastWin32Error();
|
||||
throw new IOException($"Failed to create temp file. Error: {error}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Read setup from server
|
||||
bool success = GeViAPI_SetupClient_ReadSetup(_setupClientHandle, hFile);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to read setup from GeViServer");
|
||||
}
|
||||
|
||||
_logger.Information("Setup configuration read successfully");
|
||||
}
|
||||
finally
|
||||
{
|
||||
CloseHandle(hFile);
|
||||
}
|
||||
|
||||
// Read file contents
|
||||
byte[] data = File.ReadAllBytes(tempFile);
|
||||
_logger.Information("Read {Size} bytes of setup configuration", data.Length);
|
||||
return data;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Clean up temp file
|
||||
if (File.Exists(tempFile))
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(tempFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warning(ex, "Failed to delete temp file {TempFile}", tempFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write setup configuration back to GeViServer from a byte array
|
||||
/// </summary>
|
||||
public async Task<bool> WriteSetupAsync(byte[] setupData)
|
||||
{
|
||||
return await Task.Run(() => WriteSetup(setupData));
|
||||
}
|
||||
|
||||
private bool WriteSetup(byte[] setupData)
|
||||
{
|
||||
if (!_isConnected || _setupClientHandle == IntPtr.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("SetupClient is not connected");
|
||||
}
|
||||
|
||||
if (setupData == null || setupData.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("Setup data cannot be null or empty", nameof(setupData));
|
||||
}
|
||||
|
||||
string tempFile = Path.GetTempFileName();
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Information("Writing {Size} bytes of setup configuration to GeViServer", setupData.Length);
|
||||
|
||||
// Write data to temp file
|
||||
File.WriteAllBytes(tempFile, setupData);
|
||||
|
||||
// Open file handle for reading
|
||||
IntPtr hFile = CreateFile(
|
||||
tempFile,
|
||||
GENERIC_READ,
|
||||
0, // No sharing
|
||||
IntPtr.Zero,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
IntPtr.Zero
|
||||
);
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
int error = Marshal.GetLastWin32Error();
|
||||
throw new IOException($"Failed to open temp file. Error: {error}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Write setup to server
|
||||
bool success = GeViAPI_SetupClient_WriteSetup(_setupClientHandle, hFile);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
_logger.Error("Failed to write setup to GeViServer");
|
||||
return false;
|
||||
}
|
||||
|
||||
_logger.Information("Setup configuration written successfully to GeViServer");
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
CloseHandle(hFile);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Clean up temp file
|
||||
if (File.Exists(tempFile))
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(tempFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warning(ex, "Failed to delete temp file {TempFile}", tempFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send ping to keep connection alive
|
||||
/// </summary>
|
||||
public bool SendPing()
|
||||
{
|
||||
if (!_isConnected || _setupClientHandle == IntPtr.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return GeViAPI_SetupClient_SendPing(_setupClientHandle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from GeViServer
|
||||
/// </summary>
|
||||
public void Disconnect()
|
||||
{
|
||||
if (_isConnected && _setupClientHandle != IntPtr.Zero)
|
||||
{
|
||||
_logger.Information("Disconnecting SetupClient");
|
||||
GeViAPI_SetupClient_Disconnect(_setupClientHandle);
|
||||
_isConnected = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Disconnect();
|
||||
|
||||
if (_setupClientHandle != IntPtr.Zero)
|
||||
{
|
||||
_logger.Information("Destroying SetupClient");
|
||||
GeViAPI_SetupClient_Destroy(_setupClientHandle);
|
||||
_setupClientHandle = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetConnectResultName(int result)
|
||||
{
|
||||
return result switch
|
||||
{
|
||||
0 => "connectOk",
|
||||
100 => "connectAborted",
|
||||
101 => "connectGenericError",
|
||||
300 => "connectRemoteUnknownError",
|
||||
301 => "connectRemoteTcpError",
|
||||
302 => "connectRemoteUnknownUser",
|
||||
303 => "connectRemoteConnectionLimitExceeded",
|
||||
304 => "connectRemoteClientInterfaceTooOld",
|
||||
305 => "connectRemoteServerInterfaceTooOld",
|
||||
306 => "connectRemoteSecondUserRequired",
|
||||
307 => "connectRemotePortDisabled",
|
||||
_ => $"Unknown({result})"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user