Instantly share code, notes, and snippets.
Last active
March 16, 2024 07:12
-
Star
1
(1)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save lahma0/a66a573273a779ebcb9bd43a898bfc03 to your computer and use it in GitHub Desktop.
ShareXNamedPipe
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class ShareXNamedPipeClient | |
{ | |
public static string PipeName => "ShareXPatcherPipe"; | |
public static async Task<string?> GetLastRegionBoundsXmlAsync(int connectTimeoutMs = 2000) | |
{ | |
if (!ShareXUtils.IsShareXRunning()) | |
throw new ShareXProcessNotFoundException( | |
"No running ShareX process found. Please ensure ShareX is running and try again."); | |
if (!ShareXUtils.ShareXPipeExists() || !(await IsServerListening())) | |
{ | |
// ShareX is running but pipe either doesn't exist or server isn't listening so inject the patcher. | |
await ShareXUtils.InjectPatcherAndWaitForServerAsync(); | |
} | |
var clientPipe = await ConnectToServerAsync(connectTimeoutMs); | |
var serverResponse = await ReadDataFromServerAsync(clientPipe); | |
if (string.IsNullOrWhiteSpace(serverResponse) || serverResponse == "null") | |
throw new ShareXLastRegionInvalidException( | |
"'Last Region' data returned from the ShareX server is null. This likely indicates that " + | |
"no screenshots have been taken since the ShareX process started."); | |
if (!serverResponse.StartsWith("<?xml")) | |
throw new ShareXLastRegionInvalidException( | |
"'Last Region' data returned from the ShareX server is invalid or in an unexpected format."); | |
return serverResponse; | |
} | |
private static async Task<NamedPipeClientStream> ConnectToServerAsync(int connectTimeoutMs) | |
{ | |
try | |
{ | |
var clientPipe = new NamedPipeClientStream(PipeName); | |
await clientPipe.ConnectAsync(connectTimeoutMs); | |
return clientPipe; | |
} | |
catch (TimeoutException timeoutEx) | |
{ | |
throw new ShareXServerTimeoutException( | |
$"The ShareX server did not respond within {connectTimeoutMs}ms.", timeoutEx); | |
} | |
} | |
private static async Task<string?> ReadDataFromServerAsync(NamedPipeClientStream clientPipe) | |
{ | |
using var reader = new StreamReader(clientPipe); | |
var serverResponse = await reader.ReadToEndAsync(); | |
await clientPipe.DisposeAsync(); | |
return serverResponse; | |
} | |
public static async Task<bool> IsServerListening(int connectTimeoutMs = 2000) | |
{ | |
// If ShareX isn't running or the pipe doesn't exist, the server definitely isn't listening. | |
if (!ShareXUtils.IsShareXRunning() || !ShareXUtils.ShareXPipeExists()) | |
return false; | |
try | |
{ | |
var clientPipe = await ConnectToServerAsync(connectTimeoutMs); | |
// Response is irrelevant but server expects us to read its response after connecting so do it anyways. | |
await ReadDataFromServerAsync(clientPipe); | |
} | |
catch | |
{ | |
return false; | |
} | |
return true; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ShareXNamedPipeServer | |
{ | |
public static string PipeName => "ShareXPatcherPipe"; | |
public static bool IsListening { get; private set; } | |
public static NamedPipeServerStream ServerPipe { get; private set; } | |
public static async Task RunServerAsync() | |
{ | |
if (ServerPipe != null) | |
{ | |
ShareXPatcher.WriteDebugLine("Server is already running"); | |
return; | |
} | |
try | |
{ | |
if (ServerPipe == null) | |
{ | |
ServerPipe = new NamedPipeServerStream(PipeName, | |
PipeDirection.InOut, | |
1, | |
PipeTransmissionMode.Message, | |
PipeOptions.Asynchronous); | |
// Timeouts are not supported when using Message mode and ReadAsync/WriteAsync. You must use | |
// BeginRead/BeginWrite with callbacks to properly support timeouts. | |
//ServerPipe.ReadTimeout = timeout; | |
//ServerPipe.WriteTimeout = timeout; | |
} | |
IsListening = true; | |
StreamWriter writer = null; | |
do | |
{ | |
try | |
{ | |
ShareXPatcher.WriteDebugLine("Server is waiting for new client connections..."); | |
await ServerPipe.WaitForConnectionAsync(); | |
ShareXPatcher.WriteDebugLine("Client connected to server"); | |
// Only create writer once and reuse it for each client connection. Disposing it causes | |
// ServerPipe to also be disposed. No good reason to create a new one every loop. | |
if (writer == null) | |
writer = new StreamWriter(ServerPipe) { AutoFlush = true }; | |
if (!string.IsNullOrWhiteSpace(ShareXPatcher.LastRegionBoundsXml)) | |
await writer.WriteAsync(ShareXPatcher.LastRegionBoundsXml); | |
else | |
await writer.WriteAsync("null"); | |
ServerPipe.WaitForPipeDrain(); | |
} | |
catch (IOException ex) | |
{ | |
ShareXPatcher.WriteDebugLine( | |
$"An error occurred while communicating with a client: {ex.Message}"); | |
} | |
catch (ObjectDisposedException) | |
{ | |
ShareXPatcher.WriteDebugLine( | |
"Server has been disposed so pipe server will not resume listening for clients"); | |
// ServerPipe is disposed so we must break out of the loop (bc we must recreate ServerPipe). | |
break; | |
} | |
catch (Exception ex) | |
{ | |
ShareXPatcher.WriteDebugLine( | |
$"An unknown error occurred within pipe server loop: {ex.Message}"); | |
} | |
finally | |
{ | |
if (ServerPipe != null && ServerPipe.IsConnected) | |
{ | |
ShareXPatcher.WriteDebugLine("Disconnecting client..."); | |
ServerPipe.Disconnect(); | |
} | |
} | |
if (IsListening) | |
ShareXPatcher.WriteDebugLine("Restarting pipe server..."); | |
} while (IsListening); | |
} | |
finally | |
{ | |
// This 'finally' ensures that the server pipe will be disposed of regardless. | |
ShareXPatcher.WriteDebugLine($"Calling {nameof(StopServer)}..."); | |
StopServer(); | |
} | |
} | |
public static void StopServer() | |
{ | |
IsListening = false; | |
if (ServerPipe == null) | |
{ | |
ShareXPatcher.WriteDebugLine("Server is already stopped"); | |
return; | |
} | |
try | |
{ | |
if (ServerPipe.IsConnected) | |
{ | |
ShareXPatcher.WriteDebugLine("Disconnecting client..."); | |
ServerPipe?.Disconnect(); | |
} | |
ShareXPatcher.WriteDebugLine("Closing server pipe..."); | |
ServerPipe?.Close(); | |
ShareXPatcher.WriteDebugLine("Disposing server pipe..."); | |
ServerPipe?.Dispose(); | |
} | |
catch { } | |
finally | |
{ | |
ServerPipe = null; | |
} | |
ShareXPatcher.WriteDebugLine("Server is stopped"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment