.NET Framework Interop (CLR, C#, VB)

Post your working scripts, libraries and tools for AHK v1.1 and older
Ahk New user

Re: .NET Framework Interop (CLR, C#, VB)

13 Oct 2017, 17:24

Hi lexikos

I try to use the library CLR.ahk , but I can not get it work

https://autohotkey.com/boards/viewtopic ... 8&p=172168

Code: Select all

#Include CLR.ahk

asm := CLR_LoadLibrary("DLL_Trazabilidad.dll")
DLL_Trazabilidad := CLR_CreateObject(asm, "DLL_Trazabilidad.DLL_Trazabilidad")
result := DLL_Trazabilidad.Consulta_ICT("123456789")
MsgBox, % result
Result should be 0 and I receive empty string

What should be the problem?

Thanks in advance
egocarib
Posts: 100
Joined: 21 May 2015, 18:21

Re: .NET Framework Interop (CLR, C#, VB)

14 Nov 2017, 20:43

Here is a version that is updated to work with the most recent AHK v2 build.
Spoiler

In addition, I have a question that I hope someone can help with.

I am trying to change the lock screen image in Windows 10. I started a topic here. Please take a look if you might be able to help.
burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: .NET Framework Interop (CLR, C#, VB)

26 May 2018, 15:29

It's been quite awhile since I did anything with this, and what I did was mostly in VB.NET, but check out AutoHotkey.Interop. The examples on the Github page don't show passing objects back and forth, or arrays of strings, but you can definitely pass variables back and forth between AHK and .NET. It seems like it should work for AHK objects.
Hope it helps, and I apologize if I'm telling you something you already know. I would certainly like to see your results, as this seems like a really useful thing to do.
Regards,
burque505
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: .NET Framework Interop (CLR, C#, VB)

26 May 2018, 16:42

@burque505 I am not sure that is quite the same thing.
I have a class instance via CLR, i have some methods on it which I can call quite happily with simple data types (ints, strings), but i cannot seem to call a function with the signature public void MyFunc(string[] arr)

Specifically, I want this for my HotVoice project, which adds Speech Recognition support to AHK via the MS.Speech API
burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: .NET Framework Interop (CLR, C#, VB)

26 May 2018, 16:50

Ahah, I get it now. I haven't ever called anything with CLR except COM objects for the most part and, like you, ints and strings. I've been watching your speech recognition project with interest, really cool.
Regards,
burque505
Edit: This works, but it's only closer, not there yet.

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
;#Include CLR_H.ahk ;This is my customized CLR lib for use with AutoHotkey.dll, just <CLR> is fine.

vb =
(
	Imports System.IO
	Imports System.Runtime
	Imports System.Windows.Forms
    Class Foo
		Dim arr (3) as String
        Public Sub MyFunc()
		arr(0) = "dot"
        arr(1) = "net"
        arr(2) = "perls"
        arr(3) = CStr(2010)
			For Each element As String In arr
            MessageBox.Show(element)
            MessageBox.Show("... ")
        Next
		End Sub
    End Class
)
asm := CLR_CompileVB(vb, "System.dll | System.IO.dll | System.Windows.Forms.dll")
obj := CLR_CreateObject(asm, "Foo")
obj.MyFunc()
I'll keep trying.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: .NET Framework Interop (CLR, C#, VB)

26 May 2018, 18:05

I want something like this:

Code: Select all

c# =
(
    using System.Windows.Forms;
    class Foo {
        public void MyFunc(string[] arr) {
            MessageBox.Show(arr[1]);
        }
    }
)
asm := CLR_CompileC#(c#, "System.dll | System.Windows.Forms.dll")
obj := CLR_CreateObject(asm, "Foo")
arr := ["Notepad", "Word"]		; I want to pass this AHK array of strings into a C# function
obj.MyFunc(arr)
burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: .NET Framework Interop (CLR, C#, VB)

27 May 2018, 12:16

Hi evilC, I've been struggling with this. I hope you have good luck. So far I've run into a brick wall trying to get AutoHotkey.Interop to run in a CLR code block. My though was to use ahkgetvar and ahksetvar from AutoHotkey.dll to pass the array back and forth. But I'm nowhere close yet. As an example, using Linqpad, which is handy for running .Net code without compiling, the following runs fine. I just add a reference to AutoHotkey.Interop.

Code: Select all

Dim Engine As New AutoHotkey.Interop.AutoHotkeyEngine
engine.ExecRaw("#InstallKeybdHook")
engine.ExecRaw("!^g::send Hello World")
engine.ExecRaw("msgbox, Move me out of the way`nI block threads")
The hotkey works, and the msgbox displays.
When I run the same code inside a CLR block in AHK, like this:
Spoiler
I get the following errors :headwall: :
Spoiler
So at this point, I don't even know if it's possible to use AutoHotkey.Interop in conjunction with CLR. EDIT: :oops: I recompiled AutoHotkey.Interop to make it COM accessible, but it didn't help. Regsvr32 still doesn't work on it (no entry point) (Looks like I'm mixing up COM and Interop.) Any clues?
Best regards,
burque505
sancarn
Posts: 224
Joined: 01 Mar 2016, 14:52

Re: .NET Framework Interop (CLR, C#, VB)

28 May 2018, 10:35

@evilC
I actually think it is not possible with how AHK currently sends variables to .NET (or with how .NET casts these to object). I made the following code:
Spoiler

I GOT IT TO WORK!

Code: Select all

include CLR.ahk
C# =
(
    using System;                                  //For Types
    using System.Windows.Forms;                    //For MessageBox
    using System.Reflection;                       //For BindingFlags
    class Foo {
        public void invokeLength(object obj){
            MessageBox.Show(Convert.ToString(obj.GetType().InvokeMember("Length",BindingFlags.InvokeMethod,null,obj,null)));
        }
        public void invokePop(object obj){
            MessageBox.Show(Convert.ToString(obj.GetType().InvokeMember("Pop",BindingFlags.InvokeMethod,null,obj,null)));
        }
        public void removeAt(object obj,int position){
            Object[] args = new Object[1];
            args[0] = position;
            MessageBox.Show(Convert.ToString(obj.GetType().InvokeMember("RemoveAt",BindingFlags.InvokeMethod,null,obj,args)));
        }
    }
)
asm := CLR_CompileC#(c#,"System.dll|System.Windows.Forms.dll|Microsoft.VisualBasic.dll")
obj := CLR_CreateObject(asm,"Foo")
arr := ["Notepad","Word"]
obj.invokeLength(arr)
;obj.invokePop(arr)
obj.removeAt(arr,1)
So we are now able to get the length of the array and also remove (and get) elements of the array. It's probably easy enough to just remove and insert elements into the array (all these methods are the same as seen in AHK). I still don't have any idea how to get an element of the array, but this at least gives you a method of doing it indirectly...
Last edited by sancarn on 28 May 2018, 11:15, edited 1 time in total.
burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: .NET Framework Interop (CLR, C#, VB)

28 May 2018, 11:22

sancarn, I just ran your last example on AHK_L 1.1.28.02 both 64 and 32 bit. It failed, but not silently :).
The code reached the "Before" msgbox, and then AHK crashes, both 64 and 32 bit.

Code: Select all

Unhandled Exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
   at Foo.IDispatch.Invoke(Int32 dispId, Guid& riid, Int32 lcid, INVOKEKIND wFlags, DISPPARAMS& pDispParams, Object[] result, IntPtr pExcepInfo, IntPtr puArgErr)
   at Foo.getLength(Object obj) in c:\Users\burque505\AppData\Local\Temp\x0rakik0.0.cs:line 23
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
Regards, burque505

EDIT: sancarn, I just ran your new code (things happen fast around here) :). That is really great! Thank you!
Here's your working example ported to vb:
Spoiler
EDIT: Regarding finding supported interfaces, if you change the last line to:

Code: Select all

obj.findSupportedInterfaces(arr.ToString)

you also get ICloneable, _String, IEnumerable, _Object, IConvertible, before (at least for me) it crashes with an error:
Spoiler
sancarn
Posts: 224
Joined: 01 Mar 2016, 14:52

Re: .NET Framework Interop (CLR, C#, VB)

28 May 2018, 12:13

Even more so, I figured out how to get members and get length etc. In a really nice way. Requires a few extra references but it is totally worth it:

Code: Select all

#include CLR.ahk
C# =
(
    using System;                                  //For Types
    using System.Windows.Forms;                    //For MessageBox
    using System.Reflection;                       //For BindingFlags
    using System.Dynamic;
    class Foo {
        public void getAt(dynamic obj, int position){
            MessageBox.Show(obj(position));
        }
    }
)
asm := CLR_CompileC#(c#,"System.dll|System.Windows.Forms.dll|System.Core.dll|Microsoft.CSharp.dll")
obj := CLR_CreateObject(asm,"Foo")
arr := ["Notepad","Word"]
obj.getAt(arr,1)
Ultimately if you define obj as dynamic then the array works exactly like it does in AHK: obj(1) will return the first element and obj.length() will return the object length, etc.
burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: .NET Framework Interop (CLR, C#, VB)

28 May 2018, 15:55

Very nice indeed, sancarn.
Here it is in vb:

Code: Select all

#include CLR.ahk
vb =
(
    Imports System                                  'For Types
    Imports System.Windows.Forms                    'For MessageBox
    Imports System.Reflection                       'For BindingFlags
    Imports System.Dynamic
    class Foo
    Dim obj As Object
    Dim position as Integer
    Public Sub getAt(ByVal obj As Object, ByVal position As Integer)
        MessageBox.Show(obj(position))
    End Sub
    End Class
)
asm := CLR_CompileVB(vb,"System.dll|System.Windows.Forms.dll|System.Core.dll|Microsoft.VisualBasic.dll")
obj := CLR_CreateObject(asm,"Foo")
arr := ["Notepad","Word"]
obj.getAt(arr,1)
obj.getAt(arr,2)
Regards,
burque505
burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: .NET Framework Interop (CLR, C#, VB)

29 May 2018, 11:50

A variation on sancarn's great work above (in VB, however). This shows that variables can be passed back to the CLR routine as well as received from it.
I've use a list of key-value pairs instead of a dictionary object, primarily because that's all the farther I've gotten.
Spoiler
Thanks again, sancarn!
Regards, burque505

EDIT: evilC, I think this is more or less your original request?
Spoiler
And here in VB, which I'm more comfortable with, probably in no small part because it's more forgiving of bonehead errors:
Spoiler
lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: .NET Framework Interop (CLR, C#, VB)

01 Jun 2018, 01:52

Strongly typed arrays are marshaled as SafeArrays. To find how the base type of the array is marshaled, see the Microsoft documentation. For string it's as BSTR.

ComObjArray(8, count) creates a SafeArray of strings.

https://docs.microsoft.com/en-us/dotnet ... g-behavior
https://docs.microsoft.com/en-us/dotnet ... for-arrays
freakkk
Posts: 25
Joined: 21 Sep 2014, 20:14

Re: .NET Framework Interop (CLR, C#, VB)

07 Jun 2018, 23:13

lexikos, thank you for this! It's excellent! I know I'm late to the game with using this, but I just finally had a place where I needed to use this functionality for the first time at work, and it worked flawlessly.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: .NET Framework Interop (CLR, C#, VB)

10 Jun 2018, 11:32

Thanks ever so much Lex, a lot of it went over my head, but I managed to understand enough to figure out what I want to do.

@burque - seems like it's just as simple to pass arrays of arbitrary classes:

Code: Select all

#SingleInstance, force

#include <CLR>

c# =
(
    using System.Windows.Forms;
    class Foo {
        public void MyFunc(Bar[] arr) {
            MessageBox.Show(arr[1].Name);
        }
		
		public Bar MyFactory(string name){
			return new Bar { Name = name };
		}
    }
	
	public class Bar {
		public string Name { get; set; }
	}
)
asm := CLR_CompileC#(c#, "System.dll | System.Windows.Forms.dll")
obj := CLR_CreateObject(asm, "Foo")
arr := ComObjArray(0xD , 2)

b1 := obj.MyFactory("One")
b2 := obj.MyFactory("Two")
arr[0] := b1
arr[1] := b2

obj.MyFunc(arr)
burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: .NET Framework Interop (CLR, C#, VB)

10 Jun 2018, 12:17

Nice, evilC :)
VB version:
Spoiler
User avatar
TLM
Posts: 1608
Joined: 01 Oct 2013, 07:52
Contact:

Re: .NET Framework Interop (CLR, C#, VB)

25 Jun 2018, 21:20

On a different box here w/o VS and have no idea how to set the default version of .Net framework.
Any ideas? ( I'm sure I knew how to do this before sighhh )..

edit: wait I just noticed CLR_Start() again, testing...
edit:
ok that worked Environment.Version.ToString() is returning the specified runtime crisis averted..
although it would be nice to only have to set it once :think:
*note to self* -- setting CLR_Start() does magically cache the version :lol:
tmplinshi
Posts: 1604
Joined: 01 Oct 2013, 14:57

Re: .NET Framework Interop (CLR, C#, VB)

02 Jul 2018, 08:06

Can someone tell me why this doesn't work?

Code: Select all

testHtml := "<html><body class=""gumbo"">Boo!</body></html>"

GumboBindings := Clr_LoadLibrary("Gumbo.Bindings.dll")
gumbo := Clr_CreateObject(GumboBindings, "Gumbo.Wrappers.GumboWrapper", testHtml)

MsgBox, % gumbo.ToXDocument()
https://github.com/rgripper/GumboBindings

Screenshot of JetBrains dotPeek

Download: gumbo.bindings test.zip (Include Gumbo.Bindings.dll and gumbo.dll)

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: Auntiejack56 and 125 guests