0

I'm able to get a simple c# function to work, but when I introduce something more complicated such as what's below, I'm getting syntax errors and there isn't a lot of examples on how to do this.

I've made updates to the code based on advice received here, but this code still does not function properly

cls


$dagDistribution = $null;

        $distribution = 
        @'

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Management.Automation;
    using System.Management.Automation.Runspaces;
    using System.Security;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using System.Threading;
    using System.Collections.Concurrent;
    using System.Diagnostics;
    using System.Security.Principal;    


    namespace MultiThreading
    {

        public class dagDistribution
        {

            public List<string> get(string dag)
            {
                DateTime start = DateTime.Now;

                var response = new ConcurrentBag<Collection<PSObject>>();
                var exceptions = new ConcurrentQueue<Exception>();

                string dagName = "hqdag1";

                string[] serversUnsorted = getDagMembers(dagName);
                var servers = from s in serversUnsorted orderby s select s;

                try
                {
                    Parallel.ForEach(servers, server =>
                    {
                        response.Add(runPowerShellScript(server));
                    });
                }
                catch (AggregateException ae)
                {
                    foreach (var aex in ae.InnerExceptions)
                    {
                        exceptions.Enqueue(aex);
                    }
                }

                List<string> returnValues = new List<string>();
                foreach (var item in response)
                {
                    string returnValue = parseServerResults(item);
                    returnValues.Add(returnValue);
                }

                returnValues.Sort();
                return returnValues;
            }

            private Collection<PSObject> runPowerShellScript(object server)
            {
                Collection<PSObject> psobjs = new Collection<PSObject>();
                string result = "";
                string serverName = server.ToString();

                WSManConnectionInfo wmc = new WSManConnectionInfo(new Uri("http://xxx/powershell"));
                wmc.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
                wmc.ShellUri = "http://schemas.microsoft.com/powershell/Microsoft.Exchange";

                using (Runspace runspace = RunspaceFactory.CreateRunspace(wmc))
                {
                    PowerShell powershell = PowerShell.Create();

                    if (runspace.RunspaceStateInfo.State == RunspaceState.Opened)
                    {
                        // do nothing
                    }
                    else
                    {
                        runspace.Open();
                        powershell.Runspace = runspace;
                    }

                    try
                    {
                        PSCommand command = new PSCommand();
                        command.AddScript("get-mailboxdatabase -Server " + server + " -Status");
                        powershell.Commands = command;                    
                        psobjs = powershell.Invoke();

                        if (powershell.HadErrors == true)
                        {
                            result = "Failed - " + powershell.Streams.Error[0].ToString();
                            result = result.Replace("\"", "*");
                        }
                    }
                    catch (Exception ex)
                    {
                        string fail = ex.Message;
                    }
                }
                object serverNameO = server;
                PSObject serverNameObj = new PSObject(serverNameO);
                psobjs.Insert(0, serverNameObj);

                return psobjs;
            }

            private string[] getDagMembers(string dagName)
            {
                Collection<PSObject> psobjs = new Collection<PSObject>();
                string result = "";
                string[] servers = null;

                WSManConnectionInfo wmc = new WSManConnectionInfo(new Uri("http://xxx/powershell"));
                wmc.AuthenticationMechanism = AuthenticationMechanism.Kerberos;
                wmc.ShellUri = "http://schemas.microsoft.com/powershell/Microsoft.Exchange";


                using (Runspace runspace = RunspaceFactory.CreateRunspace(wmc))
                {
                    PowerShell powershell = PowerShell.Create();

                    if (runspace.RunspaceStateInfo.State == RunspaceState.Opened)
                    {
                        // do nothing
                    }
                    else
                    {
                        runspace.Open();
                        powershell.Runspace = runspace;
                    }

                    try
                    {
                        PSCommand command = new PSCommand();
                        command.AddScript("Get-DatabaseAvailabilityGroup -Identity " + dagName);
                        powershell.Commands = command;
                        psobjs = powershell.Invoke();

                        if (powershell.HadErrors == true)
                        {
                            result = "Failed - " + powershell.Streams.Error[0].ToString();
                            result = result.Replace("\"", "*");
                        }

                        PSPropertyInfo serversTemp = null;
                        foreach (PSObject psobj in psobjs)
                        {
                            serversTemp = psobj.Properties["servers"];
                        }

                        string s_servers = serversTemp.Value.ToString();
                        servers = s_servers.Split(' ');

                    }
                    catch (Exception ex)
                    {
                        string fail = ex.Message;
                    }
                }            

                return servers;
            }

        private string parseServerResults(Collection<PSObject> serverObjs) // needs servername, totaldbs, activedbs, passivedbs, preferencecount (11,11,11,11), mounteddbs, dismounteddbs, dagname
        {
            // called independently with each server, first object is always the server name

            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            int index = 0;
            string returnValue = "";

            string serverName = "";
            int totalDbs = 0;
            int activeDbs = 0; // whichever has activation preference 1
            int passiveDbs = 0; // whichever has activation preference 2, 3 or 4       
            string activeCopyServerName = "";
            int activationPreferenceOne = 0;
            int activationPreferenceTwo = 0;
            int activationPreferenceThree = 0;
            int activationPreferenceFour = 0;
            int mountedCount = 0;
            int dismountedCount = 0;
            string dagName = "";
            string dagServerAndDatabaseName = "";

            foreach (PSObject obj in serverObjs)
            {
                if (index == 0)
                {
                    serverName = obj.ToString();
                }

                totalDbs = (serverObjs.Count - 1);

                PSMemberInfoCollection<PSPropertyInfo> props = obj.Properties;

                string currentPrimaryActivationServer = "";
                foreach (PSPropertyInfo prop in props)
                {
                    if (prop.Name == "MountedOnServer")
                    {
                        currentPrimaryActivationServer = prop.Value.ToString();
                        break;
                    }
                }

                List<string> propertyNames = new List<string>();
                foreach (PSPropertyInfo prop in props)
                {
                    string result = prop.Name + " | " + prop.Value;

                    if (prop.Name == "Mounted")
                    {
                        if (prop.Value.ToString() == "True")
                        {
                            if (currentPrimaryActivationServer.ToLower().StartsWith(serverName.ToLower()))
                            {
                                mountedCount++;
                            }
                        }
                        else
                        {
                            dismountedCount++;
                        }
                    }
                    else if (prop.Name == "MountedOnServer")
                    {
                        activeCopyServerName = prop.Value.ToString();
                    }
                    else if (prop.Name == "ActivationPreference")
                    {
                        string arr = prop.Value.ToString();
                        string[] vals = arr.Split(']');

                        foreach (string val in vals)
                        {
                            if (val != "")
                            {
                                string valTemp = val;
                                if (val.Contains("["))
                                {
                                    valTemp = val.Replace("[", "");
                                }

                                string[] preference = valTemp.Split(',');

                                string preferenceZero = preference[0].ToString().Trim();
                                string preferenceOne = preference[1].ToString().Trim();

                                if (preferenceZero.ToLower() == serverName.ToLower())
                                {
                                    if (preferenceOne == "1")
                                    {
                                        if (currentPrimaryActivationServer.ToLower().StartsWith(serverName.ToLower()))
                                        {
                                            activeDbs++;
                                        }
                                        else
                                        {
                                            passiveDbs++;
                                        }
                                    }
                                    else
                                    {
                                        if (!(currentPrimaryActivationServer.ToLower().StartsWith(serverName.ToLower())))
                                        {
                                            passiveDbs++;
                                        }
                                        else
                                        {
                                            activeDbs++;
                                        }
                                    }

                                    switch (preferenceOne)
                                    {
                                        case "1":
                                            activationPreferenceOne++;
                                            break;

                                        case "2":
                                            activationPreferenceTwo++;
                                            break;

                                        case "3":
                                            activationPreferenceThree++;
                                            break;

                                        case "4":
                                            activationPreferenceFour++;
                                            break;

                                        default:
                                            break;
                                    }
                                }
                            }
                        }
                    }
                    else if (prop.Name == "Server")
                    {
                        string activeCopyServerName2 = prop.Value.ToString();
                    }
                    else if (prop.Name == "MasterServerOrAvailabilityGroup")
                    {
                        dagName = prop.Value.ToString();
                    }
                    else if (prop.Name == "MailboxProvisioningAttributes")
                    {
                        dagServerAndDatabaseName = prop.Value.ToString();
                    }

                    propertyNames.Add(prop.Name.ToString()); // cumulative count of the property names
                }

                index++;
            }

            stopwatch.Stop();
            Console.WriteLine(serverName + " - " + stopwatch.Elapsed.ToString());

            return returnValue = serverName + "|" + totalDbs + "|" + activeDbs + "|" + passiveDbs + "|" + activationPreferenceOne + "," + activationPreferenceTwo + "," +
                activationPreferenceThree + "," + activationPreferenceFour + "|" + mountedCount + "|" + dismountedCount + "|" + dagName;
        }



        }
    }
'@

write-host "after here-string";

Add-Type -TypeDefinition $distribution -ReferencedAssemblies System.Collections, System.ComponentModel, System.Data, System.Drawing, System.Linq, System.Management.Automation, System.Security, System.Threading.Tasks, System.Windows.Forms, System.Threading, System.Collections.Concurrent, System.Security.Principal



$dagDistribution = New-Object MultiThreading.dagDistribution;

$val = $dagDistribution.get("dag2");
Bbb
  • 517
  • 6
  • 27
  • 1
    `$@"..."` defines a verbatim interpolated string. Why are you doing that? You're not using interpolation or escape characters in the strings. – Ansgar Wiechers Aug 30 '18 at 13:44
  • That was an example I saw when searching on the internet, I have since corrected that as it was not needed. – Bbb Aug 30 '18 at 18:23

1 Answers1

7

You have two problems. Probably really just one. By default Add-Type uses the C# version 5 compiler, which is the latest one to be included in Windows. The string interpolation with $ is a newer feature. See this answer Powershell Add-Type C# 6.0.

Second, you have powershell escape characters in your C# code that shouldn't be there. Instead use a literal here-string to include arbitrary C# source. EG:

   $distribution = @'
    namespace MultiThreading
    {

        ....
    }

'@

C# has no "special" way to reference .NET Framework types, so you have to provide the compiler with a list of assemblies your code depends on.

Add-Type will use will use the current .NET Framework assemblies if you specify the "short name" of the assembly in the -ReferencedAssemblies argument. So:

Add-Type -TypeDefinition $distribution -ReferencedAssemblies System.Data, System.Xml

If you need an assembly that can't be resolved this way, you have to list the Assembly FullName, and Add-Type will try to load it.

You definitely want to avoid putting a full AssemblyName for a .NET Framework assembly in your powershell code, as that might cause your script to break when running on a machine with a different .NET Framework version, or with .NET Core.

David Browne - Microsoft
  • 80,331
  • 6
  • 39
  • 67
  • If I understand you correctly, changing the @" ... "@ to @' ... '@ and also changing the commands to `command.AddScript("get-mailboxdatabase -Server " + server + " -Status");` and `command.AddScript("Get-DatabaseAvailabilityGroup -Identity " + dagName);` will correct my syntax errors? I'm getting a bunch of namespace errors now, so I believe I need something like `$Assem = ( "System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=????") and then Add-Type -ReferencedAssemblies $Assem -TypeDefinition $distribution -Language CSharp` ; Is this correct? – Bbb Aug 30 '18 at 15:59
  • I thought `Add-Type` uses whatever the latest .NET Framework install has with it? – Maximilian Burszley Aug 30 '18 at 16:48
  • With my newly updated first post, have I understood your advice correctly? My code still does not run with what is posted above, it will get namespace errors. Once I get it functioning well, I will trim the number of assemblies needed. – Bbb Aug 30 '18 at 18:22
  • Those are genuine C# compilation errors. You should start with code that compiles in Visual Studio, Visual Studio Code, or with csc.exe before pasting into powershell. – David Browne - Microsoft Aug 30 '18 at 19:05
  • @DavidBrowne-Microsoft, the code functions properly in Visual Studio. It is tested and working inside of Visual Studio, when I copy/paste it into powershell is when I get the errors listed above (I've added them to the original post) and they appear to be namespace errors. Can you confirm that I've understood your syntax advice correctly? – Bbb Aug 30 '18 at 19:17
  • 1
    One error, for instance is, `cannot declare instance members in a static class` which would be the same in VS. – David Browne - Microsoft Aug 30 '18 at 19:26
  • I've removed the static class and functions. The example I found online had those in them, you are correct that my VS code did not. With those removed I'm still getting namespace errors such as this: `Add-Type : c:\Users\xxx\AppData\Local\Temp\2\fg4an3dl.0.cs(6) : The type or namespace name 'Data' does not exist in the namespace 'System' (are you missing an assembly reference?) ` – Bbb Aug 31 '18 at 13:16
  • I can repro that too. I looks like pre-loading the referenced assemblies is not reliable. I'll update the answer. Note you reference Assemblies, not Namespaces. The doc page for the Namespace will tell you what Assembly it is defined in. – David Browne - Microsoft Aug 31 '18 at 13:30
  • Thank you, it compiles now correctly as shown above. One last question before I set this as answered. Because I took the static portion out, I cannot access the function "get()" now. How do I rectify that? `$returnVal = [MultiThreading.dagDistribution]::Get();` – Bbb Aug 31 '18 at 16:19
  • You make all the functions `static` that you want to access without first creating an instance of your class with `new-object`. If the class has no fields or properties you can just make all the functions static. – David Browne - Microsoft Aug 31 '18 at 16:22
  • Thank you, I've added the final code above in case anyone comes upon a search – Bbb Sep 04 '18 at 13:42