locked
Using an unmanaged C++ dll for AnyCPU C# compilation...

    Question

  • I have a C++ DLL that has been imported into VS 2008.  I can compile this DLL as Win32 or x64. 

    I am using this DLL in a C# application...calls to the DLL are through the P/Invoke DllImport method.  If I compile the C++ DLL as x64, I cannot run the C# application as AnyCPU or x86 without encountering a BadFormatException, and vice versa.  Is there any way to compile the C++ DLL as AnyCPU as you might in C# (and still use unsafe/unmanaged C++ code), or is there a way to detect the compile type for C# and use the appropriate C++ DLL accordingly.  Or am I stuck "hard-compiling" the C# application as x86 or x64 specifically as long as I use the corresponding C++ DLL compilation?
    Wednesday, December 23, 2009 10:29 PM

Answers

  • You can't compile in a way that works on AnyCPU and still has native, unmanaged code.

    However, if you're willing to hand-edit your .csproj file, you can setup references that are dependent on the target platform.  That way, one project can target x86 and x64, and reference the corresponding C++ assembly:

    Basically, you just put in a ItemGroup under the <Project> tag, like so:

      <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
        <Reference Include="MyCPPDLL, Version=2.0.8.42, Culture=neutral, PublicKeyToken=b1b0c32fd1ffe4f9, processorArchitecture=x86">
          <SpecificVersion>False</SpecificVersion>
          <HintPath>..\Libraries\x86\MyCPPDLL.dll</HintPath>
          <Private>True</Private>
        </Reference>
      </ItemGroup>
      <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
        <Reference Include="MyCPPDLL, Version=2.0.8.42, Culture=neutral, PublicKeyToken=b1b0c32fd1ffe4f9, processorArchitecture=x86">
          <SpecificVersion>False</SpecificVersion>
          <HintPath>..\Libraries\x86\MyCPPDLL.dll</HintPath>
          <Private>True</Private>
        </Reference>
      </ItemGroup>
      <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
        <Reference Include="MyCPPDLL, Version=2.0.8.42, Culture=neutral, PublicKeyToken=b1b0c32fd1ffe4f9, processorArchitecture=AMD64">
          <SpecificVersion>False</SpecificVersion>
          <HintPath>..\Libraries\x64\MYCPPDLL.dll</HintPath>
          <Private>True</Private>
        </Reference>
      </ItemGroup>
      <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
        <Reference Include="MyCPPDLL, Version=2.0.8.42, Culture=neutral, PublicKeyToken=b1b0c32fd1ffe4f9, processorArchitecture=AMD64">
          <SpecificVersion>False</SpecificVersion>
          <HintPath>..\Libraries\x64\MYCPPDLL.dll</HintPath>
          <Private>True</Private>
        </Reference>
      </ItemGroup>
    Just point the appropriate sections to the debug/release x86/x64 versions of your C++ library, and it will work.

    Note that, there is one downside to this - there's a "bug" in Visual Studio, where it puts a warning "The referenced component 'xxx' could not be found." three times each time you build.  It's because it's seeing all 4 references, but the compiler only uses one.  VS, on the other hand, doesn't handle it right.
    Reed Copsey, Jr. - http://reedcopsey.com
    • Marked as answer by Harry Zhu Tuesday, December 29, 2009 2:54 AM
    Wednesday, December 23, 2009 10:55 PM
  • There is a way to do it, but it's not really a good idea. Here's how:

    Build two copies of your DLL, one 32-bit and one 64-bit. On a 64-bit OS have your installer copy the 32-bit file to %WINDIR%\System32 and the 64-bit file to %WINDIR%\SysWOW64 (I may have that backwards, I remember it was odd). Build your managed assembly with AnyCPU. Now it will always load the correct image.

    Having said that, it is of course completely unnecessary. If you make an installation, just have the installation write the appropriate DLL.

    If what you want is a single distributable file, you could always package both versions of the DLL into the assembly and on startup copy the appropriate file out. This requires that your app run with admin privilages if the user executes the .exe from a protected path (like Program Files).
    • Marked as answer by Harry Zhu Tuesday, December 29, 2009 2:54 AM
    Thursday, December 24, 2009 3:04 AM

All replies

  • You can't compile in a way that works on AnyCPU and still has native, unmanaged code.

    However, if you're willing to hand-edit your .csproj file, you can setup references that are dependent on the target platform.  That way, one project can target x86 and x64, and reference the corresponding C++ assembly:

    Basically, you just put in a ItemGroup under the <Project> tag, like so:

      <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
        <Reference Include="MyCPPDLL, Version=2.0.8.42, Culture=neutral, PublicKeyToken=b1b0c32fd1ffe4f9, processorArchitecture=x86">
          <SpecificVersion>False</SpecificVersion>
          <HintPath>..\Libraries\x86\MyCPPDLL.dll</HintPath>
          <Private>True</Private>
        </Reference>
      </ItemGroup>
      <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
        <Reference Include="MyCPPDLL, Version=2.0.8.42, Culture=neutral, PublicKeyToken=b1b0c32fd1ffe4f9, processorArchitecture=x86">
          <SpecificVersion>False</SpecificVersion>
          <HintPath>..\Libraries\x86\MyCPPDLL.dll</HintPath>
          <Private>True</Private>
        </Reference>
      </ItemGroup>
      <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
        <Reference Include="MyCPPDLL, Version=2.0.8.42, Culture=neutral, PublicKeyToken=b1b0c32fd1ffe4f9, processorArchitecture=AMD64">
          <SpecificVersion>False</SpecificVersion>
          <HintPath>..\Libraries\x64\MYCPPDLL.dll</HintPath>
          <Private>True</Private>
        </Reference>
      </ItemGroup>
      <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
        <Reference Include="MyCPPDLL, Version=2.0.8.42, Culture=neutral, PublicKeyToken=b1b0c32fd1ffe4f9, processorArchitecture=AMD64">
          <SpecificVersion>False</SpecificVersion>
          <HintPath>..\Libraries\x64\MYCPPDLL.dll</HintPath>
          <Private>True</Private>
        </Reference>
      </ItemGroup>
    Just point the appropriate sections to the debug/release x86/x64 versions of your C++ library, and it will work.

    Note that, there is one downside to this - there's a "bug" in Visual Studio, where it puts a warning "The referenced component 'xxx' could not be found." three times each time you build.  It's because it's seeing all 4 references, but the compiler only uses one.  VS, on the other hand, doesn't handle it right.
    Reed Copsey, Jr. - http://reedcopsey.com
    • Marked as answer by Harry Zhu Tuesday, December 29, 2009 2:54 AM
    Wednesday, December 23, 2009 10:55 PM
  • There is a way to do it, but it's not really a good idea. Here's how:

    Build two copies of your DLL, one 32-bit and one 64-bit. On a 64-bit OS have your installer copy the 32-bit file to %WINDIR%\System32 and the 64-bit file to %WINDIR%\SysWOW64 (I may have that backwards, I remember it was odd). Build your managed assembly with AnyCPU. Now it will always load the correct image.

    Having said that, it is of course completely unnecessary. If you make an installation, just have the installation write the appropriate DLL.

    If what you want is a single distributable file, you could always package both versions of the DLL into the assembly and on startup copy the appropriate file out. This requires that your app run with admin privilages if the user executes the .exe from a protected path (like Program Files).
    • Marked as answer by Harry Zhu Tuesday, December 29, 2009 2:54 AM
    Thursday, December 24, 2009 3:04 AM