src/mdlldk

Source   Edit  

The mdlldk is a Dynamic-link libraries (DLLs) Development Kit for mIRC. The package brings templates that add the standard procedures of an mIRC dll, such as: LoadDll() and UnloadDll(); as well as facilitating the export of procedures, as it automatically creates the .def file with the symbols.

The package adds procedures that help in the development of your dll. All added procedures can be seen here.

The newProcToExport() template, which adds a procedure exported to dll and creates an entire abstraction to enable it to work in both unicode and non-unicode mIRC, that is, from version 5.6 (5.60), when support for dlls was added, up to the latest known version of mIRC. If you choose to use newProcToExport(), it will not be necessary to manually fill in the data or parms parameters, as this is done automatically, safely and without exceeding the size allocated by mIRC in the pointers and if it exceeds, it will be truncated to the limit and avoids mIRC crashes. This "magic" is done at runtime and according to each mIRC version, as the memory size allocated to the data and parms pointers has changed with the mIRC versions.

There are also the newProcToExportW() and newProcToExportA() templates, which also add an exported procedure to dll, but at a lower level than newProcToExport(). In the first template the parameters data and parms will be of type WideCString, while in the second they will be cstring. Even if you use one of these two templates you can also take advantage of safe copying to data and parms using mToWideCStringAndCopy() or mToCStringAndCopy().

Add an alias to a procedure already declared with the addAliasFor() template.

Generate your dll documentation file using nim doc. All documentation comments at the top of the body of calls to addLoadProc(), addUnloadProc(), newProcToExport(), newProcToExportW(), newProcToExportA() and addAliasFor() are passed to the documentation generated by Nim.

Finally, the exportAllProcs() template facilitates the process of exporting procedures to dll, as it generates the .def file with all the symbols that must be exported and links to the dll during the linking process. exportAllProcs() must be called at the end of your source code to work correctly.

For more information see the documentation below.

Current support

Currently supported with the gcc, clang and vcc compilers, and the C and C++ backends. It is advised to use the last version of Nim or the devel version.

Documentation used as a reference

Basic Use

This is a basic commented example:

# test.nim
# Import the mdlldk package.
import pkg/mdlldk

# Adds procedure LoadDll().
addLoadProc(false, true):
  ## The dll will be unloaded right after use and the communication between
  ## mIRC and dll will be by the use of unicode strings.
  discard

# Adds procedure UnloadDll().
addUnloadProc(RAllowUnload):
  ## The dll is allowed to unloaded.
  discard

# Adds the `info` procedure, which can be called from mIRC.
newProcToExport(info):
  ## When called, it executes a command that prints a message about the dll
  ## in the active window.
  ##
  ## Usage: `/dll test.dll info`
  result.outData = "echo -a test.dll made in Nim " & NimVersion & " for mIRC"
  result.ret = RCommand

# It must be added to the last line of your Nim code to correctly export all
# symbols to the dll.
exportAllProcs()
The above code should be compiled as follows:

nim c --app:lib --cpu:i386 --mm:orc --noMain -d:noRes -d:useMalloc -d:release test.nim

To learn more about compiler options, visit https://nim-lang.org/docs/nimc.html.

In case you want to produce a smaller dll, you can add such switches:

nim c --app:lib --cpu:i386 --mm:arc --noMain -d:noRes -d:useMalloc -d:danger -d:strip -d:lto --threads:off --opt:size test.nim

With this last line my generated dll had only 11.5KB against 81,5KB of the other one, using the Nim 2.0.2 and clang 18.1.2 (i686-w64-windows-gnu) compilers.

Procs

proc addProcToExport(name: string; size: int) {.compileTime, ...raises: [],
    tags: [], forbids: [].}

Adds procedure name, which has size bytes in the parameter list, to be exported to the dll.

Notes

  • Procedures called by mIRC have 24 bytes in the parameter list.
  • Don't forget to call the exportAllProcs() template at the end of your Nim code.
  • Should not use this proc to add procedures created with newProcToExport(), newProcToExportW() and newProcToExportA().
Source   Edit  

Templates

template addAliasFor(procName, aliasName, comments: untyped)

Adds an alias aliasName to a procedure procName already exported to the dll. comments brings the body, in which you must place the documentation comment, which will be added when using nim doc file.nim.

newProcToExport(foo):
  result.outData = "OK"
  result.ret = RReturn

addAliasFor(foo, bar):
  ## Is an alias for `foo`.

Source   Edit  
template addLoadProc(keepLoaded, strUnicode: bool; body: untyped)
Adds and exports the LoadDll() procedure, which is called when the dll is loaded in mIRC from version 5.8 onwards. In addition to adding the LoadDll() procedure, which captures and stores the information passed by mIRC to the dll, through the LoadInfo object, it makes some corrections that facilitate the development of the dll, such as:
  • determine the total bytes value allocated to the data and parms parameters according to the mIRC version and string type (WideCString or cstring), since the LoadInfo object did not always pass such information.
  • to correct and determine the version passed in the mVersion field, of the LoadInfo object, because in some versions the major version of mIRC was passed as 0 and after version 6.21 mIRC started to adopt the filling with zero to the right of the minor version, which was not done previously and caused ambiguity between versions.

Template parameters

  • keepLoaded sets the mKeep field of the LoadInfo object, if possible. If it is true it will keep the dll loaded after being called, however, if it is false, the dll will be unloaded right after use. See mKeep field in LoadInfo.
  • strUnicode sets the mUnicode field of the LoadInfo object, if possible. If it is true the communication between mIRC and dll will be by the use of unicode strings (WideCString), however, if it is false, the communication will be by ANSI strings (cstring). See mUnicode field in LoadInfo.
  • body passes a code block that will be appended to the end of the LoadDll() procedure. If you didn't want to pass any code, use the discard keyword.
Source   Edit  
template addUnloadProc(unused: UnloadReturn; body: untyped)

Adds and exports the UnloadDll() procedure, which is called when mIRC unloads the dll from version 5.8 onwards.

Template parameters

  • unused determines if the dll will remain loaded or if it can be unloaded by mIRC when not in use for ten minutes. See UnloadReturn.
  • body passes a code block that will be appended to the end of the UnloadDll() procedure. If you didn't want to pass any code, use the discard keyword.

Notes

  • The result return variable is exposed and can be accessed and modified in body.
  • The mTimeout parameter is marked as {.inject.} and can be accessed in body. Is of type UnloadMode. See UnloadMode.
Source   Edit  
template aliasFor(procname: string = "") {.pragma.}
Annotates the alias procedure documentation with the pragma aliasFor: "procname", where procname is the name of the aliased procedure. Source   Edit  
template exportAllProcs()
This template must be called at the end of your Nim code so that the .def file can be created with the name of all the procedures that must be exported to the dll. Source   Edit  
template newProcToExport(procname, body: untyped)

This template facilitates the creation of a new procedure, with the name procname, to export to dll, as it creates an abstraction layer that makes it possible to work in unicode as well as non-unicode mIRC, that is, it allows for a retroportability up to the mIRC version 5.6, when dll support was added.

Also, in this case the data parameter is of type string, and the show and nopause parameters are of type bool, providing better compatibility with Nim.

There is also no need to manipulate pointers to copy command and parameter return into data and parms, as this is done in the abstraction layer and truncates them, if necessary, to avoid crashing mIRC when the strings are longer than the allocated pointers.

Template parameters

  • procname is the name of the procedure that will be exported to the dll.
  • body passes a code block that will be attached to the internal procedure that is called by the procname procedure.

The internal procedure

proc internalProc(mWnd {.inject.}, aWnd {.inject.}: HWND,
                  data {.inject.}: string,
                  show {.inject.}, nopause {.inject.}: bool):
                 tuple[ret: ProcReturn, outData, outParms: string]
                 {.nimcall, gensym.} =
  body
Parameters marked with {.inject.} are accessed through body. See below the description of each parameter. Already the return value of the internal procedure is tuple[ret: ProcReturn, outData, outParms: string] and will be explained below.

Parameters accessible in body

  • mWnd is the handle to the main mIRC window. It is of type HWND, which is a int.
  • aWnd is the handle of the window in which the command is being issued, this might not be the currently active window if the command is being called by a remote script. It is of type HWND, which is a int.
  • data is the information sent to the dll. It is of type string.
  • show is false if the . prefix was specified to make the command quiet, or true otherwise. It is of type bool.
  • nopause is true if mIRC is in a critical routine and the dll must not do anything that pauses processing in mIRC, eg. the dll should not pop up a dialog. It is of type bool.

The fields of the internal procedure's return tuple

  • ret indicates what mIRC should do. See ProcReturn.
  • outData can be filled in with a command you want mIRC to perform if any.
  • outParms can be filled in with the parameters you want mIRC to use when performing the command passed in outData.
Source   Edit  
template newProcToExportA(procname, body: untyped)

This template makes it easy to create a new procedure to export to the dll, but at a lower level than newProcToExport(). In this case the data and parms parameters are of type cstring.

The `procname`

proc procname(mWnd {.inject.}, aWnd {.inject.}: HWND,
              data {.inject.}, parms {.inject.}: cstring,
              show {.inject.}, nopause {.inject.}: BOOL):
             ProcReturn {.stdcall, exportc.} =
  body
Parameters marked with {.inject.} are accessed through body. See below the description of each parameter. Already the return value is ProcReturn. See ProcReturn.

`procname` parameters

  • mWnd is the handle to the main mIRC window. It is of type HWND, which is a int.
  • aWnd is the handle of the window in which the command is being issued, this might not be the currently active window if the command is being called by a remote script. It is of type HWND, which is a int.
  • data is the information that you wish to send to the dll. On return, the dll can fill this variable with the command it wants mIRC to perform if any. It is of type cstring.
  • parms is filled by the dll on return with parameters that it wants mIRC to use when performing the command that it returns in the data variable. It is of type cstring.
  • show is FALSE (0) if the . prefix was specified to make the command quiet, or TRUE (1) otherwise. It is of type BOOL, which is a int32.
  • nopause is TRUE (1) if mIRC is in a critical routine and the dll must not do anything that pauses processing in mIRC, eg. the dll should not pop up a dialog. It is of type BOOL, which is a int32.
Source   Edit  
template newProcToExportW(procname, body: untyped)

This template makes it easy to create a new procedure to export to the dll, but at a lower level than newProcToExport(). In this case the data and parms parameters are of type WideCString.

The `procname` procedure

proc procname(mWnd {.inject.}, aWnd {.inject.}: HWND,
              data {.inject.}, parms {.inject.}: WideCString,
              show {.inject.}, nopause {.inject.}: BOOL):
             ProcReturn {.stdcall, exportc.} =
  body
Parameters marked with {.inject.} are accessed through body. See below the description of each parameter. Already the return value is ProcReturn. See ProcReturn.

`procname` parameters

  • mWnd is the handle to the main mIRC window. It is of type HWND, which is a int.
  • aWnd is the handle of the window in which the command is being issued, this might not be the currently active window if the command is being called by a remote script. It is of type HWND, which is a int.
  • data is the information that you wish to send to the dll. On return, the dll can fill this variable with the command it wants mIRC to perform if any. It is of type WideCString.
  • parms is filled by the dll on return with parameters that it wants mIRC to use when performing the command that it returns in the data variable. It is of type WideCString.
  • show is FALSE (0) if the . prefix was specified to make the command quiet, or TRUE (1) otherwise. It is of type BOOL, which is a int32.
  • nopause is TRUE (1) if mIRC is in a critical routine and the dll must not do anything that pauses processing in mIRC, eg. the dll should not pop up a dialog. It is of type BOOL, which is a int32.
Source   Edit