Jump to content

Using .NET libary with AutoIt, possible?


Recommended Posts

Hello. I think is better tu use this path C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0\System.Management.Automation.dll

 

 

Saludos

Link to comment
Share on other sites

Link to comment
Share on other sites

Both ways tested but always a error ?

 

_Run_PSHost_Script()

Func _Run_PSHost_Script()

;~  Local $oAssembly = _CLR_LoadLibrary("C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0\System.Management.Automation.dll")
;~     ConsoleWrite("!$oAssembly: " & IsObj($oAssembly) & @CRLF)


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

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

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


EndFunc
Quote

!$oAssembly: 1
$pAssemblyType = 0x00000000
test Automation.Runspaces.au3 (19) : ==> COM Error intercepted !
    err.number is:         0x00000003
    err.windescription:    NULL Pointer assignment
    err.description is:     
    err.source is:         
    err.helpfile is:     
    err.helpcontext is:     
    err.lastdllerror is:     0
    err.scriptline is:     19
    err.retcode is:     0x00000000

 

Link to comment
Share on other sites

Inside _CLR_CompileAssembly check the safearray of the references and replace StringStripWS($aReferences[$i], 8) with StringStripWS($aReferences[$i], 7)

 

 

Saludos

Link to comment
Share on other sites

Hi Danyfirex,

Can you explain a bit ? Was this an error that was there all the time ?

Inside _CLR_CompileAssembly check the safearray of the references and replace StringStripWS($aReferences[$i], 8) with StringStripWS($aReferences[$i], 7)

@Junkew, if you can get the .NET approach further investigated an running that would really be a huge improvement too ...

 

OK got it going it should have been :

"System.Management.Automation.Runspaces.Runspace"

#include "CLR.au3"

_Run_PSHost_Script()

Func _Run_PSHost_Script()

;~  Local $oAssembly = _CLR_LoadLibrary("C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0\System.Management.Automation.dll")
;~     ConsoleWrite("!$oAssembly: " & IsObj($oAssembly) & @CRLF)


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

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

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


EndFunc

One so now we need to get an example to create a PS script from this object.... 

But I am going to do it later... too many hours behind a PC anyhow 

 

Link to comment
Share on other sites

The Issue only exist when we add a Path that contain spaces so  when we use StringStripWS($aReferences[$i], 8) we strip all spaces (over-rides all other) So correct is use flag 3 ($STR_STRIPLEADING (1) = strip leading white space + $STR_STRIPTRAILING (2) = strip trailing white space)

 

Saludos
  

Link to comment
Share on other sites

Junkew,

Been reading a bit more about PS Runspaces : 

Quote

Host applications can use the default runspace that is provided by Windows PowerShell, which includes all available core commands, or create a custom runspace that includes only a subset of the available commands. To create a customized runspace

You are right Runspace serve a different purpose...

1. Only needed to increase performance (when using a subset of commands)

2. Only needed to execute command on a remote host

Nevertheless relating to the output there IS a difference is seems.

Quote

If you're interested in the nitty gritty details of what is different, go grab dotPeek and crack open the System.Management.Automation.dll and peruse it. One difference is that PowerShell.Invoke() has to determine the type of runspace in use in order to determine which type of pipeline to create e.g. LocalPipeline or RemotePipeline. When you use a Runspace, you actually create a derived class (LocalRunspace or RemoteRunspace) and each one of those will create the appropriate type of pipeline.

The pipeline command and according runspace determine where the out goes to ...

So leaves us still wondering why we can't grab the output .... needs more investigation ....

Keeps us bussy :D

Link to comment
Share on other sites

Good  finding @junkew

Signature beginning:
Please remember: "AutoIt"..... *  Wondering who uses AutoIt and what it can be used for ? * Forum Rules *
ADO.au3 UDF * POP3.au3 UDF * XML.au3 UDF * IE on Windows 11 * How to ask ChatGPT for AutoIt Codefor other useful stuff click the following button:

Spoiler

Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind. 

My contribution (my own projects): * Debenu Quick PDF Library - UDF * Debenu PDF Viewer SDK - UDF * Acrobat Reader - ActiveX Viewer * UDF for PDFCreator v1.x.x * XZip - UDF * AppCompatFlags UDF * CrowdinAPI UDF * _WinMergeCompare2Files() * _JavaExceptionAdd() * _IsBeta() * Writing DPI Awareness App - workaround * _AutoIt_RequiredVersion() * Chilkatsoft.au3 UDF * TeamViewer.au3 UDF * JavaManagement UDF * VIES over SOAP * WinSCP UDF * GHAPI UDF - modest begining - comunication with GitHub REST APIErrorLog.au3 UDF - A logging Library * Include Dependency Tree (Tool for analyzing script relations) * Show_Macro_Values.au3 *

 

My contribution to others projects or UDF based on  others projects: * _sql.au3 UDF  * POP3.au3 UDF *  RTF Printer - UDF * XML.au3 UDF * ADO.au3 UDF SMTP Mailer UDF * Dual Monitor resolution detection * * 2GUI on Dual Monitor System * _SciLexer.au3 UDF * SciTE - Lexer for console pane

Useful links: * Forum Rules * Forum etiquette *  Forum Information and FAQs * How to post code on the forum * AutoIt Online Documentation * AutoIt Online Beta Documentation * SciTE4AutoIt3 getting started * Convert text blocks to AutoIt code * Games made in Autoit * Programming related sites * Polish AutoIt Tutorial * DllCall Code Generator * 

Wiki: Expand your knowledge - AutoIt Wiki * Collection of User Defined Functions * How to use HelpFile * Good coding practices in AutoIt * 

OpenOffice/LibreOffice/XLS Related: WriterDemo.au3 * XLS/MDB from scratch with ADOX

IE Related:  * How to use IE.au3  UDF with  AutoIt v3.3.14.x * Why isn't Autoit able to click a Javascript Dialog? * Clicking javascript button with no ID * IE document >> save as MHT file * IETab Switcher (by LarsJ ) * HTML Entities * _IEquerySelectorAll() (by uncommon) * IE in TaskSchedulerIE Embedded Control Versioning (use IE9+ and HTML5 in a GUI) * PDF Related:How to get reference to PDF object embeded in IE * IE on Windows 11

I encourage you to read: * Global Vars * Best Coding Practices * Please explain code used in Help file for several File functions * OOP-like approach in AutoIt * UDF-Spec Questions *  EXAMPLE: How To Catch ConsoleWrite() output to a file or to CMD *

I also encourage you to check awesome @trancexx code:  * Create COM objects from modules without any demand on user to register anything. * Another COM object registering stuffOnHungApp handlerAvoid "AutoIt Error" message box in unknown errors  * HTML editor

winhttp.au3 related : * https://www.autoitscript.com/forum/topic/206771-winhttpau3-download-problem-youre-speaking-plain-http-to-an-ssl-enabled-server-port/

"Homo sum; humani nil a me alienum puto" - Publius Terentius Afer
"Program are meant to be read by humans and only incidentally for computers and execute" - Donald Knuth, "The Art of Computer Programming"
:naughty:  :ranting:, be  :) and       \\//_.

Anticipating Errors :  "Any program that accepts data from a user must include code to validate that data before sending it to the data store. You cannot rely on the data store, ...., or even your programming language to notify you of problems. You must check every byte entered by your users, making sure that data is the correct type for its field and that required fields are not empty."

Signature last update: 2023-04-24

Link to comment
Share on other sites

Interesting example for Dynamic in C# (in this case it uses a com dll from HP UFT)

static void Main(string[] args)
{
    Type ORType = Type.GetTypeFromProgID("Mercury.ObjectRepositoryUtil"); 
    dynamic ORUtil = Activator.CreateInstance(ORType);

    string ORFilePath = @"D:\TAF\Size.tsr";
    //ObjectRepositoryUtil ORUtil = new ObjectRepositoryUtil();
    ORUtil.Load(ORFilePath);
    var ChildObjects = ORUtil.GetChildren();
    for (int i = 0; i < ChildObjects.Count(); i++ )
    {
        var ChildObject = ChildObjects.Item(i);
        string title = ChildObject.GetTOProperty("micclass") + "(\"" + ORUtil.GetLogicalName(ChildObject) + "\")";
        Console.WriteLine(title);
    }
}

Put on my list to experiment with (offcourse for COM registered objects that does not make sense)

getSomeDynamicObject(ProgID)
 {  Type objectType = Type.GetTypeFromProgID(ProgID); 
    dynamic dynamicObject = Activator.CreateInstance(objectType);
    return dynamicObject;
 }

 

Link to comment
Share on other sites

  • 2 weeks later...

Hi All,

Seems to really quite on the other side of the moon :) !

What has happened on my side so far....

I have been testing the Powershell Host script to get everything running natively without the need of any intermediate C# scripts....

So using the ASYNC approach (see previous posts) .

( Read here for what the difference is between the Invoke and BeginInvoke approach : https://www.codeproject.com/Articles/10311/What-s-up-with-BeginInvoke )

Running any PS1 script give me the correct behavior so far (as if I was running it in the PowerShell ISE)

Even the Complex GUI Forms with many Controls (Graphs,etc), including Events ,are working really fine :D

So basically for me the PS host seems the way to go because :

- You can access .Net classes using PS too, not limitations so far

- Default Output to File, Printer, Clipboard, Grid

- You don't need to worry about Type Conversions between .NET CLR and AU3

- It is really Multithreading !

- You can re-use the scripts in PowerShell, a time saver...

To do :

- Find a proper way to redirect output to stdout !

  Because of .Net being multi threaded which is fantastic.

  We run into the issue that the stdout is happening in an other thread, and SciTE does not get the info back.... ?

- Testing of loading PS modules 

- Testing of remote PS scripting

- Testing of Office365 / Azure scripting

 

 

Link to comment
Share on other sites

  • Working a little more on UIA stuff building a new spy ;-)  
    Doing a lot of stuff with HP tooling (UFT/QTP) last few weeks.
  • Reading still also a lot on .NET
    so this thread is not forgotten but just some other priorities.
  • Regarding your stdOut issue
    we should look with process explorer tools to see if the AutoIt host is parent of the .NET process.
    If it is it should also inherit the stdOut in a normal way.
    maybe add somewhere the createinstance "system.console"
    This C# stuff you should call from AutoIt then (last snippet is best example I found around)
Console.SetOut(null);
var consoleOut = new StringWriter();
Console.SetOut(consoleOut);
var stdOutput = new StreamWriter(Console.OpenStandardOutput());
stdOutput.AutoFlush = true;
Console.SetOut(stdOutput);

 

Link to comment
Share on other sites

Sure this will the final workaround... if we don't find any other structural solution.

The problem is that the PS Host creates a new process thread.

And since au3 is not multi threaded capable I guess we will never be able to capture the stdout ?

If I find a solution I will give an update... But I guess we will need some C# code in the middle to handle all of this...

One more reason for this is that in most cases PS returns a PSObject Type.  Which is an unknown type for au3 ...

To get access to the data in the PSObject we need a convector, that only exists in .NET code ..

Link to comment
Share on other sites

1. Exercise ;-):D

But if I read that then I think this should be possible

  • Compile an (inline) source  to an inline assembly and then instantiate the COM object
    source should then have dispid(1), ....
  • with ObjEvent ( $ObjectVar, "functionprefix" [, "interface name"] )
  • to reach an AutoIt function

     

2.  Working lately a lot with HP UFT and dotnetfactory object

  • This states explicitly
    You can use this object to access the static methods and properties of a class that does not have an instance constructor
  • This suggest that for certain things to be reached when hosting CLR we need a (small) wrapper in managed code to reach all the power
Edited by junkew
Link to comment
Share on other sites

Looks interesting indeed ... Can confirm that it very hard to get access to the full of .NET from unmanaged host.

That's why I stopped spending time on this, and went 1 level higher on the ladder... using the PowerShell host that takes care of all of this in the background.

Up till now I can confirm that it can handle almost anything including events and much more...

 

Link to comment
Share on other sites

Trying to get this to work but get all kinds of COM errors (from AutoIt) when trying to make it dynamic
As posted below the base works but when I start commenting out in C# in favor of the other line I get all kinds of COM errors which I cannot fully explain.

@Danyfirexmaybe you can explain better on what is happening (I assume it has to do with ComVisible not set when you inherit from another object so I have to set that or start working with the unwrap)

//  class DynamicDictionary : DynamicObject
    class DynamicDictionary 
    
//      public override bool TryGetMember(
        public bool TryGetMember(
        
//      public override bool TrySetMember(
        public bool TrySetMember(

 

#include ".\Includes\CLR.au3"
;~ #include "C:\ISN AutoIt Studio\Projects\CLR\CLRv3a\Includes\CLR.au3"
_Example()

Func _Example()
    local $strSource=FileRead("dynamicObject.cs")
;~  consolewrite($strSource)
    
    Local $oAssemblyCSharp = _CLR_CompileCSharp($strSource,"System.dll|System.Core.dll|System.Dynamic.Dll")
    If IsObj($oAssemblyCSharp ) Then
        ConsoleWrite("$oAssembly: " & IsObj($oAssemblyCSharp) & @CRLF)
        Local $oPerson = _CLR_CreateObject($oAssemblyCSharp, "myDynamicSpace.DynamicDictionary")
        
;~      Local $oHandle = ObjCreateInterface( $oPerson, $sIID_IObjectHandle, $sTag_IObjectHandle )
;~      ConsoleWrite( @CRLF & "IsObj( $oHandle ) = " & IsObj( $oHandle ) & @CRLF )
;~      $oHandle.Unwrap($oPerson)
;~      ConsoleWrite( @CRLF & "IsObj( $oPerson ) = " & IsObj( $oPerson ) & @CRLF )

        If IsObj($oPerson) Then
            consolewrite("Object is created "  & @CRLF)
                        
            consolewrite("getDummyText "  & $oPerson.getDummyText("any text") & @CRLF)
            consolewrite("Count "  & $oPerson.Count() & @CRLF)
        EndIf
        
;~      $oPerson.Name="Elwin"
;~      $oPerson.Firstname="Wildschut"
        
;~      Local $oCar = _CLR_CreateObject($oAssemblyCSharp, "myDynamicSpace.DynamicDictionary")
;~      
;~      $oCar.Color="Red"
;~      $oCar.Model="Hummer"
;~      
;~      ConsoleWrite("Person: " & $oPerson.Name & $oPerson.First & @CRLF)
;~      ConsoleWrite("Car: " & $oCar.Model & $oCar.Color & @CRLF)
    Else
        consolewrite("compile error")
    EndIf

EndFunc   ;==>_Example

And C# code

using System;
using System.Text;
using System.Collections.Generic;
using System.Dynamic;

        // The class derived from DynamicObject.
namespace myDynamicSpace {
//  class DynamicDictionary : DynamicObject
    class DynamicDictionary 
    {
        // The inner dictionary.
        Dictionary<string, object> dictionary = new Dictionary<string, object>();

        // This property returns the number of elements
        // in the inner dictionary.
        public int Count
        {
            get
            {
                return dictionary.Count;
            }
        }

        public string getDummyText(string strWhatever)
        {
             // convert the script result into a single string
            StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.AppendLine("Dynamic dummy text");
            
            // return "Dynamic dummy text";
            return stringBuilder.ToString();

        }
        
        // If you try to get a value of a property 
        // not defined in the class, this method is called.
//      public override bool TryGetMember(
        public bool TryGetMember(
            GetMemberBinder binder, out object result)
        {
            // Converting the property name to lowercase
            // so that property names become case-insensitive.
            string name = binder.Name.ToLower();

            // If the property name is found in a dictionary,
            // set the result parameter to the property value and return true.
            // Otherwise, return false.
            return dictionary.TryGetValue(name, out result);
        }

        // If you try to set a value of a property that is
        // not defined in the class, this method is called.
//      public override bool TrySetMember(
        public bool TrySetMember(
            SetMemberBinder binder, object value)
        {
            // Converting the property name to lowercase
            // so that property names become case-insensitive.
            dictionary[binder.Name.ToLower()] = value;

            // You can always add a value to a dictionary,
            // so this method always returns true.
            return true;
        }
    }
}
Edited by junkew
Link with probably the answer
Link to comment
Share on other sites

Another nice (and powerfull) working example giving the start for some dot syntax and making use of the iCustomQueryInterface introduced in V4 of .NET

script.au3

#include ".\Includes\CLR.au3"

_Example()

Func _Example()
    Local $oAssDynamic = _CLR_LoadLibrary("System.Dynamic")
    local $strSource=FileRead("echo.cs")
;~  consolewrite($strSource)
    
    Local $oAssemblyCSharp = _CLR_CompileCSharp($strSource,"System.dll|System.Core.dll|System.Dynamic.Dll|Microsoft.CSharp.dll")
    If IsObj($oAssemblyCSharp ) Then
        ConsoleWrite("$oAssembly: " & IsObj($oAssemblyCSharp) & @CRLF)
        local $oecho = _CLR_CreateObject($oAssemblyCSharp, "EchoProject.Echo")
        if isobj($oecho) Then
            consolewrite("echo object created " & @CRLF)
;~          var echo = new ActiveXObject("EchoProject.Echo");
            consolewrite("Begin debug" & @CRLF);
            consolewrite($oecho.foo() & @CRLF); //foo
            consolewrite($oecho.bar & @CRLF); //bar
            $oecho.baz = "value"; 
            consolewrite($oecho.baz & @CRLF); 
            
        EndIf
 end function

echo.cs

 

//https://stackoverflow.com/questions/26459041/implement-idispatch-with-using-icustomqueryinterface-set-property-not-working
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

namespace EchoProject
{
    [ComImport]
    [Guid("00020400-0000-0000-C000-000000000046")]
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IDispatch
    {
        void GetTypeInfoCount(out uint pctinfo);

        void GetTypeInfo(uint iTInfo, int lcid, out IntPtr info);

        void GetIDsOfNames(
            ref Guid iid,
            [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 2)]
                string[] names,
            int cNames,
            int lcid,
            [Out][MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I4, SizeParamIndex = 2)]
                int[] rgDispId);

		[PreserveSig]
		void Invoke(
            int dispId,
            ref Guid riid,
            int lcid,
            System.Runtime.InteropServices.ComTypes.INVOKEKIND wFlags,
            ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pDispParams,
            [Out, MarshalAs(UnmanagedType.LPArray)] object[] result,
            IntPtr pExcepInfo,
            IntPtr puArgErr);
    }

    [ComVisible(true)]
    [ProgId("EchoProject.Echo")]
    [ClassInterface(ClassInterfaceType.None)]
    public class Echo : IDispatch, ICustomQueryInterface
    {
        private int lastDispId = 0;
        private Dictionary<int, string> dispIdNameMap = new Dictionary<int, string>();

        public CustomQueryInterfaceResult GetInterface(ref Guid iid, out IntPtr ppv)
        {
            ppv = IntPtr.Zero;
            if (typeof(IDispatch).GUID == iid)
            {
                ppv = Marshal.GetComInterfaceForObject(this, typeof(IDispatch), CustomQueryInterfaceMode.Ignore);
                return CustomQueryInterfaceResult.Handled;
            }
            return CustomQueryInterfaceResult.NotHandled;
        }

        public void GetTypeInfoCount(out uint pctinfo)
        {
            pctinfo = 0;
        }

        public void GetTypeInfo(uint iTInfo, int lcid, out IntPtr info)
        {
            info = IntPtr.Zero;
        }

        public void GetIDsOfNames(ref Guid iid, string[] names, int cNames, int lcid, int[] rgDispId)
        {
            for (int i = 0; i < cNames; i++ )
            {
                KeyValuePair<int, string> pair = dispIdNameMap.SingleOrDefault(p => p.Value == names[i]);
                if (pair.Key == 0)
                {
                    dispIdNameMap.Add(++lastDispId, names[i]);
                    rgDispId[i] = lastDispId;
                }
                else
                {
					//Assign the value 
					rgDispId[i] = pair.Key;
                }
            }
        }

		public void Invoke(int dispId, ref Guid riid, int lcid, System.Runtime.InteropServices.ComTypes.INVOKEKIND wFlags, ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pDispParams, [Out, MarshalAs(UnmanagedType.LPArray)] object[] result, IntPtr pExcepInfo, IntPtr puArgErr)
		{
			string name;
			bool retVal=dispIdNameMap.TryGetValue(dispId, out name);
			//if (result != null)
			//Console.WriteLine(dispId);
			//Console.WriteLine(name);
			if (retVal != false)
			{
				result[0] = name;
			}
		}
    }


}

 

Edited by junkew
Link to comment
Share on other sites

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