hugil
hugil

Reputation: 51

C# code in Powershell cmdlet does not output values

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

Answers (1)

hugil
hugil

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

Related Questions