在 64 位系统上使用 gcc 构建的 DLL 互操作时出现 BadImageFormatException
BadImageFormatException on interop from DLL build with gcc on 64-bit system
我正在开发 .NET 库。作为使库跨平台的任务的一部分,我决定将所有特定于平台的函数放入 C 文件并构建 DLL(对于 Windows)和 dylib(对于 macOS)。为了开始这个任务,我创建了一个简单的测试应用程序:.NET Framework 上 Windows 上的控制台应用程序,它从生成的 DLL 中调用一个简单的函数。
DLL (test.dll) 构建自的 C 文件的内容:
int Foo() { return 123; }
测试程序代码:
class Program
{
[DllImport("test")]
public static extern int Foo();
static void Main(string[] args)
{
var result = Foo();
Console.WriteLine($"Result = {result}");
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
当我 运行 程序时,我得到 BadImageFormatException.
如果我进入 Visual Studio 中的项目设置,我会看到:
如果我在下拉列表中取消选中 'Prefer 32-bit' 或 select x64,一切都很好,我看到正确的输出:
Result = 123
Press any key to exit...
但这不是我的解决方案。该库被许多用户使用,我不想(也不能)强迫他们进行一些环境设置,例如 selecting 目标平台。它也应该适用于 x86 和 x64。
DLL 是用 gcc 构建的:
gcc -v -c test.c
gcc -v -shared -o test.dll test.o
我的问题是如何解决这个问题并创建可以在任何 .NET 应用程序中定位的 DLL,而无需预先操作?也许可以构建具有双平台目标(不仅是 x64)的 DLL?
我最终构建了两个版本的 DLL,32 位和 64 位。所以在代码中我们有这样的 类:
Api.cs
namespace DualLibClassLibrary
{
internal abstract class Api
{
public abstract int Method();
}
}
Api32.cs
using System.Runtime.InteropServices;
namespace DualLibClassLibrary
{
internal sealed class Api32 : Api
{
[DllImport("test32")]
public static extern int Foo();
public override int Method()
{
return Foo();
}
}
}
Api64.cs
using System.Runtime.InteropServices;
namespace DualLibClassLibrary
{
internal sealed class Api64 : Api
{
[DllImport("test64")]
public static extern int Foo();
public override int Method()
{
return Foo();
}
}
}
ApiProvider.cs
using System;
namespace DualLibClassLibrary
{
internal static class ApiProvider
{
private static readonly bool Is64Bit = IntPtr.Size == 8;
private static Api _api;
public static Api Api
{
get
{
if (_api == null)
_api = Is64Bit ? (Api)new Api64() : new Api32();
return _api;
}
}
}
}
所以如果我们想调用Method
方法,我们会写:
var result = ApiProvider.Api.Method();
当然我们需要在输出中包含以下文件:
- test32.dll(以及 dylib 等)
- test64.dll(以及 dylib 等)
使用此系统,程序将 select 所需的文件取决于当前进程是否为 64 位。
我正在开发 .NET 库。作为使库跨平台的任务的一部分,我决定将所有特定于平台的函数放入 C 文件并构建 DLL(对于 Windows)和 dylib(对于 macOS)。为了开始这个任务,我创建了一个简单的测试应用程序:.NET Framework 上 Windows 上的控制台应用程序,它从生成的 DLL 中调用一个简单的函数。
DLL (test.dll) 构建自的 C 文件的内容:
int Foo() { return 123; }
测试程序代码:
class Program
{
[DllImport("test")]
public static extern int Foo();
static void Main(string[] args)
{
var result = Foo();
Console.WriteLine($"Result = {result}");
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
当我 运行 程序时,我得到 BadImageFormatException.
如果我进入 Visual Studio 中的项目设置,我会看到:
如果我在下拉列表中取消选中 'Prefer 32-bit' 或 select x64,一切都很好,我看到正确的输出:
Result = 123
Press any key to exit...
但这不是我的解决方案。该库被许多用户使用,我不想(也不能)强迫他们进行一些环境设置,例如 selecting 目标平台。它也应该适用于 x86 和 x64。
DLL 是用 gcc 构建的:
gcc -v -c test.c
gcc -v -shared -o test.dll test.o
我的问题是如何解决这个问题并创建可以在任何 .NET 应用程序中定位的 DLL,而无需预先操作?也许可以构建具有双平台目标(不仅是 x64)的 DLL?
我最终构建了两个版本的 DLL,32 位和 64 位。所以在代码中我们有这样的 类:
Api.cs
namespace DualLibClassLibrary
{
internal abstract class Api
{
public abstract int Method();
}
}
Api32.cs
using System.Runtime.InteropServices;
namespace DualLibClassLibrary
{
internal sealed class Api32 : Api
{
[DllImport("test32")]
public static extern int Foo();
public override int Method()
{
return Foo();
}
}
}
Api64.cs
using System.Runtime.InteropServices;
namespace DualLibClassLibrary
{
internal sealed class Api64 : Api
{
[DllImport("test64")]
public static extern int Foo();
public override int Method()
{
return Foo();
}
}
}
ApiProvider.cs
using System;
namespace DualLibClassLibrary
{
internal static class ApiProvider
{
private static readonly bool Is64Bit = IntPtr.Size == 8;
private static Api _api;
public static Api Api
{
get
{
if (_api == null)
_api = Is64Bit ? (Api)new Api64() : new Api32();
return _api;
}
}
}
}
所以如果我们想调用Method
方法,我们会写:
var result = ApiProvider.Api.Method();
当然我们需要在输出中包含以下文件:
- test32.dll(以及 dylib 等)
- test64.dll(以及 dylib 等)
使用此系统,程序将 select 所需的文件取决于当前进程是否为 64 位。