我已经用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
目录中,我也会遇到相同的异常!
我该怎么做才能解决此问题?
编辑:如果使用System.Reflection.Assembly.LoadFrom
加载{{1},则在评论中@Peter Wolf建议的Loading .NET Assemblies out of Seperate Folders后,可能的解决方案(扭曲的恕我直言) }并通过反射访问其每个类/方法/成员/等。即
itextsharp.dll
这实际上有效,但是对我来说这看起来很疯狂!
为了做到这一点,破坏了一个非常简单且良好的C#代码。
我只是简直不敢相信没有一种正常的方式来加载与宿主Delphi应用程序不同的文件夹中的C#DLL。
是否存在一种本机/正常且直接的方法来执行此操作而无需反思?
答案 0 :(得分:2)
您可以通过AssemblyResolve
事件挂接到C#DLL中的程序集解析机制。在这里,您将有机会找到并加载.NET运行时无法单独定位的程序集。您可以安装处理程序,例如类构造函数(在您的情况下为Class1
)中,但是如果您的DLL在Class1
之外有一些入口点,这可能还不够。安装处理程序的代码是:
using System;
using System.IO;
using System.Linq;
using System.Reflection;
publi 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;
};
}
// ...
}