Reputation: 51
I have written a powershell cmdlet that utilizes some DLLs using C# WebsocketSharp and Newtonsoft JSON specifically.
It is supposed to send a string of json to a websocket server and then return the reply into a Out-File, or to be used as a variable in another context i.e.
$varWithReply = Send-Cmd("ws://websocket-server.com/","mycommand")
function Send-Cmd
{
[CmdletBinding()]
Param (
[Parameter(Mandatory = $True)]
[string]$WSEndpointUrl,
[Parameter(Mandatory = $True)]
[string]$Command
)
try
{
Add-Type -Path ".\websocket-sharp.dll"
Add-Type -Path ".\Newtonsoft.Json.dll"
}
catch [System.Reflection.ReflectionTypeLoadException]
{
Write-Host "Message: $($_.Exception.Message)"
Write-Host "StackTrace: $($_.Exception.StackTrace)"
Write-Host "LoaderExceptions: $($_.Exception.LoaderExceptions)"
}
$CurrentlyLoadedAssemblies = [System.AppDomain]::CurrentDomain.GetAssemblies()
$AssembiesFullInfo = $CurrentlyLoadedAssemblies | Where-Object {
$_.GetName().Name -eq "Microsoft.CSharp" -or
$_.GetName().Name -eq "mscorlib" -or
$_.GetName().Name -eq "System" -or
$_.GetName().Name -eq "System.Collections" -or
$_.GetName().Name -eq "System.Core" -or
$_.GetName().Name -eq "System.IO" -or
$_.GetName().Name -eq "System.Linq" -or
$_.GetName().Name -eq "System.Runtime" -or
$_.GetName().Name -eq "System.Runtime.Extensions" -or
$_.GetName().Name -eq "System.Runtime.InteropServices" -or
$_.GetName().Name -eq "System.Threading" -or
$_.GetName().Name -eq "websocket-sharp" -or
$_.GetName().Name -eq "Newtonsoft.Json"
}
$AssembiesFullInfo = $AssembiesFullInfo | Where-Object {$_.IsDynamic -eq $False}
$ReferencedAssemblies = $AssembiesFullInfo.FullName | Sort-Object | Get-Unique
$usingStatementsAsString = @"
using Microsoft.CSharp;
using System.Collections.Generic;
using System.Collections;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime;
using System.Threading;
using System;
using WebSocketSharp;
using System.Net.WebSockets;
using Newtonsoft.Json;
"@
$TypeDefinition = @"
$usingStatementsAsString
namespace MyCore.Utils
{
public class WebSocketClient
{
public static void StartWSSession(string url, string cmd)
{
using (var ws = new WebSocketSharp.WebSocket(url))
{
int received = 0;
// Set the WebSocket events.
ws.OnOpen += (sender, e) =>
{
Response jsonmsg = new Response();
jsonmsg.Identifier = 2000;
jsonmsg.Message = cmd;
jsonmsg.Name = "PowershellWS";
string output = JsonConvert.SerializeObject(jsonmsg);
ws.Send(output);
};
ws.OnMessage += (sender, e) => {
Response response = JsonConvert.DeserializeObject<Response>(e.Data);
if (response.Identifier == 2000) {
Console.WriteLine(response.Message);
received ++;
ws.Close();
}
};
ws.OnError += (sender, e) =>
Console.WriteLine(e.Message);
ws.OnClose += (sender, e) =>
Console.WriteLine(e.Reason);
// Connect to the server.
ws.Connect();
while (received < 1){Thread.Sleep(1);}
}
}
}
internal class Response
{
[JsonProperty(PropertyName = "Identifier")]
public int Identifier { get; set; }
[JsonProperty(PropertyName = "Message")]
public string Message { get; set; }
[JsonProperty(PropertyName = "Name")]
public string Name { get; set; }
}
}
"@
Add-Type -ReferencedAssemblies $ReferencedAssemblies -TypeDefinition $TypeDefinition
return [MyCore.Utils.WebsocketClient]::StartWSSession($WSEndpointUrl,$Command)
}
I tried
$varWithReply = Send-Cmd("ws://websocket-server.com/","mycommand")
Send-Cmd("ws://websocket-server.com/","mycommand") | Out-File .\output.txt
but I don't get any output at all. Is this because the C# console line stream is independent? How would I go about solving this?
Upvotes: 1
Views: 93
Reputation: 51
So... looks like I made a C# rookie mistake. Thanks @H.G. Sandhagen for pointing in the right direction with your comment.
namespace MyCore.Utils
{
public class WebSocketClient
{
public static string StartWSSession(string url, string cmd)
{
string reply = null;
using (var ws = new WebSocketSharp.WebSocket(url))
{
int received = 0;
// Set the WebSocket events.
ws.OnOpen += (sender, e) =>
{
Response jsonmsg = new Response();
jsonmsg.Identifier = 2000;
jsonmsg.Message = cmd;
jsonmsg.Name = "PowershellWS";
string output = JsonConvert.SerializeObject(jsonmsg);
ws.Send(output);
};
ws.OnMessage += (sender, e) => {
Response response = JsonConvert.DeserializeObject<Response>(e.Data);
if (response.Identifier == 2000) {
Console.WriteLine(response.Message);
reply = response.Message;
received ++;
ws.Close();
}
};
ws.OnError += (sender, e) =>
Console.WriteLine(e.Message);
ws.OnClose += (sender, e) =>
Console.WriteLine(e.Reason);
// Connect to the server.
ws.Connect();
while (received < 1){Thread.Sleep(1);}
}
return reply;
}
}
internal class Response
{
[JsonProperty(PropertyName = "Identifier")]
public int Identifier { get; set; }
[JsonProperty(PropertyName = "Message")]
public string Message { get; set; }
[JsonProperty(PropertyName = "Name")]
public string Name { get; set; }
}
}
I took PS for granted, now use a return for the variable reply i made so it actually returns a value and the function itself can't be void, since that means it isn't returning anything at all, it has to be a "string"
Upvotes: 1