1

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?

2
  • Possible duplicate of How do I pass multiple parameters into a function in PowerShell? Commented Jul 28, 2019 at 14:55
  • 1
    What does return [MyCore.Utils.WebsocketClient]::StartWSSession($WSEndpointUrl,$Command) return? That is what you get as a reply. You have to return a string instead of Console.WriteLine. Commented Jul 28, 2019 at 14:55

1 Answer 1

1

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"

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.