.NET Framework Interop (CLR, C#, VB)
Re: .NET Framework Interop (CLR, C#, VB)
thanks
Last edited by IMEime on 05 Jul 2017, 18:43, edited 1 time in total.
Re: .NET Framework Interop (CLR, C#, VB)
Wow I wish I would have looked into this sooner. I am wondering if I could get some advice? I'm relatively new to C#.
I am trying to create a class to use XPathNavigator. So far what I have works, but I really don't want to go any further until I get some clarification on a few things.
Here is my code so far:
1.) How do I handle XPathNavigator properties like innerXML? Do I just create another method that returns the value?
2.) How should I handle XPathNodeIterator? Should I create another class to handle its methods?
I am trying to create a class to use XPathNavigator. So far what I have works, but I really don't want to go any further until I get some clarification on a few things.
Here is my code so far:
Code: Select all
c# =
(
using System.Xml;
using System.Xml.XPath;
class XPathNav
{
XPathNavigator nav;
XPathDocument docNav;
public void Open(string xmlPath)
{
docNav = new XPathDocument(@xmlPath);
nav = docNav.CreateNavigator();
}
public XPathNavigator sNode(string xpath)
{
return nav.SelectSingleNode(xpath);
}
public XPathNodeIterator sNodes(string xpath)
{
return nav.Select(xpath);
}
public string GetAttrVal(XPathNavigator node, string attrName)
{
return node.GetAttribute(attrName, "");
}
}
)
asm := CLR_CompileC#(c#, "System.xml.dll" )
obj := CLR_CreateObject( asm, "XPathNav" )
obj.Open( "path to xml" )
node := obj.sNode( "//node" )
msgbox % obj.GetAttrVal( node, "name" )
2.) How should I handle XPathNodeIterator? Should I create another class to handle its methods?
Re: .NET Framework Interop (CLR, C#, VB)
If you're wrapping it to make the usage easier for yourself, then the answers are:
1) However you want to handle them.
2) However you want to handle it.
If you're wrapping it just so that you can use it from AutoHotkey, don't. You can use XPathNavigator directly, just as you're using XPathNav, but obviously with different syntax. They're both .NET-based classes. Use CLR_LoadLibrary to load the System.Xml assembly.
1) However you want to handle them.
2) However you want to handle it.
If you're wrapping it just so that you can use it from AutoHotkey, don't. You can use XPathNavigator directly, just as you're using XPathNav, but obviously with different syntax. They're both .NET-based classes. Use CLR_LoadLibrary to load the System.Xml assembly.
Re: .NET Framework Interop (CLR, C#, VB)
I figured as much, but I can't get anything to work loading the assemblies directly.lexikos wrote:They're both .NET-based classes. Use CLR_LoadLibrary to load the System.Xml assembly.
Code: Select all
asm := CLR_LoadLibrary( "System.Xml" )
XPath := CLR_CreateObject( asm, "XPath" )
xPathDoc := new XPath.XPathDocument("path to xml")
xPathNav := xPathDoc.CreateNavigator()
Can't even get MessageBox class to work. What am I doing wrong?
Code: Select all
asm := CLR_LoadLibrary("System.Windows.Forms")
msg := CLR_CreateObject(asm, "MessageBox")
msg.Show("text")
Re: .NET Framework Interop (CLR, C#, VB)
Hi,
I have a dll that was created in .net and want to expose the functions for use in AutoHotkey. I think this thread provides the tools to do that but I need a jump start. Any suggestions? I have a list of all the classes / functions in the dll. I disassembled it with ildasm.exe.
Relayer
I have a dll that was created in .net and want to expose the functions for use in AutoHotkey. I think this thread provides the tools to do that but I need a jump start. Any suggestions? I have a list of all the classes / functions in the dll. I disassembled it with ildasm.exe.
Relayer
Re: .NET Framework Interop (CLR, C#, VB)
I can't begin to fathom how you came up with that code. Why are you trying to instantiate a namespace (XPath) and an abstract class (MessageBox)? CLR_CreateObject creates an object, as in new classname. You'd never use new XPath() or new MessageBox() in C#.dd900 wrote:What am I doing wrong?
CLR.ahk has no equivalent of the using statement, therefore you must always specify the full class name, including namespace.
You can't use AutoHotkey's new operator to instantiate a .NET class. If you could there'd be no need for CLR_CreateObject (just some sensible way to get the namespace).
CLR_CreateObject is just a small wrapper around the various overloads of Assembly.CreateInstance. For instance, CLR_CreateObject(asm, "XPathNav") is the same as asm.CreateInstance_2("XPathNav", true). Method #2 is used so that the name can be case-insensitive (but this overload doesn't accept parameters for the constructor).
To call static methods like MessageBox.Show(), you need to get a reference to a Type object representing the class, then call Type.InvokeMember.
First, load the dll with CLR_LoadLibrary. .NET dlls don't expose functions; they expose classes with static and instance methods. Either instantiate a class with CLR_CreateObject, or call static methods using reflection (starting with the Assembly object returned by CLR_LoadLibrary). Refer to the first post for usage of the CLR_ functions.Relayer wrote:I have a dll that was created in .net and want to expose the functions for use in AutoHotkey.
Re: .NET Framework Interop (CLR, C#, VB)
How do I get a reference to the MessageBox class Type?
I've also tried to create a domain then call domain.Load_2(assemblyName), but the only thing I can get to load is "mscorlib". Anything else just returns a "cannot find" error. Could you maybe provide an example?
Edit: Never mind I got it. Thanks for the Info.
What is 0x158? I understand what the parameter is, but I don't know how you got the value that is being passed.
Code: Select all
asm := CLR_LoadLibrary( "System.Windows.Forms" )
type := asm.GetType() ;type = System.Reflection.Assembly
Edit: Never mind I got it. Thanks for the Info.
Code: Select all
asm := CLR_LoadLibrary( "System.Windows.Forms" )
msg( "I Finally Got It!!!!!" )
msg( "Thanks For the Help..." )
return
msg( text ) {
global asm
args := ComObjArray(0xC, 1), args[0] := text
return asm.GetType_2("System.Windows.Forms.MessageBox").InvokeMember_3("Show", 0x158, ComObject(13,0), ComObject(13,0), args)
}
escape::
exitapp
Re: .NET Framework Interop (CLR, C#, VB)
The second parameter is "A bitmask comprised of one or more BindingFlags". 0x158 is a bitwise combination of BindingFlags. The values aren't shown in the documentation, but you can print them with a bit of C#, or use an IL disassembler like IL Spy.
Re: .NET Framework Interop (CLR, C#, VB)
One more question. How would I get an object reference to System.Windows.Forms.MessageBoxButtons?
Re: .NET Framework Interop (CLR, C#, VB)
You wouldn't. Enumerations are marshalled as integers, hence 0x158 for the second parameter of InvokeMember.
Re: .NET Framework Interop (CLR, C#, VB)
IL Spy says my dll has "com visible" set to FALSE. Does that make it incompatible with the tools shown in this thread?
Relayer
Relayer
-
- Posts: 1
- Joined: 10 Aug 2015, 01:00
Re: .NET Framework Interop (CLR, C#, VB)
Thank you Lexikos! Below is code for sending messages from a Gmail account.
iPhilip
Note: If you get an authentication error, try any of the solutions here.
Edit: Added error checking for email addresses.
iPhilip
Code: Select all
#NoEnv
SetWorkingDir %A_ScriptDir%
UserName = username ; Gmail login name - don't include "@gmail.com" at the end
Password = password ; Gmail login password
From := UserName "@gmail.com" ; Can be different from [email protected] - see https://support.google.com/mail/answer/22370?hl=en
To = [email protected], [email protected], [email protected] ; Separators can be "," , ";" or "|". Spaces before or after the separators are allowed.
Cc = [email protected], [email protected], [email protected] ; Separators can be "," , ";" or "|". Spaces before or after the separators are allowed.
Bcc = [email protected],[email protected],[email protected] ; Separators can be "," , ";" or "|". Spaces before or after the separators are allowed.
Subject = Test email
Body = <body><h2>Heading #2</h2><p>Html Body</p></body>
IsBodyHtml := true
Attachments = file 1.ahk, file 2.txt,file 3.jpg ; Separators can be "," , ";" or "|". Spaces before or after the separators or in the file name are allowed.
Priority = Normal ; Values can be "Low", "Normal", or "High"
global GmailObject := CreateGmailObject() ; This only has to be done once
Gmail(UserName, Password, From, To, Cc, Bcc, Subject, Body, IsBodyHtml, Attachments, Priority)
return
; ---------------------------------------
; Functions
; ---------------------------------------
; This function sends out a message from a Gmail account - only the first four parameters are required
; The return values are as follows:
; 0 if there were no problems
; 1 if problems were found but the message was sent anyway
; 2 if there was an error sending the message
Gmail(UserName
, Password
, From
, To
, Cc := ""
, Bcc := ""
, Subject := ""
, Body := ""
, IsBodyHtml := false
, Attachments := ""
, Priority := "Normal") {
return GmailObject.SendMail(UserName
, Password
, From
, To
, Cc
, Bcc
, Subject
, Body
, IsBodyHtml ? true : false
, Attachments
, Priority)
}
; This function compiles the c# code and returns the Gmail object
CreateGmailObject() {
c# =
(
using System;
using System.Net;
using System.Net.Mail;
using System.Net.Mime;
class Gmail
{
public int SendMail(string UserName,
string Password,
string From,
string To,
string Cc,
string Bcc,
string Subject,
string Body,
bool IsBodyHtml,
string Attachments,
string Priority)
{
int ReturnCode = 0;
string FileName;
string[] Files;
string[] Addresses;
string[] StringSeparators = new string[] {"," , ";" , "|"};
// Setup the SMTP client
SmtpClient smtp = new SmtpClient("smtp.gmail.com",25);
smtp.Credentials = new NetworkCredential(UserName,Password);
smtp.EnableSsl = true;
// Setup the message
MailMessage message = new MailMessage();
// From field
try
{
message.From = new MailAddress(From);
}
catch (Exception exception)
{
Console.WriteLine("Error: Invalid From email address: '" + From + "'.\nExplanation: " + exception.Message);
}
// To field - allows multiple email addresses separated by anyone of the StringSeparators
Addresses = To.Split(StringSeparators,StringSplitOptions.RemoveEmptyEntries);
foreach (string Address in Addresses)
try
{
message.To.Add(Address);
}
catch (Exception exception)
{
Console.WriteLine("Warning: Invalid To email address: '" + Address + "'.\nExplanation: " + exception.Message);
ReturnCode++;
}
if (ReturnCode == Addresses.Length)
Console.WriteLine("Error: At least one To email address must be valid!");
else if (ReturnCode > 1)
ReturnCode = 1;
// Cc field - allows multiple email addresses separated by anyone of the StringSeparators
Addresses = Cc.Split(StringSeparators,StringSplitOptions.RemoveEmptyEntries);
foreach (string Address in Addresses)
try
{
message.CC.Add(Address);
}
catch (Exception exception)
{
Console.WriteLine("Warning: Invalid Cc email address: '" + Address + "'.\nExplanation: " + exception.Message);
ReturnCode = 1;
}
// Bcc field - allows multiple email addresses separated by anyone of the StringSeparators
Addresses = Bcc.Split(StringSeparators,StringSplitOptions.RemoveEmptyEntries);
foreach (string Address in Addresses)
try
{
message.Bcc.Add(Address);
}
catch (Exception exception)
{
Console.WriteLine("Warning: Invalid Bcc email address: '" + Address + "'.\nExplanation: " + exception.Message);
ReturnCode = 1;
}
// Subject field
message.Subject = Subject;
// Body field
message.Body = Body;
if (IsBodyHtml)
message.IsBodyHtml = true;
// Attachments - allows multiple files separated by anyone of the StringSeparators
Files = Attachments.Split(StringSeparators,StringSplitOptions.RemoveEmptyEntries);
foreach (string sFile in Files)
try
{
Attachment attachment = new Attachment(sFile.Trim(),MediaTypeNames.Application.Octet);
message.Attachments.Add(attachment);
}
catch (Exception exception)
{
Console.WriteLine("Warning: " + exception.Message);
ReturnCode = 1;
}
// Priority field
if (Priority == "High")
message.Priority = MailPriority.High;
else if (Priority == "Low")
message.Priority = MailPriority.Low;
// Send the message
try
{
smtp.Send(message);
}
catch (Exception exception)
{
Console.WriteLine("Result: Message was not sent!\nExplanation: " + exception.Message);
ReturnCode = 2;
}
if (ReturnCode == 0)
Console.WriteLine("Result: Message was sent successfully.");
else if (ReturnCode == 1)
Console.WriteLine("Result: Message was sent with warnings.");
// Pass ReturnCode back to the calling function
return ReturnCode;
}
}
)
asm := CLR_CompileC#(c#, "System.dll")
return CLR_CreateObject(asm, "Gmail")
}
Edit: Added error checking for email addresses.
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)
-
- Posts: 2
- Joined: 03 Nov 2015, 22:08
Re: .NET Framework Interop (CLR, C#, VB)
Hiyas,
I am having a problem getting CLR.ahk working with my project/test and was hoping someone can help me get my head out of my ......
my c# dll
My ahk script:
The results on the f12 key is an empty msgbox, but the f11 runs perfectly.
For some reason I am not getting a return value from the test function and I don't have a clue why.
The FSPixel.dll and CLR.ahk are in the same directory as the ahk script. I am using the most up to date U64 version of ahk as well.
Any help would be appreciated.
I am having a problem getting CLR.ahk working with my project/test and was hoping someone can help me get my head out of my ......
my c# dll
Code: Select all
using System;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Media;
namespace FSPixel
{
public class FSPixel
{
public FSPixel()
{
}
public string test()
{
return "test";
}
}
}
My ahk script:
Code: Select all
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn ; Enable warnings to assist with detecting common errors.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
#include CLR.ahk
f12::
CLR_Start()
asm := CLR_LoadLibrary("FSPixel.dll")
obj := CLR_CreateObject(asm, "FSPixel")
s := obj.test()
msgbox %s%
return
f11::
c# =
(
using System.Windows.Forms;
class Foo {
public void Test() {
MessageBox.Show("Hello, world, from C#!");
}
}
)
CLR_Start()
asm := CLR_CompileC#(c#, "System.dll | System.Windows.Forms.dll")
obj := CLR_CreateObject(asm, "Foo")
obj.Test()
return
For some reason I am not getting a return value from the test function and I don't have a clue why.
The FSPixel.dll and CLR.ahk are in the same directory as the ahk script. I am using the most up to date U64 version of ahk as well.
Any help would be appreciated.
Re: .NET Framework Interop (CLR, C#, VB)
As I have not executed your code, the following is based on educated guesswork:
You are not getting a return value from the function that you are not calling. obj.Test() will return nothing if obj is not an object.For some reason I am not getting a return value from the test function and I don't have a clue why.
FSPixel is a namespace, not a class. How about FSPixel.FSPixel?obj := CLR_CreateObject(asm, "FSPixel")
-
- Posts: 2
- Joined: 03 Nov 2015, 22:08
Re: .NET Framework Interop (CLR, C#, VB)
lexikos wrote:FSPixel is a namespace, not a class. How about FSPixel.FSPixel?
Excellent, I knew it was a stupid newb mistake
Thanks..
Re: .NET Framework Interop (CLR, C#, VB)
Hello Lexikos,
I'm trying to get your old XPtable example running: .NET Framework Interop - Scripts and Functions. How must I replace function COM_Invoke which is unkown? Your Simple C# example is working.
Many thanks and greetings
hotkeyguy
I'm trying to get your old XPtable example running: .NET Framework Interop - Scripts and Functions. How must I replace function COM_Invoke which is unkown? Your Simple C# example is working.
Many thanks and greetings
hotkeyguy
Re: .NET Framework Interop (CLR, C#, VB)
The control can do a lot more than just what my example shows. To do anything useful you're going to have to learn the control's own API and how to use .NET objects via CLR.ahk.
For CLR.ahk v1.2, you basically only need to know how to do a few things:
As for translating my old example, it's simple:
For CLR.ahk v1.2, you basically only need to know how to do a few things:
- Load the assembly. This is pretty obvious. Just give CLR_LoadLibrary the assembly name (if it is registered in the Global Assembly Cache) or full path. However, it might fail if not built for the latest .NET version. In that case, you need to call CLR_Start first with the appropriate .NET version. In my case, I just recompiled XPTable since I don't have .NET 1.1 installed.
- Create an object. Just call CLR_CreateObject(asm, typename), where asm is the assembly object and typename is the type name, including namespace. Any additional parameters are passed to the object's constructor. If there are none, you can use the standard Assembly.CreateInstance method instead; i.e. asm.CreateInstance(typename).
- In a few cases you need to specify parameter types. For instance, AutoHotkey has no boolean type so you may need to use ComObject(0xB, false) and ComObject(0xB, -1) instead.
- Call overloaded methods. Unfortunately COM-callable wrappers in .NET don't really support method overloads. Each one is instead explicitly numbered, like CreateInstance, CreateInstance_2, CreateInstance_3. When you call an overloaded method, you need to figure out which number you want. (However, this doesn't apply to calling methods via .NET Reflection.)
As for translating my old example, it's simple:
- COM_Invoke(xx, "yy") is hopefully very obvious. Since xx is now an object, just do xx.yy or xx.yy().
- COM_Invoke(xx, "yy=", ...) means an assignment. Since xx is now an object, just do an assignment: xx.yy := ...
- "+" xx as a COM_Invoke parameter passes the pointer xx as an object. Since xx is now an object and not a pointer, just use xx.
- COM_Invoke_(xx, yy, ...) accepts parameters in pairs, with the COM variant type (a number) followed by the value. You can just wrap each pair of parameters in a single ComObject( ), as in 11, false -> ComObject(11, false). There's no need to specify the type of an object (9 or 13) or string (8); you can pass the value directly (remove the number).
- The old version of CLR_CreateObject had usage similar to COM_Invoke_. Again, you can just remove 8,, but replace 11,true with ComObject(11, -1) (the original code should have used -1).
- COM_Release is no longer needed since we're using object references, not pointers.
Code: Select all
#NoEnv
asm := CLR_LoadLibrary("XPTable.dll")
; Type names must be fully qualified.
table := CLR_CreateObject(asm, "XPTable.Models.Table")
columnModel := CLR_CreateObject(asm, "XPTable.Models.ColumnModel")
tableModel := CLR_CreateObject(asm, "XPTable.Models.TableModel")
table.ColumnModel := columnModel
table.TableModel := tableModel
Columns := columnModel.Columns
Columns.Add(col1:=CLR_CreateObject(asm, "XPTable.Models.TextColumn", "Text"))
Columns.Add(col2:=CLR_CreateObject(asm, "XPTable.Models.CheckBoxColumn", "CheckBox"))
Columns.Add(col3:=CLR_CreateObject(asm, "XPTable.Models.ButtonColumn", "Button"))
Rows := tableModel.Rows
; The C# code:
; tableModel.Rows[0]
; is roughly equivalent to:
; Rows := tableModel.Rows
; Rows0 := Rows.Item(0)
;
; Since we are creating the Row object, we don't need to use the above.
Rows.Add(row:=CLR_CreateObject(asm,"XPTable.Models.Row"))
Cells := row.Cells
Cells.Add(cel1:=CLR_CreateObject(asm, "XPTable.Models.Cell", "Text 1"))
Cells.Add(cel2:=CLR_CreateObject(asm, "XPTable.Models.Cell", "CheckBox 1", ComObject(0xB,-1)))
Cells.Add(cel3:=CLR_CreateObject(asm, "XPTable.Models.Cell", "Button 1", ComObject(0xB,false)))
Rows.Add(row:=CLR_CreateObject(asm, "XPTable.Models.Row"))
Cells := row.Cells
Cells.Add(cel1:=CLR_CreateObject(asm, "XPTable.Models.Cell", "Text 2"))
Cells.Add(cel2:=CLR_CreateObject(asm, "XPTable.Models.Cell", "CheckBox 2", ComObject(0xB,false)))
Cells.Add(cel3:=CLR_CreateObject(asm, "XPTable.Models.Cell", "Button 2"))
; END CODE BASED ON CODEPROJECT SAMPLE
table.Left := 10
table.Top := 10
table.Width := 300
table.Height := 200
hwnd := table.Handle
Gui, +LastFound
DllCall("SetParent","ptr",hwnd,"ptr",WinExist())
Gui, Show, W320 H220, XPTable
return
GuiClose:
ExitApp
Re: .NET Framework Interop (CLR, C#, VB)
Hello Lexikos,
thank you so much for your again superb support! Your updated example is working. For me the .NET Framework Interop is really a new adventure. Thanks again for get me started. Asap some updated links to XPTable will follow.
Greetings
hotkeyguy
thank you so much for your again superb support! Your updated example is working. For me the .NET Framework Interop is really a new adventure. Thanks again for get me started. Asap some updated links to XPTable will follow.
Greetings
hotkeyguy
Return to “Scripts and Functions (v1)”
Who is online
Users browsing this forum: Bruno and 108 guests