Jump to content

Using .NET libary with AutoIt, possible?


Recommended Posts

Have a nice holiday

And probably here for @Danyfirex some direction on how to handle things

https://stackoverflow.com/questions/7371775/how-can-i-send-a-managed-object-to-native-function-to-use-it

code example from above link

 

using namespace System;
using namespace System::Runtime::InteropServices;

void managed_function() 
{ 
  Object^ obj = gcnew Object();

  // Convert Object^ to void*
  GCHandle handle = GCHandle::Alloc(obj);
  IntPtr pointer = GCHandle::ToIntPtr(handle);
  void* ptr = pointer.ToPointer();

  unmanaged_function(ptr);

  handle.Free();
} 

void unmanaged_function(void* ptr) 
{
  // Convert void* to Object^
  IntPtr pointer(ptr);
  GCHandle handle = GCHandle::FromIntPtr(pointer);
  Object^ obj = (Object^)handle.Target;

  obj->SomeManagedMethods();
}

 

Link to comment
Share on other sites

  • UnknownWrapper Used to make an Object look like an IUnknown interface pointer when passed inside a VARIANT.

  • DispatchWrapper Used to make an Object look like an IDispatch interface pointer when passed inside a VARIANT.

http://www.informit.com/articles/article.aspx?p=27219&seqNum=8

my mind is processing this it will take a while :sweating: before I understand how to do this with AutoIt

Link to comment
Share on other sites

This is why we can't get the output to Console in PS :

https://learn-powershell.net/2016/02/14/another-way-to-get-output-from-a-powershell-runspace/

Apparently the output of PowerShell is send to a specific PowerShell Console, which is different then then Windows Console ?

That's why we can't intercept the stream in a regular way... :(

Solutions :

Get access to the   System.Management.Automation.PSDataCollection  Class. Which I tried but can't get running either ? 

Error is Null Point returned when created the CLR Object.

Or find a smart work around ... I will sleep over this during my next week...

Link to comment
Share on other sites

Hi All,

Just wanted to say that I feel like a "child in the candy store". 

Since we have now access to .Net and especially to Powershell in a native way. :lmao: :D

Gettting access to the .Net Classes and Third Party Assemblies is one thing. But also having access to all the PS Cmdlets and PS Modules out there is making it all complete !

Something I was missing for the last 5 years (and I hope someone else did too) !!

For the AutoIT community this is a real step up to an new age ... These new 'Toys' for 'Boys' (and girls) are giving us some new playground.

Thanks you all so far for the nice collaboration and contributions.

So someone should buy you all a beer at least, now that we came this far...  

Link to comment
Share on other sites

The link you share suggest $handle and $object on PS Commandline have some kind of default method that gets executed on the object.

Most logical is that default method is ToString() but as far as I know that returns the name of the class

Seen multiple solutions to deal with stdout we just have to transform and read a little more

  • Anyway, yes we have a huge improvement achieved with CLR.AU3 and we need more tutorials and cleanup and some help on transforming the pointers/handles of the .NET objects to equivalents in AutoIt which will take months/years.
  • Just waiting for MultiThread.AU3 (because we can):'(
  • No clue if we can make a nice AutoIt debugger with this CLR stuff.
  • Sure we can do OO.AU3 with Dynamic keyword
  • Would be fun to build an AutoIt C# lexer with http://www.antlr.org/download.html
  • We will reach 2050 and beyond with this stuff ;-)
Link to comment
Share on other sites

Sure agree 100%, we all can see the huge potential...

What i also forgot to mention is that I am very pleasantly surprised, about the speed it all runs in to access the .Net framework ... 

I am not complaining at all at the moment, I' m happy as can be :ILA2:... even if we have seen some limitations.

I will continue to post examples as we go along... :D  

So now we only need more contributors...

Edited by ptrex
Link to comment
Share on other sites

Link to comment
Share on other sites

The powershell version without any intermediate file. (continuation of post 289)

  • Just made a small function getCode that returns the code as a string
    Be aware on " to change to ""
    And add " to beginning of line
    and add " & @CRLF & _  to end of line
  • And changed this
    _CLR_CompileCSharp(FileRead("CodePS.cs")
    to 
    _CLR_CompileCSharp(getCode()
  • We could make magic base64 zipped strings but as an example this is cleaner and more understandable
  • Still approach 1 fully from C# that converts the collection back to a string
#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")
    Local $oAssemblyCSharp = _CLR_CompileCSharp(getCode(),"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

func getCode()
    $code="//PowershellFromMemory.au3" & @CRLF & _
"using System;" & @CRLF & _
"using System.IO;" & @CRLF & _
"using System.Collections.Generic;" & @CRLF & _
"using System.ComponentModel;" & @CRLF & _
"using System.Data;" & @CRLF & _
"using System.Drawing;" & @CRLF & _
"using System.Text;" & @CRLF & _
"//using System.Windows.Forms;" & @CRLF & _
"using System.Collections.ObjectModel;" & @CRLF & _
"using System.Management.Automation;" & @CRLF & _
"using System.Management.Automation.Runspaces;" & @CRLF & _
""  & @CRLF & _
"namespace myPowerShell { " & @CRLF & _
 ""  & @CRLF & _
"class PowerShellSample " & @CRLF & _
""  & @CRLF & _
"{ " & @CRLF & _
""  & @CRLF & _
"        /// <summary> " & @CRLF & _
"        /// Runs the given powershell script and returns the script output. " & @CRLF & _
"        /// </summary> " & @CRLF & _
"        /// <param name=""scriptText"">the powershell script text to run</param>" & @CRLF & _
"        /// <returns>output of the script</returns>" & @CRLF & _
"        /// RunScript(""Get-Process"")" & @CRLF & _
"        public string RunScript(string scriptText)" & @CRLF & _
"        {" & @CRLF & _
"            // create Powershell runspace" & @CRLF & _
"            Runspace runspace = RunspaceFactory.CreateRunspace();" & @CRLF & _
""  & @CRLF & _
"            // open it" & @CRLF & _
"            runspace.Open();" & @CRLF & _
""  & @CRLF & _
"            // create a pipeline and feed it the script text" & @CRLF & _
"            Pipeline pipeline = runspace.CreatePipeline();" & @CRLF & _
"            pipeline.Commands.AddScript(scriptText);" & @CRLF & _
""  & @CRLF & _
"            // add an extra command to transform the script output objects into nicely formatted strings" & @CRLF & _
"            // remove this line to get the actual objects that the script returns. For example, the script" & @CRLF & _
"            // ""Get-Process"" returns a collection of System.Diagnostics.Process instances." & @CRLF & _
"            pipeline.Commands.Add(""Out-String"");" & @CRLF & _
""  & @CRLF & _
"            // execute the script" & @CRLF & _
"            Collection<PSObject> results = pipeline.Invoke();" & @CRLF & _
""  & @CRLF & _
"            // close the runspace" & @CRLF & _
"            runspace.Close();" & @CRLF & _
""  & @CRLF & _
"            // convert the script result into a single string" & @CRLF & _
"            StringBuilder stringBuilder = new StringBuilder();" & @CRLF & _
"            foreach (PSObject obj in results)" & @CRLF & _
"            {" & @CRLF & _
"                stringBuilder.AppendLine(obj.ToString());" & @CRLF & _
"            }" & @CRLF & _
""  & @CRLF & _
"            return stringBuilder.ToString();" & @CRLF & _
"        }" & @CRLF & _
"}" & @CRLF & _
"}" & @CRLF

;~ consolewrite($code)
return $code
EndFunc
Link to comment
Share on other sites

And some babysteps with Multi Threading (looking for a good example that can proof that this works although I know it will have its limitations with reentrancy and @Error)

#include ".\Includes\CLR.au3"

_Example()

Func _Example()
    consolewrite("starting")
;~ 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")
    Local $oAssemblyCSharp = _CLR_CompileCSharp(getCode2(),"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 $oMT = _CLR_CreateObject($oAssemblyCSharp, "myMultiThreadClass")
        ConsoleWrite("$oObject: " & IsObj($oMT) & @CRLF)
        If IsObj($oMT) Then
            consolewrite($oMT.ToString() & @CRLF)
            $oMT.Run()
            sleep(20000) ;~ Run for about 20 seconds
        EndIf
    Else
        consolewrite("compile error")
    EndIf

EndFunc   ;==>_Example
func getCode2()
$code="using System;"& @CRLF & _
"" & @CRLF & _
"using System.Threading;"& @CRLF & _
"using System.Security;"& @CRLF & _
""& @CRLF & _
"public class MyThread {"& @CRLF & _
""& @CRLF & _
"        public void Thread1() {" & @CRLF & _
"                for (int i = 0; i < 10; i++) {"& @CRLF & _
""& @CRLF & _
"Random random = new Random();" & @CRLF & _
"Int32 mseconds=random.Next(3, 10)*500;" & @CRLF & _
" //                       Console.WriteLine(mseconds);" & @CRLF & _
"                        Thread thr = Thread.CurrentThread;"& @CRLF & _
"                        Console.WriteLine(thr.Name + ""="" + i + "" waited "" + mseconds);"& @CRLF & _
""& @CRLF & _
"                        try {"& @CRLF & _
"              Thread.Sleep(mseconds);" & @CRLF & _
"//                                Thread.Sleep(1);"& @CRLF & _
"                       }"& @CRLF & _
"                        catch (ArgumentException ae) {"& @CRLF & _
"                                Console.WriteLine(ae.ToString() );"& @CRLF & _
"                        }"& @CRLF & _
"                        catch (ThreadInterruptedException tie) {"& @CRLF & _
"                                Console.WriteLine(tie.ToString() );"& @CRLF & _
"                        }"& @CRLF & _
"                        catch (SecurityException se) {"& @CRLF & _
"                                Console.WriteLine(se.ToString() );"& @CRLF & _
"                       }"& @CRLF & _
"                }"& @CRLF & _
"        }"& @CRLF & _
"}"& @CRLF & _
""& @CRLF & _
"public class myMultiThreadClass {"& @CRLF & _
""& @CRLF & _
"        public void Run() {" & @CRLF & _
"                Console.WriteLine(""Before start thread"");"& @CRLF & _
""& @CRLF & _
"                MyThread thr1 = new MyThread();"& @CRLF & _
"               MyThread thr2 = new MyThread();"& @CRLF & _
"               MyThread thr3 = new MyThread();"& @CRLF & _
"               MyThread thr4 = new MyThread();"& @CRLF & _
""& @CRLF & _
"                Thread tid1 = new Thread(new ThreadStart(thr1.Thread1) );"& @CRLF & _
"                Thread tid2 = new Thread(new ThreadStart(thr2.Thread1) );"& @CRLF & _
"                Thread tid3 = new Thread(new ThreadStart(thr3.Thread1) );"& @CRLF & _
"                Thread tid4 = new Thread(new ThreadStart(thr4.Thread1) );"& @CRLF & _
""& @CRLF & _
"               tid1.Name = ""Thread 1"";"& @CRLF & _
"               tid2.Name = ""Thread 2"";"& @CRLF & _
"               tid3.Name = ""Thread 3"";"& @CRLF & _
"               tid4.Name = ""Thread 4"";"& @CRLF & _
""& @CRLF & _
"                tid1.Start();"& @CRLF & _
"               Thread.Sleep(1500);"& @CRLF & _
"                tid2.Start();"& @CRLF & _
"               Thread.Sleep(1500);"& @CRLF & _
"                tid3.Start();"& @CRLF & _
"               Thread.Sleep(1500);"& @CRLF & _
"                tid4.Start();"& @CRLF & _
"        }"& @CRLF & _
"}"
;~ consolewrite($code)
return $code
endFunc

 

edit: add reference to MT discussions as I am aware that with CLR.AU3 you will not have suddenly full swing MT

 

Edited by junkew
No intention to start an MT discussion
Link to comment
Share on other sites

Hi These are nice examples, but not quite without middieware  code ?

These are without middleware code (But the only the Output to Console is not yet working :(

So for the time being we can combine the 2 approaches, yours for Console Output and mine for all the rest ?

CmdLet Example

#AutoIt3Wrapper_UseX64=n
#include "CLR.Au3"

; *****************************************************
; PARAMETERS : (Output mode )
; OUTPUT : 0 = Console, 1 = GRID, 2 = Printer, 3 = File
;******************************************************

; <<<<<<<<<<<< Uncomment here >>>>>>>>>>>>>>>>
;~ _Run_PSHost_Cmdlet(0)
_Run_PSHost_Cmdlet(1)
;~ _Run_PSHost_Cmdlet(2)
;~ _Run_PSHost_Cmdlet(3)


Func _Run_PSHost_Cmdlet($iOutput)
    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)

; <<<<<<<<<<<<<<<<<<< PS COMMAND HERE >>>>>>>>>>>>>>>>>>>>
        $pObjectPS.AddCommand("Get-Process")
;~      $pObjectPS.AddCommand("Sort-Object")
;~      $pObjectPS.AddParameter("Descending")
;~      $pObjectPS.AddArgument("id")

; <<<<<<<<<<<<<<<<<<< Output >>>>>>>>>>>>>>>>>>>>

        Switch $iOutput
            Case 0
                $pObjectPS.AddCommand("Out-Host")
;~              $pObjectPS.AddCommand("Format-Wide")
            Case 1
                $pObjectPS.AddCommand("Out-GridView")

            Case 2
                $pObjectPS.AddCommand("Out-Printer")
            Case 3
                $pObjectPS.AddCommand("Out-File")
                    $sFile = @DesktopDir & "\PShost.txt"
                    ConsoleWrite("> " & $sFile & @CRLF)
                $pObjectPS.AddArgument($sFile)
            Case Else
                MsgBox(0,"PSHost","Wrong Output Choice ?")
        EndSwitch

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

    $objPsCollection = $pObjectPS.EndInvoke($objAsync)
;~  ConsoleWrite("$objPsCollection: " & IsObj($objPsCollection) & @TAB & "$objPsCollection: " & ObjName($objPsCollection) & " - " & ObjName($objPsCollection,6) & " - " & ObjName($objPsCollection,3) & @CRLF)

        Switch $iOutput
            Case 0
                MsgBox(0,"PSHost","Output To Console is not working yet ?")
            Case 1
                Sleep(2000)
;~              WinWaitClose("")
            Case 3
                MsgBox(0,"PSHost","File Output Ready on Desktop.")
        EndSwitch
EndFunc

Script Example

#AutoIt3Wrapper_UseX64=n
#include "CLR.Au3"

; *****************************************************
; PARAMETERS : (Script Text, Output mode )
; OUTPUT : 0 = Console, 1 = GRID, 2 = Printer, 3 = File
;******************************************************

; <<<<<<<<<<<< Uncomment here >>>>>>>>>>>>>>>>
_Run_PSHost_Script("$PSVersionTable.PSVersion", 0 )
;~ _Run_PSHost_Cmdlet("LS", 1)
;~ _Run_PSHost_Cmdlet("Get-Process", 1)
;~ _Run_PSHost_Cmdlet("[appdomain]::currentdomain.GetAssemblies() | Get-Member ", 1)


Func _Run_PSHost_Script($PSScript, $iOutput)
    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)

; <<<<<<<<<<<<<<<<<<< PS COMMAND HERE >>>>>>>>>>>>>>>>>>>>

    $pObjectPS.AddScript($PSScript) ; Add Script here

; <<<<<<<<<<<<<<<<<<< Output >>>>>>>>>>>>>>>>>>>>
        Switch $iOutput
            Case 0
                $pObjectPS.AddCommand("Out-Host")
;~              $pObjectPS.AddCommand("Format-Wide")

            Case 1
                $pObjectPS.AddCommand("Out-GridView")

            Case 2
                $pObjectPS.AddCommand("Out-Printer")
            Case 3
                $pObjectPS.AddCommand("Out-File")
                    $sFile = @DesktopDir & "\PShost.txt"
                    ConsoleWrite("> " & $sFile & @CRLF)
                $pObjectPS.AddArgument($sFile)
            Case Else
                MsgBox(0,"PSHost","Wrong Output Choice ?")
        EndSwitch

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

    $objPsCollection = $pObjectPS.EndInvoke($objAsync)
;~  ConsoleWrite("$objPsCollection: " & IsObj($objPsCollection) & @TAB & "$objPsCollection: " & ObjName($objPsCollection) & " - " & ObjName($objPsCollection,6) & " - " & ObjName($objPsCollection,3) & @CRLF)


        Switch $iOutput
            Case 0
                MsgBox(0,"PSHost","Output To Console is not working yet ?")
            Case 1
;~              Sleep(2000)
                WinWaitClose("")
            Case 3
                MsgBox(0,"PSHost","File Output Ready on Desktop.")
        EndSwitch
EndFunc

Hopefully someone can find a good solution for the PS console output to stdout redirection ... :graduated:

 

Link to comment
Share on other sites

You can do multitrheading in PS too, using CreateRunspacepool  from the  ' runspacefactory

$SessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()

$RunspacePool = [runspacefactory]::CreateRunspacePool

So this is worthwhile investigating, because the approach seems less complex ?

Link to comment
Share on other sites

Approach 1 works, approach 2 no solution, approach 3 DanyFirex is reading as i understand from post #308
 

So for approach 2

  • Your powershell object has no console.
    I doubt if you host the CLR if it creates one for you (to me that would not be logical as you do not allways need a console) 
    https://msdn.microsoft.com/en-us/library/system.console(v=vs.110).aspx\
    http://referencesource.microsoft.com/#mscorlib/system/console.cs,f907d79481da6ba4
     
  • SetOut method of console should do the trick
    or we should set configuration of the runtime when we start it with some config parameters that default stdout should be used.
    I assume in C# whenever you first time use console.writeline it initialises the console object which is not happening when you host a CLR.
     
  • Here some thoughts but no solution
    its repeating what we learned in the previous 300 post(s) with unwrap and invoke_member)
    If you use the powershell.net logic its all first in objects and you have to iterate over the collection to send it to any console.
    However I agree that Out-Host cmdlet can easily put it on StdOut (which is not there)
     
  • So we miss a console which I could create (i think) with below code but not working with dot syntax (will later try for invokemember)
    Maybe try CLR_CreateObject
    getConsole gives an object whereas getConsole2 does not
local $oConsole=getConsole()

consolewrite($oConsole.Title)   ;~---> ERROR

local $oConsole2=getConsole2()
consolewrite($oConsole2.Title)   ;~---> ERROR


func getConsole()
    Local $oAssembly = _CLR_LoadLibrary("mscorlib")
    Local $ptConcole = 0
    $oAssembly.GetType_2("System.Console", $ptConcole)
    Local $oConsole = ObjCreateInterface($ptConcole, $sIID_IType, $sTag_IType)

 ConsoleWrite("$oAssembly: " & IsObj($oAssembly) & @CRLF)
 ConsoleWrite("$pAssemblyType = " & Ptr($ptConcole) & @CRLF)
 ConsoleWrite("IsObj($oConsole) = " & IsObj($oConsole) & @TAB & @CRLF)

    return $oConsole
endfunc

func getConsole2()
    Local $oAssembly = _CLR_LoadLibrary("mscorlib")
    Local $oConsole = _CLR_CreateObject($oAssembly,"System.Console")

 ConsoleWrite("$oAssembly: " & IsObj($oAssembly) & @CRLF)
 ConsoleWrite("IsObj($oConsole2) = " & IsObj($oConsole) & @TAB & @CRLF)

    return $oConsole
endfunc

As in source comment in that article

/*
        Load a type from a DLL Assembly and call it


        Doing the same thing from native code as this C# code:


            System.Runtime.Remoting.ObjectHandle objptr;
            objptr = AppDomain.CurrentDomain.CreateInstanceFrom("ClassLibrary1.dll","ClassLibrary1.Class1");                                               
            object obj = objptr.Unwrap();
            Type t = obj.GetType();
            t.InvokeMember("Test",
                                   BindingFlags.InvokeMethod,
                                   null,
                                   obj,
                                   new object[0]);
 /*
    */

Which then is written in C++ like this
 

_ObjectHandlePtr pObjectHandle; 
        _ObjectPtr pObject; 
        _TypePtr pType;
        SAFEARRAY* psa;


        // Create an instance of a type from an assembly
        pObjectHandle = pDefaultDomain->CreateInstanceFrom(L"ClassLibrary1.dll",     // no path -- local directory
                                                                                      L"ClassLibrary1.Class1");
  
        variant_t vtobj = pObjectHandle->Unwrap();                                     // Get an _Object (as variant) from the _ObjectHandle
        vtobj.pdispVal->QueryInterface(__uuidof(_Object),(void**)&pObject);  // QI the variant for the Object iface
        pType = pObject->GetType();                                                         // Get the _Type iface
        psa = SafeArrayCreateVector(VT_VARIANT,0,0);                                // Create a safearray (0 length)
        pType->InvokeMember_3("Test",                                                     // Invoke "Test" method on pType
                                            BindingFlags_InvokeMethod,
                                            NULL,
                                            vtobj,
                                            psa );
        SafeArrayDestroy(psa);                                                                   // Destroy safearray
    }

 

Link to comment
Share on other sites

It more complex than it seems ... basically Powershell uses it's own Console. As you clearly can see when starting Powershell.exe or the PS ISE.

By default there is no interaction between the Windows stdin / stdout. All Output streams stay within the proprietary PS Console when using "Out-Host".

While we run the PSHost in AutoIT. There is a disconnection between the 2 environments, after the script has completed.

As you say the only way to get these 2 together is to have some middleware code in between. That grabs the PS output and send it to the Windows Console to be picked up by the PSHost script.

I have found an other stupid solution (more like  a workaround) is to use CLIPBOARD. That works really well for the time being until a more structural solution comes along.

 

Link to comment
Share on other sites

  • small step ahead with console gives its redirected but no clue where to
    so only first property gives a result
local $oConsole=getConsole()
local $propValue=0
$oConsole.InvokeMember_3("IsOutputRedirected", $BindingFlags_GetProperty, 0, 0, 0, $propValue)
consolewrite("IsOutputRedirected " & $propValue & @CRLF)
$oConsole.InvokeMember_3("Title", $BindingFlags_GetProperty, 0, 0, 0, $propValue)
consolewrite("Title " & $propValue & @CRLF)
$oConsole.InvokeMember_3("WindowHeight", $BindingFlags_GetProperty, 0, 0, 0, $propValue)
consolewrite("WindowHeight " & $propValue & @CRLF)
$oConsole.InvokeMember_3("Clear", 0x158, 0, 0, 0, 0)

func getConsole()
    Local $oAssembly = _CLR_LoadLibrary("mscorlib")
    Local $ptConcole = 0
    $oAssembly.GetType_2("System.Console", $ptConcole)
    Local $oConsole = ObjCreateInterface($ptConcole, $sIID_IType, $sTag_IType)

 ConsoleWrite("$oAssembly: " & IsObj($oAssembly) & @CRLF)
 ConsoleWrite("$pAssemblyType = " & Ptr($ptConcole) & @CRLF)
 ConsoleWrite("IsObj($oConsole) = " & IsObj($oConsole) & @TAB & @CRLF)

    return $oConsole
endfunc
Link to comment
Share on other sites

The key seems to be that we have run all of the code in a separate runspace, like I mentioned in post 312.

If you see all the example, they are all run in a separate PS runspace

RunspaceFactory.CreateRunspace

There seems to be no way around it. 

An other challenge to tackle is for example when you run a PS script where .net Forms are involved. They will NOT run to unless you run them in a seperate runspace.

The background is most likely that we are writing a wrapper in a Windows GUI environment and not .NET WPF. This means that the GUI which is focussing a WPF  is not present, and therefore will not show...

So again we have to take a step back and get the PS Runspace working using the .NET CLR Framework. This will probably solve some fundamental issues we are facing now.

If all of this would be easy, we would not be needed ;) 

Back to my Holidays now ...

Edited by ptrex
Link to comment
Share on other sites

  • Will be working on a new summary and reading backward in the thread for some fundamentals
  • I have the feeling from unmanaged we can quickly get an object / COM alike
    that we can use dot syntax with so less confusing when to use dot and when to use invokemember.
public static object GetRuntimeInterfaceAsObject(Guid clsid, Guid riid)
        {
            var getRuntimeInterfaceAsObject = typeof(RuntimeEnvironment).GetMethod("GetRuntimeInterfaceAsObject", BindingFlags.Public | BindingFlags.Static);
            return getRuntimeInterfaceAsObject.Invoke(null, new object[] { clsid, riid });
        }

implemented as an RCW should give a COM layer. But I do not have a clear clue on how to do above in AutoIT 
https://referencesource.microsoft.com/#mscorlib/system/runtime/interopservices/runtimeenvironment.cs,6079aecca66e8a2d,references

//
        // This function does the equivalent of calling GetInterface(clsid, riid) on the
        // ICLRRuntimeInfo representing this runtime. See MetaHost.idl for a list of
        // CLSIDs and IIDs supported by this method.
        //
        // Returns an RCW to requested interface on success. Throws
        // COMException with failed HR if there is a QI failure.
        //
        [System.Security.SecurityCritical]  // do not allow partial trust callers
        [ComVisible(false)]
        [ResourceExposure(ResourceScope.Process)]
        [ResourceConsumption(ResourceScope.Process)]
        public static object GetRuntimeInterfaceAsObject(Guid clsid, Guid riid)
        {
            IntPtr p = IntPtr.Zero;
            try {
                p = GetRuntimeInterfaceImpl(clsid, riid);
                return Marshal.GetObjectForIUnknown(p);
            } finally {
                if(p != IntPtr.Zero) {
                    Marshal.Release(p);
                }
            }
        }
  • Regarding powershell I do not feel there is a runspace needed as you get a result of objects already (although that can be approach 4)
    As you can see in Mutlithread example I write from managed to the unmanaged autoit output pane and as such it shares the same stdout
    In approach 2 you have the result as an object and all you want
       1. is to iterate over the objects
       2. consolewrite(object.getitem($i).value)
    For that we have the answer already in this thread but we are just not handy enough to do this quickly
      a. You get from managed just a pointer to a variant
      b. and based on the variant type you can determine what it is (like we do for determining safearray). If its not a variant returned its a managed handle (I assume)
      c. So assuming you have a handle its just a combination of unwrap and invokemember_3 (assuming dot syntax will not work when we use createinstance)
    http://www.red-gate.com/products/dotnet-development/ants-memory-profiler/learning-memory-management/understanding-dotnet-interoperability
    gives a short overview on the pointers/references/objects/variants in memory
Edited by junkew
reference to C# source
Link to comment
Share on other sites

Your multithread example is not using a PS Host, but just a using a .NET Class, correct ? 

        Local $oMT = _CLR_CreateObject($oAssemblyCSharp, "myMultiThreadClass")

; Just running C# code
Local $oMT = _CLR_CreateObject($oAssemblyCSharp, "myMultiThreadClass")

That's why you don't need a separate PS Runspace obviously...

There are 2 different worlds apparently. Anyhow we need more proof of concepts to feel how the behavior is between the 2 (.NET and PS). 

 

Link to comment
Share on other sites

Powershell is just a .NET assembly just like all the others

Its having first an internal structure when it runs all its commands and stores it in collections

The only last step which they do in their own ISE.EXE is spit it out to a console which can be any stream including the StdOut of Scite window.

The example I gave on multithread is only doing in the class itself a console.write but here a rewritten version of approach 1 where I am

a. writing to stdout from the C# managed part

b. write the returned string from AutoIt to the AutoIt console

 Here a rewrite of approach 1 to not confuse you with the MT part(s)

  • see in the getCode function the
    console.writeline
          and see in the AutoIt part the
    consolewrite
  • and as you can see it both writes to the AutoIt stdout console
#include ".\Includes\CLR.au3"
_examplePS()

Func _ExamplePS()
;~ 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")
    Local $oAssemblyCSharp = _CLR_CompileCSharp(getCode(),"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
            Local $PSResult=$oPS.RunScript("Get-Process")
            consolewrite("***** Start  UNManaged output: *****" & @CRLF)
            consolewrite($PSResult)
            consolewrite("***** End    UNManaged output: *****" & @CRLF)
        EndIf
        Else
        consolewrite("compile error")
    EndIf

EndFunc   ;==>_Example

func getCode()
    $code="//PowershellFromMemory.au3" & @CRLF & _
"using System;" & @CRLF & _
"using System.IO;" & @CRLF & _
"using System.Collections.Generic;" & @CRLF & _
"using System.ComponentModel;" & @CRLF & _
"using System.Data;" & @CRLF & _
"using System.Drawing;" & @CRLF & _
"using System.Text;" & @CRLF & _
"//using System.Windows.Forms;" & @CRLF & _
"using System.Collections.ObjectModel;" & @CRLF & _
"using System.Management.Automation;" & @CRLF & _
"using System.Management.Automation.Runspaces;" & @CRLF & _
""  & @CRLF & _
"namespace myPowerShell { " & @CRLF & _
 ""  & @CRLF & _
"class PowerShellSample " & @CRLF & _
""  & @CRLF & _
"{ " & @CRLF & _
""  & @CRLF & _
"        /// <summary> " & @CRLF & _
"        /// Runs the given powershell script and returns the script output. " & @CRLF & _
"        /// </summary> " & @CRLF & _
"        /// <param name=""scriptText"">the powershell script text to run</param>" & @CRLF & _
"        /// <returns>output of the script</returns>" & @CRLF & _
"        /// RunScript(""Get-Process"")" & @CRLF & _
"        public string RunScript(string scriptText)" & @CRLF & _
"        {" & @CRLF & _
"            // create Powershell runspace" & @CRLF & _
"            Runspace runspace = RunspaceFactory.CreateRunspace();" & @CRLF & _
""  & @CRLF & _
"            // open it" & @CRLF & _
"            runspace.Open();" & @CRLF & _
""  & @CRLF & _
"            // create a pipeline and feed it the script text" & @CRLF & _
"            Pipeline pipeline = runspace.CreatePipeline();" & @CRLF & _
"            pipeline.Commands.AddScript(scriptText);" & @CRLF & _
""  & @CRLF & _
"            // add an extra command to transform the script output objects into nicely formatted strings" & @CRLF & _
"            // remove this line to get the actual objects that the script returns. For example, the script" & @CRLF & _
"            // ""Get-Process"" returns a collection of System.Diagnostics.Process instances." & @CRLF & _
"            pipeline.Commands.Add(""Out-String"");" & @CRLF & _
""  & @CRLF & _
"            // execute the script" & @CRLF & _
"            Collection<PSObject> results = pipeline.Invoke();" & @CRLF & _
""  & @CRLF & _
"            // close the runspace" & @CRLF & _
"            runspace.Close();" & @CRLF & _
""  & @CRLF & _
"            // convert the script result into a single string" & @CRLF & _
"            StringBuilder stringBuilder = new StringBuilder();" & @CRLF & _
"            foreach (PSObject obj in results)" & @CRLF & _
"            {" & @CRLF & _
"                stringBuilder.AppendLine(obj.ToString());" & @CRLF & _
"            }" & @CRLF & _
""  & @CRLF & _
"            Console.WriteLine(""***** Start    Managed output: *****"" );"& @CRLF & _
"            Console.WriteLine(stringBuilder.ToString());"& @CRLF & _
"            Console.WriteLine(""***** Finished Managed output: *****"" );"& @CRLF & _
"            return stringBuilder.ToString();" & @CRLF & _
"        }" & @CRLF & _
"}" & @CRLF & _
"}" & @CRLF

;~ consolewrite($code)
return $code
EndFunc

 

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...