- If .NET 9 is not installed on your machine, you need to download and run the x64 ".NET Desktop Runtime" installer from here.
- Download and run the Keysharp installer from the Releases page.
- The install path can be optionally added to the $PATH varible, so you can run it from the command line from anywhere.
- The path entry will be removed upon uninstall.
- It also registers Keysharp.exe as the default program to open
.ksfiles. So after installing, double click any.ksfile to run it.
- The install path can be optionally added to the $PATH varible, so you can run it from the command line from anywhere.
- Download and unzip the zip file from the Releases page.
- CD to the unzipped folder.
- Run
.\Keysharp.exe yourfilename.ahk
- Download the latest version of Visual Studio 2022.
- This should install .NET 9. If it doesn't, you need to install it manually from the link above.
- Open Keysharp.sln
- Build all (building the installer is not necessary).
- CD to bin\release\net9.0-windows
- Run
.\Keysharp.exe yourtestfile.ahk
Keysharp is a fork and improvement of the abandoned IronAHK project, which itself was a C# re-write of the C++ AutoHotkey project.
The intent is for Keysharp to run on Windows, Linux and eventually Mac. For now, only Windows is supported.
This project is in the alpha testing stage and is not yet recommended for production systems.
Some general notes about Keysharp's implementation of the AutoHotkey v2 specification:
-
The syntax is v2 style. While some remnants of V1 will work, it's unintentional and only v2 is supported.
-
The operation of Keysharp is different than AHK. While AHK is an interpreted scripting language, Keysharp actually creates a compiled .NET executable and runs it.
-
The process for reading and running a script is:
- Pass the script to Keysharp.exe which parses it and generates a Document Object Model (DOM) tree.
- The DOM compiler generates C# code for a single program.
- The C# program code is compiled into an in-memory executable.
- The executable is ran in memory as a new process.
- Optionally output the generated C# code to a .cs file for debugging purposes with the
-codeoutoption. - Optionally output the generated executable to an .exe file for running standalone in the future with the
-exeoutoption.
-
Keysharp supports files with the
.ahkextension, however installing it will not register it with that extension. Instead, it will register the other extension it supports,.ks. -
In addition to
Keysharp.exe, there is another executable that ships with the installer namedKeyview.exe. This program can be used to see the C# code that is generated from the corresponding script code.- It gives real-time feedback so you can see immediately when you have a syntax error.
- It is recommended that you use this to write code.
- The features are very primitive at the moment, and help improving it would be greatly appreciated.
Despite our best efforts to remain compatible with the AHK v2 spec, there are differences. Some of these differences are a reduction in functionality, and others are an increase. There are also slight syntax changes.
- Keysharp follows the .NET memory model.
- There is no variable caching with strings vs numbers. All variables are C# objects.
- Values not stored in variables are, like regular variables, only eligible to be freed once they go out of scope.
FileOpen("test.txt", "w").Write("hello") ; The temporary file object does not get deleted at the end of the line, only possibly at the end of the current scope.
+ Object destructors/finalizers are called at a random point in time, and `Collect()` should be used if they need to be invoked predictably.
- AHK says about the inc/dec ++/-- operators on empty variables: "Due to backward compatibility, the operators ++ and -- treat blank variables as zero, but only when they are alone on a line".
- Keysharp breaks this and will instead create a variable, initialize it to zero, then increment it.
- For example, a file with nothing but the line
x++in it, will end with a variable named x which has the value of 1.
- Function objects behave differently in a few ways.
- The underlying function object class is named
FuncObj. This was named so, instead ofFunc, because C# already contains a built in class namedFunc. - Function objects can be created by passing the name of the function as as a direct reference or as a string to
Func(). - This can be done by passing the name of the desired function as a direct reference or as a string, and optionally an object and a parameter count like so:
Func(functionName [, object, paramCount]).Func("functionName" [, object, paramCount]).
- Each call to these functions returns a new unique function object. For a given function, it's best to create one object and reference that throughout the script.
- For built-in functions which take a function object as a parameter, there are four ways to call them:
- The underlying function object class is named
Func1() {
}
SetTimer(Func1) ; Pass a direct reference to the function.
SetTimer("Func1") ; Pass the name of the function.
SetTimer(Func(Func1)) ; Pass a direct reference to the function as an argument to Func().
SetTimer(Func("Func1")) ; Pass the name of the function as an argument to Func().
- Closures are not supported. The current behaviour is that a nested function (including anonymous functions) is automatically converted to a normal top-level function.
- Exception classes aren't, and can't be, derived from
KeysharpObject.- That is because for the exception mechanics to work in C#, all exception objects must be derived from the base
System.Exceptionclass, and multiple inheritance is not allowed. - User-defined exception classes must derive from
Error. - Catch statements with multiple exception classes listed on a single line will parse, but will not generate the expected functionality.
- List each exception type separately to work around this.
- That is because for the exception mechanics to work in C#, all exception objects must be derived from the base
CallbackCreate()does not support theCDecl/Coption because the program will be run in 64-bit mode.- The
paramCountparameter is unused. The callback that gets created supports passing up to 31 parameters and the number that actually gets passed is adjusted internally. - Passing string pointers to
DllCall()when passing a created callback is strongly recommended against. This is because the string pointer cannot remain pinned, and is likely to crash the program if the pointer gets moved by the GC. - Usage of the created callback will be extremely inefficient, so usage of
CallbackCreate()is discouraged.
- The
- Deleting a tab via
GuiCtrl.Delete()does not reassociate the controls that it contains with the next tab. Instead, they are all deleted. - The size and positioning of some GUI components will be slightly different than AHK because WinForms uses different defaults.
- The class name for statusbar/statusstrip objects created by Keysharp is "WindowsForms10.Window.8.app.0.2b89eaa_r3_ad1". However, for accessing a statusbar created by another, non .NET program, the class name is still "msctls_statusbar321".
- Using the class name with
ClassNNon .NET controls gives long, version specific names such as "WindowsForms10.Window.8.app.0.2b89eaa_r3_ad1" for a statusbar/statusstrip.- This is because a simpler class names can't be specified in code the way they can in AHK with calls to
CreatWindowEx(). - These long names may change from machine to machine, and may change for the same GUI if you edit its code.
- There is an new
NetClassNNproperty alongsideClassNN. - The class names of all GUI controls created in Keysharp are prefixed with the string "Keysharp", eg:
KeysharpButton,KeysharpEditetc... NetClassNNwill give values like 'KeysharpButton6' (note that the final digit is the same for theClassNNand theNetClassNN).- Due to the added simplicity,
NetClassNNis preferred overClassNNfor WinForms controls created with Keysharp. - This is used internally in the index operator for the Gui class, where if a control with a matching
ClassNNis not found, then controls are searched for theirNetClassNNvalues.
- This is because a simpler class names can't be specified in code the way they can in AHK with calls to
- Tooltips function slightly differently.
- When specifying a coordinate for a ToolTip, it will attempt to show it relative to the currently focused Keysharp form.
- If there is no focused form, it will attempt to show it relative to the last form which was created. If none are found, it will use the main form shown when double clicking the tray icon.
- If the form is minimized, then it will attempt to use the RestoreBounds property of the form. This may not work sometimes, so the ToolTip may never show in that case.
- Tooltips cannot be used if the script is not persistent (meaning, it has no main window). This is because C# tooltips require a parent control or form.
TrayTip()functions slightly differently.- Muting the sound played by the tip is not supported with the
Muteoption. The sound will be whatever the user has configured in their system settings. - The option
4to use the program's tray icon is not supported. It is always shown in the title of the tip. - The option
32to use the large version of the program's tray icon is not supported. Windows will always show the small version.
- Muting the sound played by the tip is not supported with the
Sleep()works, but usesApplication.DoEvents()internally which is not a good programming practice and can lead to hard to solve bugs.- For this reason, it's recommended that users use timers for repeated execution rather than a loop with calls to
Sleep().
- For this reason, it's recommended that users use timers for repeated execution rather than a loop with calls to
- The Optimization section of the
#HotIfdocumentation doesn't apply to Keysharp because it uses compiled code, thus the expressions are never re-evaluated. - The
#ErrorStdOutdirective will not print to the console unless piping is used. For example:.\Keysharp.exe .\test.ahk | more.\Keysharp.exe .\test.ahk | more > out.txt
- Menu items, whether shown or not, have no impact on threading.
AddStandard()detects menu items by string, instead of ID, because WinForms doesn't expose the ID.ControlMove()andControlSetPos()operate relative to their immediate parent, which may not be the main window if they are contained in a nested control.
- Delays are not inserted after every window and control related call. Due to the design of Keysharp, this is not needed and causes out of order message processing bugs.
SetWinDelay(),A_WinDelay,SetControlDelayandA_ControlDelayexist but have no effect.
- Static function variables are initialized on program startup, rather than the first time the function is called. This is because C# does not support static function variables.
- The built in class methods
__Init()and__New()are not static. They are instance methods so they can access static and instance member variables. - Because
__Init()and__New()are not static, a new special static function has been created named__StaticInit(). Simple static member variable initialization should be done inline. However, more complex initialization should be done inside of__StaticInit(). This function will be called exactly once.
class myclass
{
static simplevar := 123
static complexvar := ""
static __StaticInit()
{
; Note, global is required because `this.` can't be used because `__StaticInit()` is a static function.
; Only static members may be accessed within this function.
global
complexvar := 456; complex initialization here
}
}
+ If static variables are initialized in `__New()` instead of inline, they won't contain valid values until an instance of the class is created. Further, they will be reinitialized for every instance of the class created.
- The parameters for
__New()in a class definition will be not be automatically passed to the base class.- To pass the values as is to the base, change the values passed, or the order they are passed in, call
super.__New(arg1, arg2, ...)in__New()with the arguments in the needed order. - This is not needed for classes derived from built-in types.
- To pass the values as is to the base, change the values passed, or the order they are passed in, call
- Because
__Init(),__New()andCall()are auto-generated methods, users must not also define functions with these same names.- This also applies to the auto-generated
__Classproperty and constructors with the same name as the class they are defined in.
- This also applies to the auto-generated
- Function objects are much slower than direct function calls due to the need to use reflection. So for repeated function calls, such as those involving math, it's best to use the functions directly.
- The
Fileobject is internally namedKeysharpFileso that it doesn't conflict withSystem.IO.File. obj.OwnProps()/ObjOwnProps()take an optional second/third parameter as a boolean (default:True). PassTrueto only return the properties defined by the user, elseFalseto also return properties defined internally by Keysharp.- In
SetTimer(), the priority is not in the range -2147483648 and 2147483647, instead it is only 0-4. - If a
ComObjectwithVarTypeofVT_DISPATCHand a null pointer value is assinged a non-null pointer value, its type does not change. ThePtrmember remains available. A_LineNumberis not a reliable indicator of the line number because the preprocessor condenses the code before parsing and compiling it.- Loop counter variables for
for inloops declared inside of a function cannot have the same name as a local variable declared inside of that same function.
testfunc()
{
arr := [10, 20, 30]
loopvar := 0 ; Either change the name of this variable, the loop variable, or move this declaration outside of the function.
for (loopvar in arr)
{
}
}
ObjPtr()returns an IUnknownComObjectwith the pointer wrapped in it, whereasObjPtrAddRef()returns a raw pointer.- Pointers returned by
StrPtr()must be freed by passing the value to a new function namedObjFree().StrPtr()does not return the address of the string, instead it returns the address of a copy of the bytes of the string.
Sleep()will not do any sleeping if shutdown has been initiated./Debugcommand line switch is not implemented.- If a script is compiled then none of Keysharp or AutoHotkey command parameters apply.
- The syntax used in
Format()is exactly that ofstring.Format()in C#, except with 1-based indexing. Traditional AHK style formatting is not supported.- Full documentation for the formatting rules can be found here.
- The default name for the array of parameters in a variadic function is
args, instead ofparams. This is due toparamsbeing a reserved word in C#.- The array for variadic parameters is read only and cannot be manipulated in the way a normal
Arraycan.
- The array for variadic parameters is read only and cannot be manipulated in the way a normal
DllCall()has the following caveats:- Use
PtrandStringBufferfor double pointer parameters such asLPTSTR*.
- Use
ImageSearch()takes an options string as a fifth parameter, rather than inserted in the string before theimageFileparameter.- In AHK, when applied to a power operation, the unary operators apply to the entire result. So
-x**yreally means-(x**y).- In Keysharp, this behavior is different due to an inability to resolve bugs in the original code. So follow these rules instead:
- To negate the result of a power operation, use parentheses:
-(x**y). - To negate one term of a power operation before applying, use parentheses around the term:
(-x)**yor-(x)**y.
- To negate the result of a power operation, use parentheses:
- In Keysharp, this behavior is different due to an inability to resolve bugs in the original code. So follow these rules instead:
- A leading plus sign on numeric values, such as
+123or+0x123is not supported. It has no effect anyway, so just omit it. - AHK does not support null, but Keysharp uses it in some cases to determine if a variable has ever been assigned to, such as with
IsSet(). - Leading spaces and tabs are not omitted from the strings in continuation strings. They will be parsed as is, according to the options specified. Trailing spaces and tabs will not be trimmed unless
RTrimis specified. - In continuation statements, the smart behavior logic for left trimming each line is disabled. Lines are not left trimmed by default and are only left trimmed if
LTrimis specified. - Because a comma at the end of a line indicates a continuation statement, command style syntax with a trailing comma is not supported:
MouseGetPos &mrX, &mrY, , ; not supportedMouseGetPos &mrX, &mrY ; omitting the trailing commas is supportedMouseGetPos(&mrX, &mrY) ; using parens is preferredMouseGetPos(&mrX, &mrY, , ) ; trailing commas can be used with parens
- Quotes in strings cannot be escaped with double quotes, they must use the escape character, `.
- Dynamic variable references like %x% can only refer to a global variable. There is no way to access a local variable in C# via reflection.
Gotostatements cannot use any type of variable. They must be labels known at compile time and function just like goto statements in C#.Gotostatements being called as a function likeGoto("Label")are not supported. Instead, just usegoto Label.- Callback functions do not require a
*parameter to work. Sofunc()andfunc(*)can both be used as callbacks. - Optional function parameters can be specified using the
?suffix, however it is not needed or supported when referring to that parameter inside of the function, for example:
myfunc(a, b, c?, d?)
{
e := a
f := b
g := c ; No question mark needed for c or d.
h := d
}
- The spread operator asterisk (
*) can be used as a function argument only if the function parameter is variadic, for example:
myfunc(a, b*) {
}
myfunc(c*) ; not allowed because `a` isn't variadic
myfunc(1, c*) ; allowed
- The parameter name for the variadic
__New()method in classes is alwaysargsunless otherwise specified.
class class1
{
__New() ; args is implicit.
{
for n in args
{
; Do something.
}
}
}
- The
#Requiresdirective differs in the following ways:- In addition to supporting
AutoHotkey, it also supportsKeysharp. - Sub versions such as -alpha and -beta are not supported. Only the four numerical values values contained in the assembly version in the form of
0.0.0.0are supported.
- In addition to supporting
- Global variables can be accessed from within class methods by using the
program.prefix:program.a := 123. - Accessing class member variables within methods and properties does not require the
this.prefix.- Instead, just reference the member variable using
global, and that will distinguish it between a local function variable of the same name. - Using
this.is still supported, but is slower, so avoid using it if possible.
- Instead, just reference the member variable using
- If a class and sublcass both have properties with the same name, the following rules apply when accessing the properties within a member function in the base class:
global propnameandthis.propnamerefer to the property defined in the most derived subclass class.super.propnamerefers to the property defined in the base class.- To avoid confusion, it is best not to give properties the same name between base and sub classes.
- For any
__Enum()class method, it should have a parameter value of 2 when returningArrayorMap, since their enumerators have two fields. - Auto-generated variables and functions will have the prefix
_ks_, so to avoid naming collisions you shouldn't create variables/functions with that same prefix. - RegEx uses PCRE2 engine powered by the PCRE.NET library. There are a few limitations compared to the AutoHotkey implementation:
- The following options are different:
- S: Studies the pattern to try improve its performance.
- This is not supported. All RegEx objects are internally created with the
PcreOptions.Compiledoption specified, so performance should be reasonable.
- This is not supported. All RegEx objects are internally created with the
- u: This new option disables optimizations PCRE2_NO_AUTO_POSSESS, PCRE2_NO_START_OPTIMIZE, and PCRE2_NO_DOTSTAR_ANCHOR. This option can be useful when using callouts, since these optimizations might prevent some callouts from happening.
- S: Studies the pattern to try improve its performance.
- Callouts differ in a few ways:
- Callouts do not set
A_EventInfo - The callout function must be a top-level function
- A named callout must be enclosed in "", '', or {}
- Callouts do not set
- RegEx operator ~= returns a RegExMatchInfo, which is treated as an integer in comparison or math operations
- The following options are different:
Mapinternally uses a real hashmap, which means item access, insertions and removals are faster, which is especially true for larger datasets. To keep at least partial compatibility with AutoHotkey theMapobject is copied and sorted before enumeration, which means modifying theMapduring enumeration will not have the same effect as in AHK.- A new
HashMapclass has been added which extendsMapand does not perform sorting before enumeration.
- A new
- Buffer has an
__Item[]indexer which can be used to read a byte at a 1-based offset. - Buffer has
ToHex(),ToBase64(), andToByteArray()methods which can be used to convert the contents to string (hex or base64), or a byte-array to for example write to a file. - A new class named
StringBufferwhich can be used for passing string memory toDllCall()which will be written to inside of the call.- There are two methods for creating a
StringBuffer:StringBuffer(str := "") => StringBuffer: Creates aStringBufferwith a string ofstrand a capacity of 256.StringBuffer(str, capacity) => StringBuffer: Creates aStringBufferwith a string ofstrand a capacity ofMax(16, capacity).
StringBufferis implicitly castable toString.
- There are two methods for creating a
sb := StringBuffer("hello")
MsgBox(sb) ; Shows "hello".
+ As an alternative to passing a `Buffer` object with type `Ptr` to a function which will allocate and place string data into the buffer, the caller can instead use a `StringBuffer` object to hold the new string.
+ This relieves the caller of having to create a `Buffer` object, then call `StrGet()` on the new string data.
+ `wsprintf()` is one such example.
; Using a Buffer:
ZeroPaddedNumber := Buffer(20)
DllCall("wsprintf", "Ptr", ZeroPaddedNumber, "Str", "%010d", "Int", 432, "Cdecl")
MsgBox(StrGet(ZeroPaddedNumber)) ; Shows "0000000432".
; Using a StringBuffer:
sb := StringBuffer()
DllCall("wsprintf", "Ptr", sb, "Str", "%010d", "Int", 432, "Cdecl")
MsgBox(sb) ; No need to use StrGet() anymore.
+ `StringBuffer` internally uses a `StringBuilder` which is how C# P/Invoke handles string pointers.
- New methods for
Array:Add(value) => Integer: Adds a single element to the array.- This should be more efficient than
Push(values*)when adding a single item because it's not variadic. It also returns the length of the array after the add completes.
- This should be more efficient than
Filter(callback: (value [, index]) => Boolean) => Array: Applies a filter to each element of the array and returns a new array consisting of all elements for whichcallbackreturned true.FindIndex(callback: (value [, index]) => Boolean, startIndex := 1) => Integer: Returns the index of the first element for whichcallbackreturned true, starting atstartIndex. Returns 0 ifcallbacknever returned true.- If
startIndexis negative, the search starts from the end of the array and moves toward the beginning.
- If
IndexOf(value, startIndex := 1) => Integer: Returns the index of the first item in the array which equals value, starting atstartIndex. Returns 0 if value is not found.- If
startIndexis negative, the search starts from the end of the array and moves toward the beginning.
- If
Join(separator := ',') => String: Joins together the string representation of all array elements, separated byseparator.MapTo(callback: (value [, index]) => Any, startIndex := 1) => Array: Maps each element of the array, starting atstartIndex, into a new array where the mapping incallbackperforms some operation.
lam := (x, i) => x * i
arr := [10, 20, 30]
arr2 := arr.MapTo(lam)
+ `Sort(callback: (a, b) => Integer) => this`: Sorts the array in place. The callback should use the usual logic of returning -1 when `a < b`, 0 when `a == b` and 1 otherwise.
-
Hyperbolic versions of the trigonometric functions:
Sinh(value) => DoubleCosh(value) => DoubleTanh(value) => Double
-
A New function
RandomSeed(Integer)to reinitialize the random number generator for the current thread with a specified numerical seed. -
New file functions:
-
FileDirName(filename) => Stringto return the full path to filename, without the actual filename or trailing directory separator character. -
FileFullPath(filename) => Stringto return the full path to filename.
-
-
New window functions:
-
WinMaximizeAll()to maximize all windows. -
WinGetAlwaysOnTop([winTitle, winText, excludeTitle, excludeText]) => Integerto determine whether a window will always stay on top of other windows.
-
-
Run/RunWait()can take an extra string for the argument instead of appending it to the program name string. However, the original functionality still works too.- The new signature is:
Run/RunWait(target [, workingDir, options, &outputVarPID, args]).
- The new signature is:
-
When specifying colors for GUI components, the list of supported known colors can be found here.
-
ListViewsupports a new methodDeleteCol(col) => Booleanto remove a column. The value returned indicates whether the column was found and deleted. -
New methods and properties for
Menu:-
HideItem(),ShowItem()andToggleItemVis()which can show, hide or toggle the visibility of a specific menu item. -
MenuItemName()to get the name of a menu item, rather than having to useDllCall(). -
SetForeColor()to set the fore (text) color of a menu item. -
MenuItemCountto get the number of sub items within a menu.
-
-
Picturesupports clearing the picture by setting theValueproperty to empty. -
New options for
UpDown:- These relieve the caller of having to use native Windows API calls.
-
IncrementXXXto specify an increment other than 1.MyGui.Add("UpDown", "x5 y55 vMyNud Increment10", 1)
-
Hexto show the numeric value in hexadecimal.
-
TabControlsupports a new methodSetTabIcon(tabIndex, imageIndex)to relieve the caller of having to useSendMessage(). -
TreeViewsupports a new methodGetNode(nodeIndex) => TreeNodewhich retrieves a raw winforms TreeNode object based on a passed in ID. -
Gui controls support taking a boolean
Autosize(default:false) argument in theAdd()method to allow them to optimally size themselves. -
Guihas a new property namedVisiblewhich get/set whether the window is visible or not. -
A new function
ShowDebug()to show the main window and focus the debug output tab. -
A new function
OutputDebugLine()which is the same asOutputDebug()but appends a linebreak at the end of the string. -
EnvUpdate()is retained to provide for a cross platform way to update environment variables. -
The 40 character limit for hotstring abbreviations has been removed. There is no limit to the length.
-
FileGetSize()supportsGandTfor gigabytes and terabytes. -
DateAdd()andDateDiff()support taking a value of"L"for theTimeUnitsparameter to add miLliseconds or return the elapsed time in milliseconds, respectively.- See the new accessors
A_NowMs/A_NowUTCMs.
- See the new accessors
-
SubStr()uses a default of 1 for the second parameter,startingPos, to relieve the caller of always having to specify it. -
New string functions:
-
Base64Decode(str) => Arrayto convert a Base64 string to a Buffer containing the decoded bytes. -
Base64Encode(value) => Stringto convert a byte array to a Base64 string. -
NormalizeEol(str, eol) => Stringto make all line endings in a string match the value passed in, or the default for the current environment. -
StartsWith(value, token [,comparison]) => BooleanandEndsWith(value, token [,comparison]) => Booleanto determine if the beginning or end of a string start/end with a given string. -
Join(separator, params*) => Stringto join each parameter together as a string, separated byseparator.- Pass params as
params*if it's a collection.
- Pass params as
-
-
New RegEx functions
RegExMatchCs()andRegExReplaceCs()which use the C# style regular expression syntax rather than PCRE2.-
OutputVarinRegExMatchCs()will be of typeRegExMatchInfoCs. - PCRE exceptions are not thrown when there is an error, instead C# regex exceptions are thrown.
- To learn more about C# regular expressions, see here.
- The following options are different:
-
-A: Forces the pattern to be anchored; that is, it can match only at the start of Haystack. Under most conditions, this is equivalent to explicitly anchoring the pattern by means such as
^.- -This is not supported, instead just use
^or\Ain your regex string.
- -This is not supported, instead just use
-
-C: Enables the auto-callout mode.
- -This is not supported. C# regular expressions don't support calling an event handler for each match. You must manually iterate through the matches yourself.
-
-D: Forces dollar-sign ($) to match at the very end of Haystack, even if Haystack's last item is a newline. Without this option, $ instead matches right before the final newline (if there is one). Note: This option is ignored when the
moption is present.- -This is not supported, instead just use
$. However, this will only match\n, not\r\n. To match theCR/LFcharacter combination, include\r?$in the regular expression pattern.
- -This is not supported, instead just use
-
-J: Allows duplicate named subpatterns.
- -This is not supported.
-
-S: Studies the pattern to try improve its performance.
- -This is not supported. All RegEx objects are internally created with the
RegexOptions.Compiledoption specified, so performance should be reasonable.
- -This is not supported. All RegEx objects are internally created with the
-
-U: Ungreedy.
- -This is not supported, instead use
?after:*, ?, +, and {min,max}.
- -This is not supported, instead use
-
-X: Enables PCRE features that are incompatible with Perl.
- -This is not supported because it's Perl specific.
-
`a `n `r: Causes specific characters to be recognized as newlines.- -This is not supported.
-
\Kis not supported, instead, try using(?<=abc).
-
-
-
The v1
MapmethodsMaxIndex()andMinIndex()are still supported. They are also supported forArray. -
New function
GetScreenClip(x, y, width, height [, filename]) => Bitmapcan be used to return a bitmap screenshot of an area of the screen and optionally save it to file. -
Rich text boxes are supported by passing
RichEdittoGui.Add(). The same options fromEditare supported with the following caveats:-
Multilineis true by default. -
WantReturnandPasswordare not supported. -
UppercaseandLowercaseare supported, but only for key presses, not for pasting. - The
Gui.Control.Valueproperty will only get/set the displayed text of the control. To get/set the raw rich text, use the new propertyGui.Control.RichText.- Use
AltSubmitwithSubmit()to get the raw rich text. - Attempting to use
Gui.Control.RichTexton any control other thanRichEditwill throw an exception.
- Use
-
-
Loading icons from .NET DLLs is supported by passing the name of the icon resource in place of the icon number.
- To set the tray icon to the built in suspended icon:
TraySetIcon(A_KeysharpCorePath, "Keysharp_s.ico")
- To set a menu item to the same:
parentMenu.SetIcon("Menu caption", A_KeysharpCorePath, "Keysharp_s.ico")
- To set the tray icon to the built in suspended icon:
-
New clipboard functions:
-
CopyImageToClipboard(filename [,options])is supported which copies an image to the clipboard.- Uses the same arguments as
LoadPicture(). - This is a fully separate copy and does not share any handle, or perform any file locking with the original image being read.
- Uses the same arguments as
-
IsClipboardEmpty() => Booleanreturns whether the clipboard is truly empty.
-
-
When sending a string through
SendMessage()using theWM_COPYDATAmessage type, the caller is no longer responsible for creating the specialCOPYDATAstruct.- Instead, just pass
WM_COPYDATA (0x4A)as the message type and the string as thelparam, andSendMessage()will handle it internally. - Note, this will send the string as UTF-16 Unicode. If you need to send to a program which expects ASCII, then you'll need to manually create the
COPYDATAstruct.
- Instead, just pass
-
A new function
Collect()which callsGC.Collect()to force a memory collection.- This rarely ever has to be used in properly written code.
- Calling
Collect()may not always have an immediate effect. For example if an object is assigned to a variable inside a function and then the variable is assigned an empty string then callingCollect()after it will not cause the object destructor to be called. Only after the function has returned will the object be considered to have no references andCollect()starts working. - If an object destructor needs to be called immediately then it may better to call
Object.__Delete()manually.
-
In addition to using
#ClipboardTimeout, a new accessor namedA_ClipboardTimeoutcan be used at any point in the program to get or set that value. -
A compiled script can be reloaded.
- AHK does not support reloading a compiled script.
-
A new function
RunScript(code, callbackOrAsync?, name := "DynamicScript", executable?)which dynamically parses, compiles, and runs the provided code. Optionally provide the script name; whether to run it asynchronously (non-unset non-zerocallbackOrAsynccauses async run without a callback); an executable path to run the compiled assembly (defaults to the current process). IfcallbackOrAsyncis provided a function then it is called after the script has finished with theProcessInfoas the only argument. Over multiple runsRunScriptis faster than running the process manually and writing to StdIn because of assembly and compilation caching.
This function returns aProcessInfoobject encapsulating info and I/O for the process. Available properties:HasExited,ExitCode,ExitTime(YYYYMMDDHH24MISS),StdOut,StdErr,StdIn(asKeysharpFile). Available methods:Kill(). -
A_EventInfois not limited to positive values when reporting the mouse wheel scroll amount.- When scrolling up, the value will be positive, and negative when scrolling down.
-
New accessors:
-
A_AllowTimersreturns whether timers are allowed or not. It's also easier to set this value rather than callThread("NoTimers"). -
A_CommandLinereturns the command line string. This is preferred over passingGetCommandLinetoDllCall()as noted above. -
A_DefaultHotstringCaseSensitivereturns the default hotstring case sensitivity mode. -
A_DefaultHotstringConformToCasereturns the default hotstring case conformity mode. -
A_DefaultHotstringDetectWhenInsideWordreturns the default hotstring word detection mode. -
A_DefaultHotstringDoBackspacereturns the default hotstring backspacing mode. -
A_DefaultHotstringDoResetreturns the default hotstring resetting mode. -
A_DefaultHotstringEndCharRequiredreturns the default hotstring ending character mode. -
A_DefaultHotstringEndCharsreturns the default hotstring ending characters. -
A_DefaultHotstringKeyDelayreturns the default hotstring key delay length in milliseconds. -
A_DefaultHotstringNoMousereturns whether mouse clicks are prevented from resetting the hotstring recognizer because#Hotstring NoMousewas specified. -
A_DefaultHotstringOmitEndCharreturns the default hotstring ending character replacement mode. -
A_DefaultHotstringPriorityreturns the default hotstring priority. -
A_DefaultHotstringSendModereturns the default hotstring sending mode. -
A_DefaultHotstringSendRawreturns the default hotstring raw sending mode. -
A_DirSeparatorreturns the directory separator character which is\on Windows and/elsewhere. -
A_HasExitedreturns whether shutdown has been initiated. -
A_KeysharpCorePathprovides the full path to the Keysharp.Core.dll file. -
A_LoopRegValuewhich makes it easy to get a registry value when usingLoop Reg. -
A_MaxThreadsreturns the valuenspecified with#MaxThreads n. -
A_NoTrayIconreturns whether the tray icon was hidden with #NoTrayIcon. -
A_NowMs/A_NowUTCMsreturns the current local/UTC time formatted to include milliseconds like so "YYYYMMDDHH24MISS.ff".- These can be used with
DateAdd()/DateDiff()using"L"for theTimeUnitsparameter.
- These can be used with
-
A_SuspendExemptreturns whether subsequent hotkeys and hotstrings will be exmpt from suspension because#SuspendExempt truewas specified. -
A_TotalScreenHeightreturns the total height in pixels of the virtual screen. -
A_TotalScreenWidthreturns the total width in pixels of the virtual screen. -
A_UseHookreturns the valuenspecified with#UseHook n. -
A_WinActivateForcereturns whether the forceful method of activating a window is in effect because#WinActivateForcewas specified. -
A_WorkAreaHeightreturns the height of the working area of the primary screen. -
A_WorkAreaWidthreturns the width of the working area of the primary screen.
-
-
Log(number, base := 10)is by default base 10, but it can accept a double as the second parameter to specify a custom base. -
In
SetTimer():- The callback is passed the function object as the first argument, and the date/time the timer was triggered as a YYYYMMDDHH24MISS string for the second argument.
- This allows the handler to alter the timer by passing the function object back to another call to
SetTimer(). - Timers are not disabled when the program menu is shown.
-
A new timer function
EnabledTimerCount()which returns the number of currently enabled timers in existence. -
Using an underscore
_to discard the result of an expression is supported the same way it is in C# like:_ := myfunc()
-
superis not restricted to being used within a class's code. It can be accessed outside of the class like so:
classobj := myclass()
classobj.super.a := 123
- Reference parameters for functions using
&are supported with the following improvements and caveats:- Passing class members, array indexes and map values by reference is supported.
func(&classobj.classprop)func(&myarray[5])func(&mymap["mykey"])
- Reference parameters in functions work for class methods, global functions, built in functions, lambdas and function objects.
- Lambdas with a single reference parameter can be declared with no parentheses:
lam := &a => a := (a * 2)
- Lambdas with a single reference parameter can be declared with no parentheses:
- For an argument to be passed as a reference, the function parameter in that position must be declared as a reference:
func(&p1) { }
- Reference parameters cannot be optional because C# does not support it.
func(p1, &p2 := 0) {} ; not supportedfunc(p1, &p2) {} ; supported
- When passing a class member variable as a dynamic reference to a function from within another function of that same class, the
thisprefix must be used:
- Passing class members, array indexes and map values by reference is supported.
class myclass
{
x := 11
y11 := 0
myclassreffunc(&val)
{
}
callmyclassreffunc()
{
myclassreffunc(&this.y%x%) ; Use this.
}
}
- New functions for encrypting/decrypting an object:
- Encrypt or decrypt an object using the AES algorithm:
AES(value, key, decrypt := false) => Array. - Generate hash values using various algorithms:
MD5(value) => String,SHA1(value) => String,SHA256(value) => String,SHA384(value) => String,SHA512(value) => String. - Calculate the CRC32 polynomial of an object:
CRC32(value) => Integer. - Generate a secure cryptographic random number:
SecureRandom(min, max) => Decimal.
- Encrypt or decrypt an object using the AES algorithm:
- New class and functions for managing real threads which are not related to the green threads that are used for the rest of the project.
- A
RealThreadis created by callingStartRealThread().
- A
class RealThread
{
RealThread(Task)
RealThread ContinueWith(funcobj [, params*]) => RealThread ; Call `funcobj` after the task completes, optionally passing `params` to it and return a new `RealThread` object for the continuation thread.
Wait([timeout]) ; Wait until the thread object which was passed to the constructor completes. Optionally return after a specified timeout period in milliseconds elapses.
}
+ `StartRealThread(funcobj [, params*]) => RealThread` Call `funcobj` in a real thread, optionally passing `params` to it, and return a `RealThread` object.
+ `LockRun(lockobj, funcobj [, params*])` Call `funcobj` inside of a lock on `lockobj`, optionally passing `params` to it.
+ `lockobj` must be initialized to some value, such as an empty string.
FuncObj(),IsFunc(),Any.HasProp()andObjBindMethod()take a new optional parameter which specifies the parameter count of the method to search for.- The new signatures are:
ObjBindMethod(obj [, paramCount , method, params]) => FuncObjFuncObj(name, object [, paramCount]) => FuncObjIsFunc(functionName [, paramCount]) => IntegerAny.HasProp(propName [, paramCount]) => Integer- The only properties which can have parameters are the
__Item[]indexer properties.
- The only properties which can have parameters are the
- This is needed to resolve the proper overloaded method.
- Omit
paramCountor pass -1 to just use the first encountered method on the specified object with the specified name.
- The new signatures are:
KeysharpObjecthas a new methodOwnPropCount()which corresponds to the global functionObjOwnPropCount().ComObjConnect()takes an optional third parameter as a boolean (default:false) which specifies whether to write additional information to the debug output tab when events are received.- New function
Mail(recipients, subject, message, options)to send an email.recipients: A list of receivers of the message.subject: Subject of the message.message: Message body.options: AMapwith any the following optional key/value pairs:- "attachments": A string or
Arrayof strings of file paths to send as attachments. - "bcc": A string or
Arrayof strings of blind carbon copy recipients. - "cc": A string or
Arrayof strings of carbon copy recipients. - "from": A string of comma separated from address.
- "replyto": A string of comma separated reply address.
- "host": The SMTP client hostname and port string in the form "hostname:port".
- "header": A string of additional header information.
- "attachments": A string or
- Preprocessor directives are supported using the familiar syntax of C#.
#if symbolis used to enable a section of code if symbol is defined.- By default, the following are defined:
WINDOWSif you are running the script on Microsoft Windows.LINUXif you are running the script on linux.KEYSHARP
#elsecan be used to take an alternate path if the preceding#ifevaluates to false.#elif symbolcan be used to evaluate another symbol if the preceding#ifor#elifevaluate to false.- All preprocessor blocks must end with an
#endif - New preprocessor symbols can be defined using
#define symbol. - Logical statements can be evaluated using the operators
&&,||and!. - Evaluation of preprocessor statements are case insensitive.
- Some examples are:
#if WINDOWS
MsgBox("Windows")
#elif LINUX
MsgBox("linux")
#else
MsgBox("Unsupported OS")
#endif
#if !(WINDOWS || LINUX)
MsgBox("Unsupported OS")
#endif
#if 1
MsgBox("Always true")
#endif
#if 0
MsgBox("Always false")
#endif
#define NEW_DEFINE
#if NEW_DEFINE
MsgBox("True because of new definition")
#endif
- Command line switches may start with either
/(Windows-only),-or--. - Command line switches
--script
Causes a compiled script to ignore its main code and instead executes the provided script. For this to apply,--scriptmust be the first command line argument.
Example:CompiledScript.exe /script /ErrorStdOut MyScript.ahk "Script's arg 1"--version,-v
Displays Keysharp version.--codeout
In addition to running the script, Keysharp outputs a .cs file with the same name as the script containing the code which was used to compile. This is the same code displayed in Keyview.--exeout
In addition to running the script, Keysharp outputs a .exe file which can be ran as standalone from Keysharp (but still requires .NET 9).--minimalexeout
Same as--exeoutbut the number of file dependencies is reduced by embedding them in Scriptname.dll. The resulting program will have five dependencies: Scriptname.exe, Scriptname.dll, Keysharp.Core.dll, Scriptname.deps.json, and Scriptname.runtime.config. To get a truly single-file executable the script must be compiled as a C# project, for example as Keysharp.OutputTest in the Keysharp solution.--validate
Compiles but does not run the script. Can be used to check for load-time errors.--assembly [Type Method]
Reads pre-compiled assembly code from the file or StdIn and runs it. Optionally also provide the entrypoint type and method, but if omitted then the default typeKeysharp.CompiledMain.programand methodMainare used.
- Nested classes are not supported.
- Nested functions are not supported.
VarSetStrCapacity()andObjGet/SetCapacity()have been removed because C# manages its own memory internally.ListLines()is omitted because C# doesn't support it.ObjPtr()is not implemented because objects can be moved by the GC.- There is no such thing as dereferencing in C#, so the
*dereferencing operator is not supported. - The
R,DnorTnparameters inFormatTime()are not supported, except for 0x80000000 to disallow user overrides.- If you want to specify a particular format or order, do it in the format argument. There is no need or reason to have one argument alter the other.
- Here is a list of the C# style DateTime formatters which are supported.
- Static text controls do not send the Windows
API WM_CTLCOLORSTATIC (0x0138)message to their parent controls like they do in AHK. - Renaming Keysharp.exe to run a specific script by default will not work.
- Double click handlers for buttons are not supported.
- UpDown controls with paired buddy controls are not supported. Keysharp just uses the regular NumericUpDown control in C#.
- The options
16,HorzandWraphave no effect. - The min and max values cannot be swapped.
- The options
IL_Create()only takes one parameter:largeIcons.initialCountandgrowCountare no longer needed because memory is handled internally.LoadPicture()does not accept aGDI+argument as an option.- For slider events, the second parameter passed to the event handler will always be
0because it's not possible to retrieve the method by which the slider was moved in C#. PixelGetColor()ignores themodeparameter.DirSelect():- The
1,3and5options don't apply and the New Folder button will always be shown. - Modality cannot be configured with
Gui.Opt("+OwnDialogs")because the folder select dialog is always modal. - Restricting folder navigation is not supported.
- The
MsgBox():- The modality options are ignored.
- The message box will block the window that launched it by default. If
+OwnDialogsis in effect, then all GUIs in the script are blocked until it is dismissed. - System modal dialog boxes are no longer supported on Windows.
- The help option
16384is ignored.
- Only
Tab3is supported, no older tab functionality is present. - When adding a
ListView, theCountoption is not supported because C# can't preallocate memory for aListView. - Function references are supported, but the VarRef object is not supported.
- The address of a variable cannot be taken using the reference operator except when passing an argument to a function.
x := &var ; not supportedfuncthattakesref(&x) ; supported
OnMessage()doesn't observe any of the behavior mentioned in the documentation regarding the message check interval because it's implemented in a different way.- A GUI object is required for
OnMessage()to be used.
- A GUI object is required for
- Pausing a script is not supported because a Keysharp script is actually a running program.
- The pause menu item and
Pause()function have been removed.
- The pause menu item and
ObjAddRef()andObjPtrAddRef()do not have an effect for non-COM objects. Instead, use the following:newref := theobj ; adds 1 to the reference countnewref := "" ; subtracts 1 from the reference count
#Warnto enable/disable compiler warnings is not supported yet.- The
/scriptoption for compiled scripts does not apply and is therefore not implemented. - The Help and Window Spy menu items are not implemented yet.
Download()only supports the*0option, and not any other numerical values.- Static class variables cannot be overridden in subclasses. So regardless of the class used to access the variable, they all refer to the same static member variable.
- Static class member variable initializers like
static x.y := 42are not supported. Instead, just initialize in one step likestatic x := { y, 42 }. - Within a class, a property and a method cannot have the same name. However, they can if one is in a base class and the other is in a subclass.
- The concept of a class prototype is not supported, because it doesn't exist in C# classes. Thus, there is no
.Prototypemember. - Properties other than
__Item[]cannot take parameters. If you need to pass a parameter, use a method instead.- This also applies to properties which have been dynamically defined with
DefineProp().
- This also applies to properties which have been dynamically defined with
- Static
__Item[]properties are not allowed, only instance__Item[]properties. This is because C# does not support static indexers. - When passing
"Interrupt"as the first argument toThread(), the third argument forLineCountis not supported because Keysharp does not support line level awareness. - Tooltips do not automatically disappear when clicking on them.
- The initial IronAHK developers 2010 - 2015
- Logical string comparison, cddl 1.0
- Cross platform INI file processor
- P/Invoke calls
- Tuple splatter
- Semver version parsing
- PictureBox derivation
- Using SendMessage() with string
- Program icon is a derivative of work by Bamicon
- NAudio
- Scintilla editor for .NET
- Scintilla setup code in Keyview
- Various posts on Stack Overflow
Please make an account here and post a ticket.