在Delphi中加载C# DLL没有找到依赖库DLL
Loading C# DLL in Delphi does not find dependent library DLL
我用 C# 编写了一个小的互操作 DLL 供 Delphi(不是 COM)使用。
我正在使用 iTextSharp
生成 PDF 文件。
C# DLL 基本上非常简单:
namespace ClassLibrary1
{
public class Class1
{
[DllExport("Test1", CallingConvention = CallingConvention.StdCall)]
public static void Test1()
{
System.IO.FileStream fs = new FileStream(@"D:\x.pdf", FileMode.Create, FileAccess.Write, FileShare.None);
Document document = new Document();
document.SetPageSize(PageSize.A4);
PdfWriter writer = PdfWriter.GetInstance(document, fs);
document.Open();
document.Add(new Paragraph("Hello World"));
document.Close();
writer.Close();
fs.Close();
}
}
}
和 Delphi 主机应用程序代码(位于不同的文件夹中):
procedure TForm1.Button1Click(Sender: TObject);
type
TDLLProc = procedure; stdcall;
var
DLLModule: HMODULE;
DLLProc: TDLLProc;
begin
// SetCurrentDir('G:\Projects\Test\ClassLibrary1\bin\x86\Release');
DLLModule := LoadLibrary('G:\Projects\Test\ClassLibrary1\bin\x86\Release\ClassLibrary1.dll');
if DLLModule = 0 then RaiseLastWin32Error;
DLLProc := GetProcAddress(DLLModule, 'Test1');
if @DLLProc = nil then RaiseLastWin32Error;
DLLProc(); // external exception E0434352
end;
问题是调用该方法会引发 external exception E0434352
因为出于某种原因 C# DLL 找不到 itextsharp.dll
.
如果我将 itextsharp.dll
复制到 Project1.exe
目录,一切正常。
如果我将 Project1.exe
复制到 'G:\Projects\Test\ClassLibrary1\bin\x86\Release\
一切正常。
我不想将 ClassLibrary1.dll
和 itextsharp.dll
放在我的 EXE 程序目录中,而是从它们自己的目录中使用它们。
我试过显式设置SetCurrentDirecory
,也试过SetDllDirectory
,也试过LoadLibraryEx
。没有任何帮助。
似乎即使我将 itextsharp.dll
DLL(仅用于测试)放在 C:\Windows
目录中,我也会得到同样的异常!
我该怎么做才能解决这个问题?
编辑: 查看@Peter Wolf 在评论中建议的 Loading .NET Assemblies out of Seperate Folders 后可能的解决方案(扭曲的恕我直言)是否使用 System.Reflection.Assembly.LoadFrom
加载itextsharp.dll
assmbly 并通过反射访问它的每个 classes/methods/members/etc 。即
[DllExport("Test1", CallingConvention = CallingConvention.StdCall)]
public static void Test1()
{
var asm = System.Reflection.Assembly.LoadFrom(@"G:\Projects\Test\ClassLibrary1\bin\x86\Release\itextsharp.dll");
Type tDocument = asm.GetType("iTextSharp.text.Document", true, true);
dynamic document = Activator.CreateInstance(tDocument);
Type tPageSize = asm.GetType("iTextSharp.text.PageSize", true, true);
tPageSize.GetMethod("GetRectangle");
// ETC... ETC... ETC...
}
这确实有效,但是我觉得这太疯狂了!
毁了一个非常简单和好的 C# 代码只是为了能够做到这一点。
我无法相信没有 正常 方法来加载位于与我的主机 Delphi 应用程序不同的文件夹中的 C# DLL。
有没有 native/normal 直接的方法来做到这一点而不需要反射???
您可以通过 AssemblyResolve
事件连接到 C# DLL 中的程序集解析机制。在这里,您有机会定位和加载 .NET 运行时无法自行定位的程序集。您可以安装处理程序,例如在 class 构造函数中(在您的情况下为 Class1
),但如果您的 DLL 在 Class1
之外有一些入口点,这可能还不够。安装处理程序的代码是:
using System;
using System.IO;
using System.Linq;
using System.Reflection;
public class Class1
{
static Class1()
{
var dllDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name);
if (assembly != null)
{
return assembly;
}
var fileName = args.Name.Split(',')[0] + ".dll";
var filePath = Path.Combine(dllDirectory, fileName);
if (File.Exists(filePath))
{
return Assembly.LoadFile(filePath);
}
return null;
};
}
// ...
}
我用 C# 编写了一个小的互操作 DLL 供 Delphi(不是 COM)使用。
我正在使用 iTextSharp
生成 PDF 文件。
C# DLL 基本上非常简单:
namespace ClassLibrary1
{
public class Class1
{
[DllExport("Test1", CallingConvention = CallingConvention.StdCall)]
public static void Test1()
{
System.IO.FileStream fs = new FileStream(@"D:\x.pdf", FileMode.Create, FileAccess.Write, FileShare.None);
Document document = new Document();
document.SetPageSize(PageSize.A4);
PdfWriter writer = PdfWriter.GetInstance(document, fs);
document.Open();
document.Add(new Paragraph("Hello World"));
document.Close();
writer.Close();
fs.Close();
}
}
}
和 Delphi 主机应用程序代码(位于不同的文件夹中):
procedure TForm1.Button1Click(Sender: TObject);
type
TDLLProc = procedure; stdcall;
var
DLLModule: HMODULE;
DLLProc: TDLLProc;
begin
// SetCurrentDir('G:\Projects\Test\ClassLibrary1\bin\x86\Release');
DLLModule := LoadLibrary('G:\Projects\Test\ClassLibrary1\bin\x86\Release\ClassLibrary1.dll');
if DLLModule = 0 then RaiseLastWin32Error;
DLLProc := GetProcAddress(DLLModule, 'Test1');
if @DLLProc = nil then RaiseLastWin32Error;
DLLProc(); // external exception E0434352
end;
问题是调用该方法会引发 external exception E0434352
因为出于某种原因 C# DLL 找不到 itextsharp.dll
.
如果我将 itextsharp.dll
复制到 Project1.exe
目录,一切正常。
如果我将 Project1.exe
复制到 'G:\Projects\Test\ClassLibrary1\bin\x86\Release\
一切正常。
我不想将 ClassLibrary1.dll
和 itextsharp.dll
放在我的 EXE 程序目录中,而是从它们自己的目录中使用它们。
我试过显式设置SetCurrentDirecory
,也试过SetDllDirectory
,也试过LoadLibraryEx
。没有任何帮助。
似乎即使我将 itextsharp.dll
DLL(仅用于测试)放在 C:\Windows
目录中,我也会得到同样的异常!
我该怎么做才能解决这个问题?
编辑: 查看@Peter Wolf 在评论中建议的 Loading .NET Assemblies out of Seperate Folders 后可能的解决方案(扭曲的恕我直言)是否使用 System.Reflection.Assembly.LoadFrom
加载itextsharp.dll
assmbly 并通过反射访问它的每个 classes/methods/members/etc 。即
[DllExport("Test1", CallingConvention = CallingConvention.StdCall)]
public static void Test1()
{
var asm = System.Reflection.Assembly.LoadFrom(@"G:\Projects\Test\ClassLibrary1\bin\x86\Release\itextsharp.dll");
Type tDocument = asm.GetType("iTextSharp.text.Document", true, true);
dynamic document = Activator.CreateInstance(tDocument);
Type tPageSize = asm.GetType("iTextSharp.text.PageSize", true, true);
tPageSize.GetMethod("GetRectangle");
// ETC... ETC... ETC...
}
这确实有效,但是我觉得这太疯狂了!
毁了一个非常简单和好的 C# 代码只是为了能够做到这一点。
我无法相信没有 正常 方法来加载位于与我的主机 Delphi 应用程序不同的文件夹中的 C# DLL。
有没有 native/normal 直接的方法来做到这一点而不需要反射???
您可以通过 AssemblyResolve
事件连接到 C# DLL 中的程序集解析机制。在这里,您有机会定位和加载 .NET 运行时无法自行定位的程序集。您可以安装处理程序,例如在 class 构造函数中(在您的情况下为 Class1
),但如果您的 DLL 在 Class1
之外有一些入口点,这可能还不够。安装处理程序的代码是:
using System;
using System.IO;
using System.Linq;
using System.Reflection;
public class Class1
{
static Class1()
{
var dllDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name);
if (assembly != null)
{
return assembly;
}
var fileName = args.Name.Split(',')[0] + ".dll";
var filePath = Path.Combine(dllDirectory, fileName);
if (File.Exists(filePath))
{
return Assembly.LoadFile(filePath);
}
return null;
};
}
// ...
}