'Inno Setup - External .NET DLL with dependencies

I am trying to use a custom DLL in a Inno Setup script during installation. I wrote a very simple function that basically checks a connection string for a MySQL database using MySQL .NET connector (there is no MySQL client on the target server). The code of this exported function is:

public class DbChecker
{
    [DllExport("CheckConnexion", CallingConvention.StdCall)]
    public static int CheckConnexion([MarshalAs(UnmanagedType.LPStr)] string connexionString)
    {
        int success;
        try
        {
            MySqlConnection connection = new MySqlConnection(connexionString);
            connection.Open();
            connection.Close();
            success = 0;
        }
        catch (Exception)
        {
            success = 1;
        }
        return success;
    }
}

The function is imported this way in Inno Setup :

[Files]
Source: "..\..\MyDll\bin\x86\Release\*"; Flags: dontcopy;

and

[Code]
function CheckConnexion(connexionString: AnsiString): Integer;
external 'CheckConnexion@files:MyDll.dll,MySql.Data.dll stdcall setuponly loadwithalteredsearchpath';`

The problem is that the setup throws an exception at runtime:

Runtime Error (at 53:207):

External exception E0434352.

I think I have to use the files prefix because the function is called in the NextButtonClick event handler, before files are copied to the {app} directory.

Both MyDll.dll and MySql.Data.dll are correctly extracted to the {tmp} directory at runtime.

I tried both with and without the loadwithalteredsearchpath flag with the same result.

What I found is that this error code is a generic .NET runtime error code.

If I remove the part using MySql.Data it works perfectly fine (except that it does nothing...)

As advised on other threads I've been trying to log the error in my .NET code using EventLog and UnhandledException but I have the same exception no matter what (and no log source is created), even without the MySQL part. I checked EventLog permissions on my computer.

It seems that the exception is thrown as soon as I use anything else that "basic" C# code (whenever I try to load another DLL).



Solution 1:[1]

There is probably a better way, but this will do.

Implement an initialization function (Init here) that sets up AppDomain.AssemblyResolve handler that looks for an assembly in the path of the main (executing) assembly:

[DllExport("Init", CallingConvention.StdCall)]
public static void Init()
{
    AppDomain currentDomain = AppDomain.CurrentDomain;
    currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);
}

private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
    string location = Assembly.GetExecutingAssembly().Location;
    AssemblyName name = new AssemblyName(args.Name);
    string path = Path.Combine(Path.GetDirectoryName(location), name.Name + ".dll");
    if (File.Exists(path))
    {
        return Assembly.LoadFrom(path);
    }
    return null;
}

Import it to the Inno Setup:

procedure Init(); external 'Init@files:MyDll.dll stdcall setuponly';

And call it before calling the function that needs the dependency (CheckConnexion).


Another solution might be this:
Embedding DLLs in a compiled executable


Btw, no need for the loadwithalteredsearchpath flag. It has no effect on .NET assemblies imo. They are needed for native DLL dependencies: Loading DLL with dependencies in Inno Setup fails in uninstaller with "Cannot import DLL", but works in the installer.

Solution 2:[2]

I found something else that might be helpful for anyone stumbling upon this page.

In my scenario, I have several C# methods that I call from InnoSetup using DllExport. In one of those methods, I call another of the methods. This caused Inno to throw "External exception E0434352".

If I moved the code to a method not called by InnoSetup, everything worked fine.

So...

[DllExport("Fu", CallingConvention = CallingConvention.StdCall)]
public static int Fu()
{
    // Stuff
}

[DllExport("Bar", CallingConvention = CallingConvention.StdCall)]
public static int Bar()
{
    Fu();
}

...causes InnoSetup to cry, but:

[DllExport("Fu", CallingConvention = CallingConvention.StdCall)]
public static int Fu()
{
    LocalFu();  
}

private static int LocalFu()
{
    // Stuff
}

[DllExport("Bar", CallingConvention = CallingConvention.StdCall)]
public static int Bar()
{
    // Stuff
    LocalFu();
    // Other stuff
}

...is fine.

I don't know if this is caused by Inno or DllExport, so I'll forgo direct derision and blame society as a whole for my lost morning. (Or myself for being a new to this thing.)

Solution 3:[3]

I would like to expand upon Martin's answer. There is a way to resolve the assemblies without having to call an Init method first and that is by including a static constructor in your .NET class:

public class MyClass
{
    static MyClass()
    {
        AppDomain currentDomain = AppDomain.CurrentDomain;
        currentDomain.AssemblyResolve += MyResolveEventHandler;
    }

    private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
    {
        var location = Assembly.GetExecutingAssembly().Location;
        var assemblyName = new AssemblyName(args.Name);
        var path = Path.Combine(Path.GetDirectoryName(location), assemblyName.Name + ".dll");
        if (File.Exists(path))
        {
            return Assembly.LoadFrom(path);
        }
        return null;
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 GordoFabulous
Solution 3 breez