Jump to content

Using .NET libary with AutoIt, possible?


Recommended Posts

Hi all,

This is how far I am now before releasing a new version :

- added CLR Constants.au3 - Danyfirex
    - Global Constants added (Magic numbers)

- added .NET CLR CreateObject vs ObjCreate Example.au3 - Junkew
    - 2 approaches give the same result (only valid for COM Visible Assembly)
    - Includes a function that shows you which Assembly Classes are COM Vissible or not.

- added .NET CLR CreateComInstanceFrom Example - Danyfirex
    - You can use it for Regfree COM Assembly Access
    - System.Activator has 4 methods :
        • CreateComInstanceFrom : Used to create instances of COM objects.
        • CreateInstanceFrom : Used to create a reference to an object from a particular assembly and type name.
        • GetObject : Used when marshaling objects.
        • CreateInstance : Used to create local or remote instances of an object.

- added .NET Access SafeArrays Using AccVarsUtilities Example - LarsJ
- added SafeArray Utilities functions in Includes - LarsJ 

- added .NET Access Native MSCorLib Classes - System - ptrex
  Multiple System Class Examples :
    - System.Random
    - System.DateTime
    - System.Diagnostics.PerformanceCounter
    - System.IO.FileInfo
    - System.Int32
    - System.Double
    - System.Speech
    - System.Web

- added Third Party Assembly Access - ptrex
    - WinSCP : https://winscp.net/eng/download.php
    - IonicZip : http://dotnetzip.codeplex.com/

Link to comment
Share on other sites

Hi Guys,

I got something really interesting going !

We can call native PowerShell Commands using the .NET CLR, which is much more interesting then calling .NET functions.

But again I need your help to get the output ... 

 

Example5() ; System.Management.Automation

Func Example5()
    Local $oAssembly = _CLR_LoadLibrary("System.Management.Automation")
    ConsoleWrite("!$oAssembly: " & IsObj($oAssembly) & @CRLF)

    ; Create Object
    Local $pAssemblyType = 0
    $oAssembly.GetType_2("System.Management.Automation.PowerShell", $pAssemblyType)
    ConsoleWrite("$pAssemblyType = " & Ptr($pAssemblyType) & @CRLF)

    Local $oActivatorType = ObjCreateInterface($pAssemblyType, $sIID_IType, $sTag_IType)
    ConsoleWrite("IsObj( $oAssemblyType ) = " & IsObj($oActivatorType) & @TAB & @CRLF)

    ; Create Object
    Local $pObjectPS = 0
    $oActivatorType.InvokeMember_3("Create", 0x158, 0, 0, 0, $pObjectPS)
    ConsoleWrite("IsObject: " & IsObj($pObjectPS) & @TAB & "$pObject: " & ObjName($pObjectPS) & @CRLF)

    $pObjectPS.AddCommand("Get-Process") ; <<<<<<<<<<<<<< PS COMMAND HERE <<<<<<<<<<<<<<

    $objAsync = $pObjectPS.BeginInvoke()
    ConsoleWrite("$objAsync " & IsObj($objAsync & @TAB & "$pObject: " & ObjName($objAsync) ) & @CRLF)

    $objPsCollection = $pObjectPS.EndInvoke($objAsync)

;~  Debug
    ConsoleWrite("$objPsCollection: " & IsObj($objPsCollection) & @TAB & "$objPsCollection: " & ObjName($objPsCollection) & " - " & ObjName($objPsCollection,6) & " - " & ObjName($objPsCollection,3) & @CRLF)

    Local $pColls = $objPsCollection
    ConsoleWrite("$pColl = " & Ptr($pColls) & @CRLF)
    Local $iDim = SafeArrayGetDim($pColls)
    ConsoleWrite("$iDim = " & $iDim & @CRLF)

;~  Local $vt = 0
    SafeArrayGetVartype($objAsync, $vt)
    ConsoleWrite("$vt = " & $vt & @CRLF)

EndFunc

As far as I can see it return an Object that contains the string output of the PS Command. 

But whatever I tried with using SafeArrays or Conventional COM approach I can't get anything visible ?

I added some debugging info to the script, so you can see the output info of the object.

If we can this going then we have a winner again !

Link to comment
Share on other sites

using System;
using System.Management.Automation;  // Windows PowerShell namespace.


namespace HostPS1
{
  class HostPS1
  {
    static void Main(string[] args)
    {

      // Call the PowerShell.Create() method to create an 
      // empty pipeline.
      PowerShell ps = PowerShell.Create();

      // Call the PowerShell.AddCommand(string) method, add 
      // the Get-Process cmdlet to the pipeline. Do 
      // not include spaces before or after the cmdlet name 
      // because that will cause the command to fail.
      ps.AddCommand("Get-Process");

      Console.WriteLine("Process                 Id");
      Console.WriteLine("----------------------------");


      // Call the PowerShell.Invoke() method to run the 
      // commands of the pipeline in the default runspace.
        foreach (PSObject result in ps.Invoke())
      {
        Console.WriteLine("{0,-24}{1}",
                result.Members["ProcessName"].Value,
                result.Members["Id"].Value);
      } // End foreach.
    } // End Main.
  } // End HostPs1.
}
  •  

why you use begininvoke?

 

Edited by junkew
<ComVisibleAttribute(False)>
Link to comment
Share on other sites

Just scanning some powershell examples in C#

https://www.codeproject.com/Articles/18229/How-to-run-PowerShell-scripts-from-C

So

Approach 1 is copy/paste above in a class and use the CLR compile functions and you get your full result back as string

Approach 2 is what you are trying but then I assume you have to read from stdout/stdin with regular autoit functions

Approach 3 DanyFirex jumps in and easily translates it from C# to AutoIt :sweating:

Link to comment
Share on other sites

Link to comment
Share on other sites

Approach 1 working

Change this in clr.au3 in _CLR_CompileAssembly then at least you get an indication for each compile error

If $oCompilerRes.Errors.Count() Then
        ConsoleWrite("!" & $oCompilerRes.Errors.Count() & @CRLF)
        ConsoleWrite("!" & $oCompilerRes.Errors.Item(0).ErrorText() & @CRLF)
        Return 0
    EndIf

AutoIt with all dll's needed referenced

#include ".\Includes\CLR.au3"

_Example()

Func _Example()
;~ C:\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0
    Local $oAssemblyCSharp = _CLR_CompileCSharp(FileRead("CodePS.cs"),"System.dll|System.Core.dll|System.Data.dll|System.Management.dll|C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll")
    If IsObj($oAssemblyCSharp ) Then
        ConsoleWrite("$oAssembly: " & IsObj($oAssemblyCSharp) & @CRLF)
        Local $oPS = _CLR_CreateObject($oAssemblyCSharp, "myPowerShell.PowerShellSample")
        ConsoleWrite("$oObject: " & IsObj($oPS) & @CRLF)
        If IsObj($oPS) Then
            consolewrite($oPS.RunScript("Get-Process"))
        EndIf
    Else
        consolewrite("compile error")
    EndIf

EndFunc   ;==>_Example

C# Class working from Visual C# Community Edition. Save as CodePS.cs

using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
//using System.Windows.Forms;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;

namespace myPowerShell {
class PowerShellSample 
{
        /// <summary>
        /// Runs the given powershell script and returns the script output.
        /// </summary>
        /// <param name="scriptText">the powershell script text to run</param>
        /// <returns>output of the script</returns>
        /// RunScript("Get-Process")
        public string RunScript(string scriptText)
        {
            // create Powershell runspace
            Runspace runspace = RunspaceFactory.CreateRunspace();

            // open it
            runspace.Open();

            // create a pipeline and feed it the script text
            Pipeline pipeline = runspace.CreatePipeline();
            pipeline.Commands.AddScript(scriptText);

            // add an extra command to transform the script output objects into nicely formatted strings
            // remove this line to get the actual objects that the script returns. For example, the script
            // "Get-Process" returns a collection of System.Diagnostics.Process instances.
            pipeline.Commands.Add("Out-String");

            // execute the script
            Collection<PSObject> results = pipeline.Invoke();

            // close the runspace
            runspace.Close();

            // convert the script result into a single string
            StringBuilder stringBuilder = new StringBuilder();
            foreach (PSObject obj in results)
            {
                stringBuilder.AppendLine(obj.ToString());
            }

            return stringBuilder.ToString();
        }
}
}

 

Steps done

1.  Find a C# example

2. Compile it working in Visual Studio (as console application)

3. Make it a dll project and check all the references you need and write down their path etc. and get it compile error free 

4. Copy / paste the class file to a new file.cs

5. Do above example in AutoIt

Important to find location of Powershell try in ISE: [psobject].assembly.location

Edited by junkew
In ISE: [psobject].assembly.location
Link to comment
Share on other sites

yes fully working

>Running:(3.3.14.2):C:\Program Files (x86)\AutoIt3\autoit3_x64.exe "C:\Users\Elwin\Documents\UIA\CLRv3\CLRv3\.NET Code Compiler.au3"    
--> Press Ctrl+Alt+Break to Restart or Ctrl+Break to Stop
System.dll
System.Core.dll
System.Data.dll
System.Management.dll
C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll
$oAssembly: 1
$oObject: 1

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName                                                   
-------  ------    -----      -----     ------     --  -- -----------                                                   
    149      10     1864       6408              8004   1 adb                                                           
    218      20     3624      10008              3684   0 AppleMobileDeviceService                                      
    634      38    11336      27868       0,56  18832   1 ApplePhotoStreams                                             
    442      30     7264      23528       0,25  12988   1 ApplePhotoStreamsDownloader                                   
    363      23     5048      14736       0,25  18476   1 APSDaemon                                                     
    140       8     1328       5388              2144   0 armsvc                                                        
    369      23    68592      15356       1,45  20720   1 asusUPCTLoader                                                
    397      13    18452      15616      40,91   3812   0 audiodg                                                       
    402      40    83488      56496       0,39  21264   1 AutoIt3_x64                                                   
    149      12     4896      13096       0,05  10684   1 AutoIt3Wrapper                                                
    185      13     5172      13756       0,06  21060   1 AutoIt3Wrapper                                                
    137       8     1296       5312              3724   0 BcmSqlStartupSvc                                              
    436      57   160840     118012      18,20   6136   1 chrome

 

Link to comment
Share on other sites

LS cmdlet example

#include ".\Includes\CLR.au3"

_Example()

Func _Example()
;~ C:\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0
    Local $oAssemblyCSharp = _CLR_CompileCSharp(FileRead("CodePS.cs"),"System.dll|System.Core.dll|System.Data.dll|System.Management.dll|C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll")
    If IsObj($oAssemblyCSharp ) Then
        ConsoleWrite("$oAssembly: " & IsObj($oAssemblyCSharp) & @CRLF)
        Local $oPS = _CLR_CreateObject($oAssemblyCSharp, "myPowerShell.PowerShellSample")
        ConsoleWrite("$oObject: " & IsObj($oPS) & @CRLF)
        If IsObj($oPS) Then
            consolewrite($oPS.RunScript("Ls"))
        EndIf
    Else
        consolewrite("compile error")
    EndIf

EndFunc   ;==>_Example

output

>Running:(3.3.14.2):C:\Program Files (x86)\AutoIt3\autoit3_x64.exe "C:\Users\Elwin\Documents\UIA\CLRv3\CLRv3\.NET Code Compiler.au3"    
--> Press Ctrl+Alt+Break to Restart or Ctrl+Break to Stop
System.dll
System.Core.dll
System.Data.dll
System.Management.dll
C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll
$oAssembly: 1
$oObject: 1


    Directory: C:\Users\Elwin\Documents\UIA\CLRv3\CLRv3


Mode                LastWriteTime         Length Name                                                                   
----                -------------         ------ ----                                                                   
d-----        26-5-2017     20:10                Includes                                                               
-a----        20-4-2017     12:42           5638 .NET Access Custom Assembly.au3                                        
-a----        12-5-2017     21:00           1541 .NET Access Custom Assembly2.au3                                       
-a----        17-5-2017     23:08           5644 .NET Access Native MSCorLib Classes - GetMembers.au3                   
-a----        17-5-2017     23:04           2171 .NET Access Native MSCorLib Classes - GetTypes.au3                     
-a----        20-4-2017     18:01           1199 .NET Access Native MSCorLib Classes - Invoke Members.au3

 

 

 

Link to comment
Share on other sites

Usefull examples

Top memory using processes

consolewrite($oPS.RunScript("ps | sort –p ws | select –last 5"))

Bios info

consolewrite($oPS.RunScript("Get-WmiObject -Class Win32_BIOS -ComputerName ."))

Someone can now start a different thread to build his own ISE IDE :sweating:

 

Quickfixes installed

consolewrite($oPS.RunScript("Get-WmiObject -Class Win32_QuickFixEngineering -ComputerName ."))

 

Edited by junkew
added one more
Link to comment
Share on other sites

Some help needed on expando object, i can create the object but not able to add properties (and later even more difficult add eventhandler that calls autoit as a method)

I get com error on the property whereas documentation says that it will add the property on the fly when used first time

#include ".\Includes\CLR.au3"

example15()

Func Example15()
    local $goActivator=getActivator()
    Local $oAssCore = _CLR_LoadLibrary("mscorlib")
    Local $oAssCore2= _CLR_LoadLibrary("system.core")

    Local $ooItsAnOOObject= _CLR_CreateObject($oAssCore2, "System.Dynamic.ExpandoObject")

    ConsoleWrite("!$ooItsAnOOObject " & IsObj($ooItsAnOOObject) & @CRLF)
;~$ooItsAnOOObject.x=123
 $ooItsAnOOObject.name="O its a name property"
;~ $ooItsAnOOObject.street ="OO its a little magic"
;~ $ooItsAnOOObject.city= "OO .NET is expandoing more"
;~ $ooItsAnOOObject.events= "Yes, but do not know how yet but try... methods of dynamicobject look promising"

    ConsoleWrite("!$ooItsAnOOObject.name  " & $ooItsAnOOObject.name  & @CRLF)
;~  ConsoleWrite("!$ooItsAnOOObject.street  " & $ooItsAnOOObject.street  & @CRLF)
;~  ConsoleWrite("!$ooItsAnOOObject.city  " & $ooItsAnOOObject.city  & @CRLF)
;~  ConsoleWrite("!$ooItsAnOOObject.events  " & $ooItsAnOOObject.events  & @CRLF)


EndFunc
  • This is given as alternative but no clue how to transform that to AutoIt syntax
dynamic person = new ExpandoObject(); 
var dictionary = (IDictionary<string, object>)person; 
dictionary.Add("Name", "Filip"); 
dictionary.Add("Age", 24); 
- See more at: https://www.filipekberg.se/2011/10/02/adding-properties-and-methods-to-an-expandoobject-dynamicly/#sthash.V2Sn6b3t.dpuf
dynamic expando = new ExpandoObject();
        var p = expando as IDictionary<String, object>;
        p["A"] = "New val 1";
        p["B"] = "New val 2";
  • If expando is not working then dynamic class in C# in a CS file will probably work together with compileCSharp as done for powershell example

 

 

Link to comment
Share on other sites

Hi Junkew, 

Thanks for post the PS example, but this is still not what we are looking for... 

Even though it works, you still use C# code in the middle, so this is a kind of workaround solution ... comparable with PowerShell COM

While the initial attempt is to get rid of all the middle-ware :(

Nevertheless if I change the command in my code POST 283 to something not accepted I get proper out ?

$pObjectPS.AddCommand("Get-Process111") ; <<<<<<<<<<<<<< PS COMMAND HERE <<<<<<<<<<<<<<
Quote

!$oAssembly: 1
$pAssemblyType = 0x0260FEF8
IsObj( $oAssemblyType ) = 1    
IsObject: 1    $pObject: Object
$objAsync 1
.NET CLR Activator - GetType.au3 (32) : ==> COM Error intercepted !
    err.number is:         0x80020009
    err.windescription:    Exception occurred.

    err.description is:     The term 'Get-Process111' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
    err.source is:         System.Management.Automation
    err.helpfile is:     
    err.helpcontext is:     0
    err.lastdllerror is:     0
    err.scriptline is:     32
    err.retcode is:     0x80131501
 

So I am wondering where the PS command output redirected to in case you run a proper PS command ... ?

 

Link to comment
Share on other sites

ok will change the initial version to run from memory as is in the other examples although that is with an exe.
The DLL is not needed in the middle you can do it straight from memory.
      .NET CLR Execute EXE in Memory.au3

But I agree same should be possible with approach 2. Posted a link to this thread in the POS Com thread. maybe others jump in.

 

 

 

Link to comment
Share on other sites

OK I made a little progress with my example,

I can do output to GRID without the needed of middleware ... :D

But output to Console give this kind of error ?

Quote

.NET CLR Activator - GetType.au3 (33) : ==> COM Error intercepted !
    err.number is:         0x80020009
    err.windescription:    Exception occurred.

    err.description is:     A command that prompts the user failed because the host program or the command type does not support user interaction. Try a host program that supports user interaction, such as the Windows PowerShell Console or Windows PowerShell ISE, and remove prompt-related commands from command types that do not support user interaction, such as Windows PowerShell workflows.
    err.source is:         System.Management.Automation
    err.helpfile is:     
    err.helpcontext is:     0
    err.lastdllerror is:     0
    err.scriptline is:     33
    err.retcode is:     0x80131501

 

Link to comment
Share on other sites

For sure its not an array you get back its a specific collection object

@Danyfirex  (or other expert) Can you explain how to transform this to AutoIt?
                     So more or less in generic terms what to do

                     1. If return value is string            ---> we know

                      2. if return value is integer          ---> we know

                    3. if return value is an safearray    ---> we know, but could be easier hopefully with some wrappers around the functions

                    4. if return value is an .NET object   ---> no clue, and this powershell endinvoke is an example 

                    5. if return value is multiple interfaces (CAST logic in C#)     ---> No clue how to cast to different interface when needed

                     6. ........    ---> we hit them later :D

Public Function EndInvoke (
    asyncResult As IAsyncResult
) As PSDataCollection(Of PSObject)

Parameters
asyncResult
Type: System.IAsyncResult
The IAsyncResult interface returned by the BeginInvoke call. This interface represents the status of the call.

Return Value

Type: System.Management.Automation.PSDataCollection(Of PSObject)
A PSDataCollection(Of T) of type PSObject that contains the results of the BeginInvoke call. This method returns null when the BeginInvoke overloads that take an output buffer are used.

 

Link to comment
Share on other sites

I think I start to understand why I don't get this rolling ... but let me further investigate...

Problem is I am leaving 'AGAIN' on Holidays so I can't proceed my route :( for a while...

But you are right we should also get our hands on how to deal with .NET system.objects returned .... ?

Some other one that fits in your "6" is how to get access to .NET Constants & Enums .... ? 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...