Inno Setup - внешняя.NET DLL с зависимостями
Я пытаюсь использовать пользовательские библиотеки DLL в сценарии установки Inno во время установки. Я написал очень простую функцию, которая в основном проверяет строку подключения для базы данных MySQL, используя коннектор MySQL .NET (на целевом сервере нет клиента MySQL). Код этой экспортируемой функции:
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;
}
}
Функция импортируется таким образом в Inno Setup:
[Files]
Source: "..\..\MyDll\bin\x86\Release\*"; Flags: dontcopy;
а также
[Code]
function CheckConnexion(connexionString: AnsiString): Integer;
external 'CheckConnexion@files:MyDll.dll,MySql.Data.dll stdcall setuponly loadwithalteredsearchpath';`
Проблема состоит в том, что установка выдает исключение во время выполнения:
Ошибка выполнения (в 53:207):
Внешнее исключение E0434352.
Я думаю, что я должен использовать files
префикс, потому что функция вызывается в NextButtonClick
обработчик событий, перед тем как файлы будут скопированы в {app}
каталог.
И то и другое MyDll.dll
а также MySql.Data.dll
правильно извлечены в {tmp}
каталог во время выполнения.
Я пробовал как с, так и без loadwithalteredsearchpath
флаг с тем же результатом.
Я обнаружил, что этот код ошибки является общим кодом ошибки.NET во время выполнения.
Если я удаляю часть, используя MySql.Data
он отлично работает (кроме того, что ничего не делает...)
Как советовали в других темах, я пытался записать ошибку в мой код.NET, используя EventLog
а также UnhandledException
но у меня есть одно и то же исключение, несмотря ни на что (и источник журналов не создается), даже без части MySQL. Я проверил разрешения EventLog на моем компьютере.
Кажется, что исключение выдается, как только я использую что-нибудь еще, что "основной" код C# (всякий раз, когда я пытаюсь загрузить другую DLL).
3 ответа
Возможно, есть лучший способ, но это подойдет.
Реализовать функцию инициализации (Init
здесь), который устанавливает AppDomain.AssemblyResolve
обработчик, который ищет сборку по пути основной (исполняющей) сборки:
[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;
}
Импортируйте его в Inno Setup:
procedure Init(); external 'Init@files:MyDll.dll stdcall setuponly';
И вызвать его перед вызовом функции, которая нуждается в зависимости (CheckConnexion
).
Другое решение может быть таким:
Встраивание DLL в скомпилированный исполняемый файл
Кстати, нет необходимости в loadwithalteredsearchpath
флаг. Это не влияет на сборки.NET IMO.
Я нашел кое-что еще, что могло бы быть полезным для любого, кто натыкается на эту страницу.
В моем сценарии у меня есть несколько методов C#, которые я вызываю из InnoSetup, используя DllExport. В одном из этих методов я вызываю другой из методов. Это заставило Inno выдать "Внешнее исключение E0434352".
Если я переместил код в метод, не вызываемый InnoSetup, все работало нормально.
Так...
[DllExport("Fu", CallingConvention = CallingConvention.StdCall)]
public static int Fu()
{
// Stuff
}
[DllExport("Bar", CallingConvention = CallingConvention.StdCall)]
public static int Bar()
{
Fu();
}
... заставляет InnoSetup плакать, но:
[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
}
...Это хорошо.
Я не знаю, вызвано ли это Inno или DllExport, поэтому я воздержусь от прямых насмешек и обвиню общество в целом в моем утраченном утре. (Или я за то, что я новичок в этом.)
Я хотел бы расширить ответ Мартина. Существует способ разрешить сборки без предварительного вызова метода Init, а именно путем включения статического конструктора в ваш класс .NET:
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;
}
}