IronSlug
IronSlug

Reputation: 492

Why does my C++ interop works on .Net 4.5 but not on 4

In my project, I have to use a third party C++ dll (not COM). I have developed an interface C# dll which is used by my main program. This is the setup :

IDE : VS Express Desktop 2013 x64

Legacy.dll (C++) Interface.dll (C# .Net4.5 AnyCPU) Program.exe (C# .Net4.5 AnyCPU)

And this is my interop class :

public static class Legacy
{
    // Establish a connection with a protocol channel
    // extern “C” long WINAPI LegacyConnect(unsigned long ProtocolID, unsigned long Flags, unsigned long *pChannelID)
    [DllImport("Legacy.dll")]
    public static extern LegacyErrCodes LegacyConnect(Int32 ProtocolId, Int32 Flags, ref Int32 pChannelId);

    // Terminate a connection with a protocol channel
    // extern “C” long WINAPI LegacyDisconnect(unsigned long ChannelID)
    [DllImport("Legacy.dll")]
    public static extern LegacyErrCodes LegacyDisconnect(Int32 ChannelId);

    // General I/O control functions for reading and writing protocol configuration parameters (e.g. initialization, baud rates, programming voltages, etc.)
    // extern “C” long WINAPI LegacyIoctl(unsigned long ChannelID, unsigned long IoctlID, void *pInput, void *pOutput)
    [DllImport("Legacy.dll")]
    public static extern LegacyErrCodes LegacyIoctl(Int32 ChannelID, Int32 IoctlId, byte[] pInput, Int32 pOutput);
    [DllImport("Legacy.dll")]
    public static extern LegacyErrCodes LegacyIoctl(Int32 ChannelID, Int32 IoctlId, ref SCONFIG_LIST pInput, Int32 pOutput);
    [DllImport("Legacy.dll")]
    public static extern LegacyErrCodes LegacyIoctl(Int32 ChannelID, Int32 IoctlId, ref SCONFIG_HWTYPE_BAUDRATE pInput, Int32 pOutput);
    [DllImport("Legacy.dll")]
    public static extern LegacyErrCodes LegacyIoctl(Int32 ChannelID, Int32 IoctlId, IntPtr pInput, Int32 pOutput);
    [DllImport("Legacy.dll")]
    public static extern LegacyErrCodes LegacyIoctl(Int32 ChannelID, Int32 IoctlId, ref int pInput, Int32 pOutput);

    // Read message(s) from a protocol channel
    // extern “C” long WINAPI LegacyReadMsgs(unsigned long ChannelID, Legacy_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout)
    [DllImport("Legacy.dll")]
    public static extern LegacyErrCodes LegacyReadMsgs(Int32 ChannelID, ref Legacy_MSG pMsg, ref Int32 pNumMsgs, Int32 TimeOut);

    // Write message(s) to a protocol channel
    // extern “C” long WINAPI LegacyWriteMsgs(unsigned long ChannelID, Legacy_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout)
    [DllImport("Legacy.dll")]
    public static extern LegacyErrCodes LegacyWriteMsgs(Int32 ChannelID, ref Legacy_MSG pMsg, ref Int32 pNumMsgs, Int32 TimeOut);
    [DllImport("Legacy.dll")]
    public static extern LegacyErrCodes LegacyWriteMsgs(Int32 ChannelID, IntPtr pMsg, ref Int32 pNumMsgs, Int32 TimeOut);
}

Both .Net project initially targeted the framework 4.5 with plateform setted to AnyCPU and everything worked fine.

I was asked to downgrade the solution to target Framework 4 and luckily, no code had to be modified in Interface.dll and nothing relative to its use in Program.dll, so the setup became :

Legacy.dll (C++) Interface.dll (C# .Net4 AnyCPU) Program.exe (C# .Net4 AnyCPU)

Since then, I was getting a BadImageFormatException every time my Inteface.dll tried to call a function of the C++ dll.

I have found a way around : if I set my calling program (Program.exe) to target x86 CPU only, it works again.

Legacy.dll (C++) Interface.dll (C# .Net4 AnyCPU) Program.exe (C# .Net4 x86)

I am nothing but a high level developer so I'm absolutely lost about this problem. Why is this happening ? Is it a way to restore my Program.exe configuration to AnyCPU ? If not, does that really mean my program will run as a 32 bits one even on a 64 bits machine ?

Upvotes: 1

Views: 312

Answers (1)

Martin Liversage
Martin Liversage

Reputation: 106796

BadImageFormatException is most likely a result of trying to load an executable having the wrong "bitness" (e.g. loading a 32 bit DLL in a 64 bit process). If Legacy.dll is a 32 bit DLL (which it seems to be based on the information you have provided) you should simply compile Program.exe targetting x86. If you target AnyCPU then Program.exe will execute in a 64 bit process on a 64 bit operating system and you do not want that to happen if you are loading 32 bit DLL's. Actually, in .NET 4.5 this was slightly changed so the default for an executable project is to use AnyCPU but to "prefer 32 bit" (/platform:anycpu32bitpreferred) which explains why you only experience the problem when you downgrade to .NET 4.

does that really mean my program will run as a 32 bits one even on a 64 bits machine ?

Yes, your program loads a 32 bit legacy DLL and has to execute in a 32 bit process even on a 64 bit operating system. Only if you can get a 64 bit version of the legacy DLL will your program be able to execute in a 64 bit process.

In general you should avoid targetting AnyCPU if your code has a dependency on a DLL that only exists as either 32 bit or 64 bit which normally is the case when you are doing interop. Instead any assemblies that have say a 32 bit dependency should target x86 and this specific targetting should flow all the way up the dependency chain to the executable project.

Project dependencies

Upvotes: 4

Related Questions