G1ANT - disposing of unmanaged code in C# macros - c#

I am enjoying using G1ANT's "macros" capability to call unmanaged code, but the unmanaged objects are of course not being automatically garbage collected absent code to do it.
My request is specifically for best practices in disposing of unmanaged code in these G1ANT C# macros, not for disposing of the same in C# generally, and it is not a request to fix the code below, which runs as is just fine.
If I were coding in C# using Visual Studio, I would likely use a System.Runtime.InteropServices.SafeHandle class, override the Finalize method, or use one of the other approaches in common use (see also this post on disposing of unmanaged objects in C#).
But none of these approaches appear to be a good fit for G1ANT macros per se, at least with my novice experience of them.
For illustration purposes I'm referring to this G1ANT code, but WITHOUT the last line in the macro (ahk.Reset()), because it runs fine with that line, more than once. (I'm painfully aware that there must be a much better example, but as I'm new to G1ANT, this is the only thing I have so far.) What I'm after is C# code that works in G1ANT when there is no explicit disposal of the unmanaged object:
addon core version 4.100.19170.929
addon language version 4.100.19170.929
-dialog ♥macrodlls
♥macrodlls = System.dll,System.Drawing.dll,System.Windows.Forms.dll,AutoHotkey.Interop.dll,System.Runtime.InteropServices.dll
-dialog ♥macrodlls
♥macronamespaces = System,AutoHotkey.Interop,System.Windows.Forms
⊂
var ahk = AutoHotkeyEngine.Instance;
//Load a library or exec scripts in a file
ahk.LoadFile("functions.ahk");
//execute a specific function (found in functions.ahk), with 2 parameters
ahk.ExecFunction("MyFunction", "Hello", "World");
string sayHelloFunction = "SayHello(name) \r\n { \r\n MsgBox, Hello %name% \r\n return \r\n }";
ahk.ExecRaw(sayHelloFunction);
//execute's newly made function\
ahk.ExecRaw(#"SayHello(""Mario"") ");
var add5Results = ahk.ExecFunction("Add5", "5");
MessageBox.Show("ExecFunction: Result of 5 with Add5 func is" + add5Results);
addon core version 4.100.19170.929
addon language version 4.100.19170.929
-dialog ♥macrodlls
♥macrodlls = System.dll,System.Drawing.dll,System.Windows.Forms.dll,AutoHotkey.Interop.dll,System.Runtime.InteropServices.dll,System.Reflection.dll,Microsoft.CSharp.dll
-dialog ♥macrodlls
♥macronamespaces = System,AutoHotkey.Interop,System.Windows.Forms,System.Reflection
⊂
var ahk = AutoHotkeyEngine.Instance;
//Load a library or exec scripts in a file
ahk.LoadFile("functions.ahk");
//execute a specific function (found in functions.ahk), with 2 parameters
ahk.ExecFunction("MyFunction", "Hello", "World");
string sayHelloFunction = "SayHello(name) \r\n { \r\n MsgBox, Hello %name% \r\n return \r\n }";
ahk.ExecRaw(sayHelloFunction);
//executes new function
ahk.ExecRaw(#"SayHello(""Mario"") ");
var add5Results = ahk.ExecFunction("Add5", "5");
MessageBox.Show("ExecFunction: Result of 5 with Add5 func is" + add5Results);
ahk.Reset();
⊃
⊃
It's taken nearly verbatim from the AutoHotkey.Interop github page.
Without the last line in the macro ('ahk.Reset()), the code runs perfectly the first time through, but on the second run G1ANT still sees the included AutoHotkey file, and warns of duplicate function definitions, but continues and still functions properly. The as-far-as-I-can-tell-undocumented AutoHotkey.Interop command Reset() takes care of the garbage collection problem by calling
public void Terminate()
{
AutoHotkeyDll.ahkTerminate(1000);
}
public void Reset() {
Terminate();
AutoHotkeyDll.ahkReload();
AutoHotkeyDll.ahktextdll("", "", "");
}
Thus, the AutoHotkeyEngine instance itself appears to be garbage collected, even without the ahk.Reset();, but the AutoHotkey script it loads into an object is not.
Stopping the G1ANT.Robot application and restarting, then reloading the script above (as mentioned, without the line ahk.Reset();), works just fine, but once again only for a single run.
Edit: The given answer's advice on treatment of singletons is what I will use henceforth when loading of AutoHotkey function scripts and the DLL itself. It seems prudent and good practice to check to see if the DLL or function file have been loaded, whether problems exist or not. "An ounce of prevention", etc. In addition, I have forked the AutoHotkey.Interop repo here, adding a boolean check to see if the AutoHotkeyEngine instance is ready.
Best regards,
burque505

You use AutoHotkeyEngine.Instance, so I guess it's a singleton. It will stay loaded in memory as long as the corresponding dll is kept there, and the latter is loaded and lives as long as the its domain lives. The macro app domain (the place where script stuff is placed) currently lives as long as Robot's app domain, so in fact your singleton instance lives as long as Robot.
Either:
don't use singleton,
or reset it right after obtaining the instance (kinda what you already did),
or treat it as a singleton that has life span longer than your app. In this case after obtaining singleton instance do a check if your functions file has been already loaded and only load it if it wasn't done already.

Related

Call method from interface or abstract class within static void

I'm usually a javascript developer, but for my company I just started learning c# in order to use the CimatronE 13 API to develop custom command line PDM tools for this 3D modelling software.
As I'm making progress understanding the programming language, there's this frustrating situation where I want to use an API endpoint method but I can't manage to get it working.
The Cimatron documentation says the following:
IPdm::GetRelatedDocuments
Syntax: RelatedDocuments = GetRelatedDocuments ( DocumentPath );
This method allows you to get related files from compound types of files, for example Assembly or Drawing.
Input: (String) DocumentPath,
Path to file. For example \Documents\Location\Folder\Document. The file must be Assembly or Drawing.
Return: (Variant) RelatedDocuments,
Variant type array each element of which contain two dimensioned string type array of files related to selected one.
This looks pretty straight forward to me, so I tried calling it in multiple ways from within the static void Main() method, but I keep getting errors:
var RelatedDocuments = interop.CimBaseAPI.IPdm.GetRelatedDocuments("path");
CS0120: An object reference is required for the non-static field, method, or property 'IPdm.GetRelatedDocuments(string)'
interop.CimBaseAPI.IPdm pdm = new interop.CimBaseAPI.IPdm();
var RelatedDocuments = pdm.GetRelatedDocuments("path");
CS0144: Cannot create an instance of the abstract class or interface 'IPdm'
Any ideas? It's probably simple but I'm still a noob with c# :p
EDIT:
Cimatron documentation about the interface interop.CimBaseAPI.IPdm:
Properties:
Get
Query (String, DocumentEnumType, DocumentEnumUnit )
Variant
Methods:
A lot, including Variant GetRelatedDocuments ( String )
As how I see it now... interop.CimatronE.IPdm is an interface and in order to use it's methods, we first need access to the Cimatron application. Using the application object, we can use it's methods to get the desired interfaces such as IPdm and use their methods.
The following code gives no errors from the compiler but does when executing. This seems to be related to version 13 of CimatronE, since the application object works just fine using version 12. A lot has changed between these versions which I think is the reason the API is not functioning properly, outdated.
interop.CimAppAccess.AppAccess AppAcc = new interop.CimAppAccess.AppAccess();
interop.CimatronE.IApplication CimApp = /*(interop.CimatronE.IApplication)*/AppAcc.GetApplication();
interop.CimatronE.IPdm pdm = CimApp.GetPdm();
var RelatedDocuments = pdm.GetRelatedDocuments("path");
Console.WriteLine(RelatedDocuments);
Please correct me if I'm wrong! (since I just started and still learning c#)
I ran into this same issue with Cimatron 14.
I needed to make some changes in Visual Studio for things run properly with Cimatron.
Run Visual Studio in administrator mode
Set your Debug & Release Solution Platform to 'x64'
It was also recommended to point the build path for release & debug to the same folder as the Cimatron references. In my case 'C:\Program Files\3D Systems\Cimatron\14.0\Program'. However my code appears to run fine without this.
I created the Cimatron Application with this code (VB.Net):
Dim gAppAccess As New CIMAPPACCESSLib.AppAccess 'Define an AppAccess object to get running active application
Dim gApp As CIMAPPACCESSLib.Application 'Define an Application object
gApp = gAppAccess.GetApplication 'Getting running active application
If gApp Is Nothing Then
gApp = New CIMAPPACCESSLib.Application 'Creating a new instance of a Cimatron application
End If
References: Interop.CIMAPPACCESSLib.dll & interop.CimServicesAPI.dll
It is my understanding that Cimatron 15 may also requires some manifest changes.
There is some help information in the Cimatron program under Cimatrom Modules > Cimaton SDK that may be mildly helpful.

How to load referenced script libraries from a Roslyn script file?

I figured out I cannot load one script library from another easily:
module.csx
string SomeFunction() {
return "something";
}
script.csx
ExecuteFile("module.csx");
SomeFunction() <-- causes compile error "SomeFunction" does not exist
This is because the compiler does not know of module.csx at the time it compiles script.csx afaiu. I can add another script to load the two files from that one, and that will work. However thats not that pretty.
Instead I like to make my scripthost check for a special syntax "load module" within my scripts, and execute those modules before actual script execution.
script.csx
// load "module.csx"
SomeFunction()
Now, with some basic string handling, I can figure out which modules to load (lines that contains // load ...) and load that files (gist here https://gist.github.com/4147064):
foreach(var module in scriptModules) {
session.ExecuteFile(module);
}
return session.Execute(script)
But - since we're talking Roslyn, there should be some nice way to parse the script for the syntax I'm looking for, right?
And it might even exist a way to handle module libraries of code?
Currently in Roslyn there is no way to reference another script file. We are considering moving #load from being a host command of the Interactive Window to being a part of the language (like #r), but it isn't currently implemented.
As to how to deal with the strings, you could parse it normally, and then look for pre-processor directives that are of an unknown type and delve into the structure that way.
Support for #load in script files has been added as of https://github.com/dotnet/roslyn/commit/f1702c.
This functionality will be available in Visual Studio 2015 Update 1.
Include the script:
#load "common.csx"
...
And configure the source resolver when you run the scripts:
Script<object> script = CSharpScript.Create(code, ...);
var options = ScriptOptions.Default.WithSourceResolver(new SourceFileResolver(new string[] { }, baseDirectory));
var func = script.WithOptions(options).CreateDelegate()
...

Prevent detouring functions. Avoid cracking

There is an application that checks for activation using DLL Check function. Check returns 1 if application is activated and 0 otherwise. I create simple application and DLL containing function MyCheck (which always returns 1) with the same signature and detoured Check function with my version using MS detours lib for function hooking. Obviously it works and the application is successfully cracked, so I need to avoid it.
I tried to call Check function directly (by specifying exact address), without even using GetProcAddress, but looks like detours lib is modifying the function body itself, not export table.
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate bool CheckFunctionDelegate();
static void Main(string[] args)
{
ProcessModule module = Process.GetCurrentProcess().Modules
.Cast<ProcessModule>()
.First(m => m.ModuleName == "licensing_check.dll");
IntPtr procedurePtr = IntPtr.Add(module.BaseAddress, 0x00003FF0);
// Calling validation function by pointer
CheckFunctionDelegate checkFunction = (CheckFunctionDelegate)
Marshal.GetDelegateForFunctionPointer(procedurePtr, typeof(CheckFunctionDelegate));
if (checkFunction())
{
// do some stuff
}
}
}
Then I tried to read function body and I see that after detour MD5 checksum differs from the original one. So I'm trying to read entire contents of DLL in memory and check it to confirm that DLL contents are not changed, but it doesn't work either. It throws AccessViolationException.
Process.EnterDebugMode();
ProcessModule module = Process.GetCurrentProcess().MainModule;
byte[] data = new byte[module.ModuleMemorySize];
Marshal.Copy(module.BaseAddress, data, 0, module.ModuleMemorySize);
I used MainModule here, but it gives the same error for each module in Process.GetCurrentProcess().Modules collection.
I would appreciate any help on this, I'm not necessarily expecting to solve it in one of the ways I describe, any good solution is acceptable.
Thanks.

Determining interop function caller

I'm exposing a C# class to COM using these attributes:
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
[GuidAttribute("2325EBEB-DB5F-4D29-B220-64845379D9C5")]
[ComSourceInterfaces(typeof(WrapperEvents))]
in this class I have a function:
public void shutdownService()
This function is meant to be called just once from a VB6 client via COM Interop. Everything works fine. But somehow, it's being called more than once. My C# codes doesn't call this function directly. So I'm guessing the problem is in VB6 code. Unfortunately, that's not what the VB6 team thinks.
Is there a way to determine the caller of this function, ie. from my C#code or the VB6 code?
Right now I'm using a simple function to get the stacktrace:
public void LogStack()
{
var trace = new System.Diagnostics.StackTrace();
foreach (var frame in trace.GetFrames())
{
var method = frame.GetMethod();
if (method.Name.Equals("LogStack")) continue;
logger.Debug(string.Format("LogStack: {0}::{1}",
method.ReflectedType != null ? method.ReflectedType.Name : string.Empty, method.Name));
}
}
Obviously, I got somthing like this on the log:
2011-12-23 08:28:40,067 1 DEBUG (null) LogStack: Service::shutdownService
Since the only line of LogStack is the COM exposed function, I assume it's being called from vb6. But that's not enough proof for the VB6 team. Any idea how to really prove where function ?
You can try several things:
set a breakpoint in your code to trigger the debugger, then look at the call stack.
You could do an application dump here from visual studio and send it to them or screenshot the stack.
ex. Debugger.Break
http://www.netsplore.com/PublicPortal/blog.aspx?EntryID=12
Dump with "Savre Dump As"
http://msdn.microsoft.com/en-us/library/d5zhxt22.aspx
Use the com tracing
from a system level see
http://support.microsoft.com/kb/926098
I also recall a tool being installed with visual studio 6 do to this as well

Why do 'requires' statements fail when loading (iron)ruby script via a C# program?

IronRuby and VS2010 noob question:
I'm trying to do a spike to test the feasibility of interop between a C# project and an existing RubyGem rather than re-invent that particular wheel in .net. I've downloaded and installed IronRuby and the RubyGems package, as well as the gem I'd ultimately like to use.
Running .rb files or working in the iirb Ruby console is without problems. I can load the both the RubyGems package, and the gem itself and use it, so, at least for that use case, my environment is set up correctly.
However, when I try to do the same sort of thing from within a C# (4.0) console app, it complains about the very first line:
require 'RubyGems'
With the error:
no such file to load -- rubygems
My Console app looks like this:
using System;
using IronRuby;
namespace RubyInteropSpike
{
class Program
{
static void Main(string[] args)
{
var runtime = Ruby.CreateRuntime();
var scope = runtime.ExecuteFile("test.rb");
Console.ReadKey();
}
}
}
Removing the dependencies and just doing some basic self-contained Ruby stuff works fine, but including any kind of 'requires' statement seems to cause it to fail.
I'm hoping that I just need to pass some additional information (paths, etc) to the ruby runtime when I create it, and really hoping that this isn't some kind of limitation, because that would make me sad.
Short answer: Yes, this will work how you want it to.You need to use the engine's SetSearchPaths method to do what you wish.
A more complete example
(Assumes you loaded your IronRuby to C:\IronRubyRC2 as the root install dir)
var engine = IronRuby.Ruby.CreateEngine();
engine.SetSearchPaths(new[] {
#"C:\IronRubyRC2\Lib\ironruby",
#"C:\IronRubyRC2\Lib\ruby\1.8",
#"C:\IronRubyRC2\Lib\ruby\site_ruby\1.8"
});
engine.Execute("require 'rubygems'"); // without SetSearchPaths, you get a LoadError
/*
engine.Execute("require 'restclient'"); // install through igem, then check with igem list
engine.Execute("puts RestClient.get('http://localhost/').body");
*/
Console.ReadKey();

Categories