Updated code: In this script, a splashscreen is introduced to display at the beginning of the script for a couple of seconds. A progress bar is placed on top of it, displaying Starting... . Also, the splashscreen is accompanied by a short music clip.
The splashcreen image is saved in folder images as image1.gif and the sound wav file under folder wav as wav1.wav . FileInstall though copies them to C#AHK directory under the Windows temp directory.
A clarification, besides XML, Autohotkey exchange arrays up to 8 dimensions. Of course XML can be seen as an advanced multidimensional array and there are no limits for exchanging that between Autohotkey and C#.
Quote:
Updated code: Minor corrections. Now Autohotkey escapes commas from XML when it uses it with xpath.ahk, COM does not display error messages, by setting ComObjError(false), if C# .NET encounters errors (only C# can do that now, if it is programmed to do so in case of error) and DLLs files and the C# compiled from Autohotkey executable are all stored in $temp$/C#AHK where %temp% is the Windows temp directory. This is done to prevent the case where other applications may store Dlls or executable file like the one used by the application, with the same name as the Dlls/executable this application uses that may be of different version or hold different content and thus will cause problems to the application.
Quote:
This is an updated script. It again introduces MyFunctions.dll and also includes xpath.ahk for XML processing. XML is useful so that you can send it to C# to do further processing and then send it back to Autohotkey for using it with its GUI or process it with the unique capabilities of AutohotKey. The Autohotkey script also populates a List View with the array of values returned from C# to Autohotkey. There are some minor additions too (and OnExit event and a hotkey maping from ^!c to ^!s).
Also, now the included ahk scripts (CRL.ahk and xpath.ahk) will have to be stored in ahk_files and Dll files are stored bin. Both directories reside where you main ahk script resides.
Quote:
Updated code: This version is the same as the previous one, but it does not use the .NET Dll MyFuncions.dll . It only uses the .NET Dll Mysql.Data.dll . The reason I made this new post is for people that want to run the script, to only have to download Mysql.Data.dll for .NET from mysql.com to do that. Just save MySql.Data.dll in the folder where you will place the autohot script with the code in the post. Also, in the same folder place COM.ahk (the COM_L version for Autohotkey_L ). Remember to also install Autohotkey_L , .NET framework and MySQL and run its server (create in there a database called solontesting and make a table called shares and put data in it, also check that you have a user called root that has no password and that has rights on the database solontesting) to your PC if you haven't already do that. Once you are done, right click on the autohotscript you made, and select Run Script.
Also, to make things clearer, I shoud mention that the script compiles two C# code blocks, once in file (that is necessary if you want to include third party .NET Dlls in your application) and once in memory.
Quote:
Updated code: Now you can compile the file to .exe and the compiled file will be completetly standalone. It will have in it, the third party DLLs it loads (MySql.Data.dll and MyFunctions.dll) installed in it, the .exe file. Of course it will also have in it, the CRL.ahk file too.
Note that you should have SetWorkingDir %A_Temp%
and when you load the DLLs you should also tell the autohotkey script to find them at %A_Temp%\MySql.Data.dll and %A_Temp%\MyFunctions.dll
. You should also set the CLR_StartDomain to have the AppDomain base path to A_TEMP and finally you should tell CLR_CompileC# to compile C# to exe in the path %A_Temp% .
Quote:
Updated code: Now compiled C# is loaded only once and the compiled C# classes/instances keep state.
Quote:
This is for Autohotkey_L
It demonstrates how to write Autohotkey and C# code in an Autohotkey script and connect and query MySql.
The script includes CRL.ahk (CRL_L version, for Autohotkey_L) and also requires Mysql driver for .NET. It also uses another dll called MyFunctions.dll (referenced with using MathFunctions; in C#). The two dll files are simply put in the directory where you will store this script and CRL.ahk. You need to have .NET framework installed in your PC too.
Code:
;The #Directives must come before OnExit to work, #include seems to be an exception
#SingleInstance force
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
;SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
SetWorkingDir %A_Temp% ; This is the temp directory of Windows, this is where the Autohotkey application will look for the DLLs it will load and the exe compiled
;inline C# code.
#include ahk_files\CLR.ahk ; For using C# .NET from autohotkey
#Include ahk_files\xpath.ahk ; For XML
; OnExit must enclose all autoexecute code in order for OnExit to call the ExitSub if the user exits tha application
; OnExit Display pop up for confirmation
OnExit, ExitSub
; the following two files are the dlls that C# inline code uses and they will be included in the exe if the ahk script is compiled to exe.
; Note that you still need to have the two DLL files in the original DIR where your uncompiled script is, so that FileInstall will copy them to temp directory
; The compiled exe AutohotKey file will be standalone, without needing to have any files to run.
FileCreateDir, %A_Temp%\C#AHK ; Create directory to hold the Dlls and the C# compiled file, not directly in the Windows temp directory, because other programs may store the Dlls with the same names or different versions, that could cause issues to the application
FileInstall, bin\MySql.Data.dll, %A_Temp%\C#AHK\MySql.Data.dll,1
FileInstall, bin\MyFunctions.dll, %A_Temp%\C#AHK\MyFunctions.dll,1
; Wav files
FileInstall, wav\wav1.wav, %A_Temp%\C#AHK\wav1.wav,1
; Images
FileInstall, images\image1.gif, %A_Temp%\C#AHK\image1.gif
SplashImage, %A_Temp%\C#AHK\image1.gif, b fs18, AHK + C# Demo and more ;Always on top, but Yes/No exit dialog box if called will be on top of the splashscreen.
SoundPlay, %A_Temp%\C#AHK\wav1.wav
Loop, 300
{
Progress, %a_index%, , Starting..., AHK + C# Demo
Sleep, 1
}
Progress, Off
Sleep 1000
SplashImage, Off
c# =
(
using System;
using System.Data;
using System.Windows.Forms;
using System.Data.SqlClient;
using MySql.Data.MySqlClient;
using MySql.Data;
using MathFunctions; // It uses the MyFunctions.dll .
// The class that allows the program to run, Note that other classes are directly accessed though.
class Start
{
Foo myfoo = new Foo();
public static int counter = 0;
static void Main()
{
MessageBox.Show("This program is not standalone. Please run the normal program Test.exe");
}
public void MathFuct(){
int add = AddClass.Add(125,125); // Static call to class in MyFunctions.dll
int mul = MultiClass.Multiply(25,25); // Static call to class in MyFunctions.dll
int fac = FactorialClass.Factorial(5); // Static call to class in MyFunctions.dll
string str = "Addition result = " + add.ToString() + "\n"
+ "Multiplication result = " + mul.ToString() + "\n"
+ "Factorial result = " + fac.ToString();
MessageBox.Show(str, "Call To MyFunctions.dll");
}
public int Test(int returnInt_, int returnInt2_)
{
int valueFromFooTest = myfoo.calc(returnInt_, returnInt2_);
counter = counter + 1;
return valueFromFooTest + counter;
}
public void selectfrommysql()
{
myfoo.mysql();
}
}
class Foo {
public static int local = 0;
public Foo() {
MessageBox.Show("Hello, world, from secondary class of C#!");
}
public int calc(int returnInt, int returnInt2) {
MessageBox.Show("Hello, world, from function of secondary class of C#!");
local = local + 1;
return returnInt + returnInt2 + local;
}
public void mysql() {
//create a MySQL connection with a query string
MySqlConnection connection = new MySqlConnection("server=localhost;database=solontesting;uid=root;password=");
MySqlCommand command = connection.CreateCommand();
MySqlDataReader Reader;
command.CommandText = "select * from shares";
int x;
try
{
connection.Open();
}
catch(Exception excp)
{
Exception myExcp = new Exception("Error connecting you to " +
"the my sql server. Internal error message: " + excp.Message, excp);
MessageBox.Show("C# failed to connect to MySql. " + myExcp); // Display error message, Autohotkey won't display its own because ComObjError(false) is set later in the code.
throw myExcp;
}
Reader = command.ExecuteReader();
string thisrow = "";
while (Reader.Read()){
for (int i= 0;i<Reader.FieldCount;i++)
thisrow+=Reader.GetValue(i).ToString() + ",";
}
//close the connection
connection.Close();
MessageBox.Show("C# queried Mysql, the results are: " + thisrow);
}
}
)
; XML holder variables
x = <root><element var="test">Test</element></root>
x2 = <root><element var="test">XML Test 2: showing contents of a node.</element></root>
x3 = (<?xml version="1.0" encoding="UTF-8" ?>
<root>
<item>
<name>!mail</naquotme>
<description>Standard mail reply form</description>
<scope>Lotus Notes</scope>
</item>
<item>
<name>!scont</name>
<description>Subject of Contact Exception email</description>
<scope>Lotus Notes</scope>
</item>
<item>
<name>!cont</name>
<description>Body of Contact Exception email</description>
<scope>Lotus Notes</scope>
</item>
<item>
<name>mll</name>
<description>Cleans up ICT Incident report mail</description>
<scope></scope>
</item>
<item>
<name>pwmll</name>
<description>Cleans up ICT password reset mail</description>
<scope>global</scope>
</item>
<item>
<name>!regards</name>
<description>Best regards [Your name],</description> <!-- You need to escape a comma with ` Autohotkey requires that -->
<scope>Lotus Notes</scope>
</item>
<item>
<name>tel</name>
<description>telephone number</description>
<scope>global</scope>
</item>
<item>
<name>Pw</name>
<description>Password</description>
<scope></scope>
</item>
<item>
<name>pw</name>
<description>password</description>
<scope></scope>
</item>
</root>)
returnVal := 0
CLR_Start()
c#_dll_file = %A_Temp%\C#AHK\MySql.Data.dll ; Files are found at %A_Temp% because I use FileInstall at the top of the script
c#_dll_fileTwo = %A_Temp%\C#AHK\MyFunctions.dll ; Files are found at %A_Temp% because I use FileInstall at the top of the script
all_dlls = System.dll | System.Windows.Forms.dll | System.Data.dll | %c#_dll_file% | %c#_dll_fileTwo%
Windows_temp_folder = %A_Temp%\C#AHK ; Windows temp directory for storing temporary files, this is where we will store the C# exe compiled code
;Start a new AppDomain so we can define the base path to load assemblies from.
CLR_StartDomain(pApp, A_Temp) ; Base path to load the DLLS with be the temp directory of Windows
CLR_CompileC#(c#, all_dlls, pApp, Windows_temp_folder . "\QueryMySql.exe")
asm := CLR_LoadLibrary("QueryMySql.exe", pApp)
MsgBox, Hello From AutoHotKey, press control, alt and s to start the demo. Control, alt and q to quit application.
return ; End of OnExit
; Quits the application
^!q::
ExitApp
return
; continue the application
^!c::^!s ; map keys to the keys that start the application
; Start the application
^!s::
MsgBox, Hello From AutoHotKey, control, alt and s were pressed to run the following.
returnVal := runMainBody(returnVal,asm, x, x2, x3)
return
runMainBody(returnVal,asm, x, x2, x3) ; asm must be passed to a function in order to be able to
;compile the C# code of variable c# once and be able to keep state
{
/*
Working with XML
*/
Msgbox, Working with XML
Msgbox Testing XML processing, first example: changing content.
; Testing XML processing, first example: changing content.
xpath_load(x) ; XML content can be loaded in-place directly
Msgbox Original content
Msgbox, % xpath(x, "element/text()" )
Msgbox, Changing content and showing the whole XML document
xpath(x, "element/text()", "XML Test 1: Content changed")
MsgBox, % xpath_save(x) ; show the new source without having to save it
Msgbox, Second XML example: showing content
; Second XML example: showing content
xpath_load(x2)
node = % xpath(x2, "element/text()")
MsgBox, %node%
Msgbox Third XML example: looping through XML and showing content
; Third XML example: looping through XML and showing content
xpath_load(x3)
Loop
{
If xpath(x3, "/root/item[" . A_Index . "]") = ""
Break
hotkeys := xpath(x3, "/root/item[" . A_Index . "]/name/text()")
description := xpath(x3, "/root/item[" . A_Index . "]/description/text()")
scope := xpath(x3, "/root/item[" . A_Index . "]/scope/text()")
StringReplace, description, description, ,, `, ; Clean commas from XML, the strange code is produced by xpath in the place of comma for some reason
Msgbox,%hotkeys% %description% %scope%
}
Msgbox Fourth XML example: looping through XML and changing content
; Fourth XML example: looping through XML and changing content
Loop
{
If xpath(x3, "/root/item[" . A_Index . "]") = ""
Break
xpath(x3, "/root/item[" . A_Index . "]/name/text()", "Dummy Text")
xpath(x3, "/root/item[" . A_Index . "]/description/text()")
xpath(x3, "/root/item[" . A_Index . "]/scope/text()")
}
Msgbox, XML changed
; Save x3 XML
xpath_save(x3)
Msgbox Fifth XML example: looping through XML and showing changed content
; Fifth XML example: looping through XML and showing changed content
xpath_load(x3)
Loop
{
If xpath(x3, "/root/item[" . A_Index . "]") = ""
Break
hotkeys := xpath(x3, "/root/item[" . A_Index . "]/name/text()")
description := xpath(x3, "/root/item[" . A_Index . "]/description/text()")
scope := xpath(x3, "/root/item[" . A_Index . "]/scope/text()")
StringReplace, description, description, ,, `, ; Clean commas from XML
Msgbox,%hotkeys% %description% %scope%
}
/*
Communicating from Autohotkey to C# and vice versa
*/
ComObjError(false) ; Disable COM Errors, so that only C# will produce error whenever wanted.
obj := CLR_CreateObject(asm, "Start")
obj.MathFuct() ; equivalent to obj["MathFuct"];
obj["selectfrommysql"] ; obj[anything] should be equivalent to COM_Invoke(obj,anything). AutoHotKey_L supports COM natively
returnValue := obj["Test",10, 20] ; obj[anything] should be equivalent to COM_Invoke(obj,anything). AutoHotKey_L supports COM natively
returnVal := returnVal + returnValue
Msgbox, Value given from C#, total is kept in AutoHotKey: %returnVal%
Msgbox, Value given from C#, total is kept in C#: %returnValue%
MsgBox, Sending arrays to C#, C# will loop through them and display their values, which are
; Sending an array to C#
arr := ComObjArray(8, 2)
arr[0] := "string 1"
arr[1] := "string 2"
param := ComObjParameter(0x2008, ComObjValue(arr))
; Sending a second array to C#
arr2 := ComObjArray(3, 2)
arr2[0] := 3
arr2[1] := 4
param2 := ComObjParameter(0x2008, ComObjValue(arr2))
asm := CLR_CompileC#(LoadC#(), "System.dll|System.Windows.Forms.dll" )
obj := CLR_CreateObject(asm, "Test")
ArrayFromC# := Object()
ArrayFromC# := obj["StringIntTest",arr,arr2]
MsgBox, C# returned arrays to Autohotkey, Autohotkey will loop through them, their values are
For value in ArrayFromC#
MsgBox %value%
ArrayFromC# := obj["ReturnString",arr]
For value in ArrayFromC#
MsgBox %value%
/*
Dynamically building a List View from the second C# array returned to Autohotkey
*/
Msgbox, Dynamically building a List View from the second C# array returned to Autohotkey
; Create the ListView with two columns, Name and Size:
Gui, Add, ListView, r1 w300 +HScroll +VScroll gMyListView, Name ; Added scrollbars too
; Gather a list of file names from a folder and put them into the ListView:
For value in ArrayFromC#
LV_Add("", value)
LV_ModifyCol() ; Auto-size each column to fit its contents.
LV_ModifyCol(2, "Integer") ; For sorting purposes, indicate that column 2 is an integer.
; Display the window and return. The script will be notified whenever the user double clicks a row.
Gui, Show
; Speech To Text
Text := "Well done. Now you have autohotkey communicate with C Sharp .NET . Press control alt and c to continue the demo."
SAPI := ComObjCreate("SAPI.SpVoice")
MsgBox, 0, Rate: 0, Text To Speech
SAPI.speak(Text)
return returnVal
}
LoadC#() {
c# =
(
using System;
using System.Windows.Forms;
public class Test
{
public int[] StringIntTest(string[] mystrings, int[] myintegers)
{
foreach(string mystring in mystrings)
MessageBox.Show(mystring);
foreach(int myinteger in myintegers)
MessageBox.Show( Convert.ToString(myinteger) );
myintegers[0] = 5;
myintegers[1] = 6;
return myintegers;
}
public string[] ReturnString(string[] mystrings)
{
mystrings[0] = "String 7";
mystrings[1] = "String 8";
return mystrings;
}
}
)
return c#
}
MyListView:
if A_GuiEvent = DoubleClick
{
LV_GetText(RowText, A_EventInfo) ; Get the text from the row's first field.
ToolTip You double-clicked row number %A_EventInfo%. Text: "%RowText%"
}
return
ExitSub:
MsgBox, 4100,Exit?, Are you sure you want to exit?, 5 ;Always on top.
WinSet, AlwaysOnTop, toggle, Exit?
IfMsgBox, Yes
{
ExitApp
}
return