19

Here's my problem. I am wrapping a C dll in C#. To do this, I am first writing a C++/CLI wrapper. The native C library is linked to the C++/CLI wrapper. (Linker properties in C++/cli project).

Here's how it is all organised now: - The native C .lib: both x86 and 64bit.

  • 1 solution containing 2 projects:
    • C++/CLI wrapper project to which is linked native C .lib
    • C# project referencing C++/CLI project

My problem comes from the fact that I need C# to target "Any CPU". But this option is not available in C++/CLI since it compiles directly to native code.

My idea to solve this is: - Compile C++/CLI wrapper in x86 and then change the config and compile to 64 bit. When it compiles, I would like to tell it which dll to take based on the platform. ie: if compiling in 64bit, link 64 bit native C dll, else if x86, link x86 native C. - With this done, I should then be able to have Any CPU target in my C# platform. Here again, instead of referencing my C++/CLI wrapper project, I would reference the required dll based on the target platform.

My questions are:

  • How to I tell the C++/CLI project which .lib to link to based on the target platform?
  • How to I tell the C# project which C++/CLI dll to reference based on the target platform?

Let me add that the C# project a CLASS LIBRARY to be used by an x86 or x64 client.

I hope my question is clear enough. Any helps would be appreciated !

UPDATE based on:Conditional references in .NET project, possible to get rid of warning?...

So now I've edited my .csproj file using a condition to reference the dll as follows:

<ItemGroup>
    <Reference Include="AlibCppWrapper, Version=1.0.4303.21410, Culture=neutral, PublicKeyToken=c0c17a53adc44091, processorArchitecture=AMD64"
               Condition="$(Platform) == 'x64'">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\x64\Debug\AlibCppWrapper.dll</HintPath>
    </Reference>
    <Reference Include="AlibCppWrapper, Version=1.0.4303.21410, Culture=neutral, PublicKeyToken=c0c17a53adc44091, processorArchitecture=x86"
               Condition="$(Platform) == 'x86'">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\Debug\AlibCppWrapper.dll</HintPath>
    </Reference>
  </ItemGroup>

Unfortunately this doesn't work as the $(Platform) is set to AnyCPU...

Community
  • 1
  • 1
nche
  • 1,082
  • 3
  • 14
  • 32
  • 2
    I don't see a question. If you want to target x64 and x86 you need to compile seperate files for each platform (C/C++) and reference the correct library within your C# application depending on which platform it is. You could also decide you just want to force a certain platform by using the reference to a x86 or x64 compiled library. I don't see a '?' feel free to edit your question and make your question clear. – Security Hound Oct 11 '11 at 14:48
  • 2
    Is the "Any CPU" a hard requirement? The lazy programmer's solution to this is just to compile for x86 since it runs in x64. – Ilian Oct 11 '11 at 14:53
  • 2
    yes it is a hard requirement. The C# project is actually a library to be used by a client and server that can be either installed on a 64 bit or on x86 – nche Oct 11 '11 at 14:55
  • @nche: I still don't understand why, since x86 libraries can be installed fine in x64 machines. But if you can't change the requirements, then you go with Yahia's suggestions below. – Ilian Oct 11 '11 at 14:59
  • @Ilian Pinzon - if a process using a x86 library and is running on a x64 operating system then the process will be a 32-bit process. In some cases this can limit the full potential of your application. – Security Hound Oct 11 '11 at 15:13
  • 1
    This is a deployment problem. The native DLLs are not a problem, you can give them different names and specify them in the linker's Additional Dependencies setting. The rub is the C++/CLI assembly. The CLR can automatically pick the right assembly based on the processorArchitecture attribute but that requires deploying them to the GAC. Which requires two installers. Since you need them anyway, the simple solution is to just provide two different installers, one for a 32-bit machine and another for a 64-bit machine. Each deploying the correct flavor of the DLLs. – Hans Passant Oct 11 '11 at 15:13
  • 1
    @IlianPinzon - If I have my C# library as x86 then it cannot be called by a client program compiled on a x64 architecture with Any CPU... And changing this client to x86 is not possible. – nche Oct 11 '11 at 15:31

2 Answers2

22

What you describe is known as "side-by-side assembly" (two versions of the same assembly, one 32 and the other 64 bit)... I think you will find these helpful:

EDIT - as per comment:

Here you can find a walkthrough for exactly your scenario: .NET DLL wrapping C++/CLI DLL referencing a native DLL

janw
  • 8,758
  • 11
  • 40
  • 62
Yahia
  • 69,653
  • 9
  • 115
  • 144
  • @nche - This is the answer you should accept. This will do exactly what you want and is an elegant solution. The correct way to make an application support both x64 and x86 platforms is to make seperate assemblies. Your application cannot be a x64 process if it references a 32-bit library assembly. – Security Hound Oct 11 '11 at 15:01
  • The side-by-side solution in the first link seems to fit my problem but the thing is that my C# project is a dll, not an exe. Hence I don't have any main were I could programatically define which dll to load with respect to the architecture. Is it possible to use conditional references for example? ie: in my C++/cli project, the adequate C dll is linked (x86 or x64) according to architecture... and the same for the C# project (adequate c++/cli reference linked according to the architecture)... – nche Oct 13 '11 at 10:49
  • this solution would be perfect if my C# project was a dll. Then I would dynamically load the dll. But in my case it is a **class library**. So I cannot dynamically load the required underlying dll can I?... – nche Oct 13 '11 at 12:45
  • You will have to provide that class library as a DLL – Yahia Oct 13 '11 at 12:57
  • Yes that class library is to be provided as a dll. The problem is that I want to compile it as AnyCPU – nche Oct 13 '11 at 13:33
  • 1
    sorry - you want the impossible... more than what is described above is NOT possible... if the above in any way helped then please upvote/mark as accepted... – Yahia Oct 13 '11 at 14:00
  • 1
    hmm.. so is **sorry - you want the impossible** the reason this *answer* is **accepted** as an answer? For, if you disregarded the comments, I would have thought 16 people and the OP were upvoting and accepting an answer that was addressing a distinctively different issue/consideration/question. – Brett Caswell Sep 21 '15 at 21:20
  • This is not a good answer. It's providing links only. 50% of these are not available anymore as of today. Furthermore, it may have provided some valuable information back when the links were working, but apparently at no point in time it has ever answered OP's question which is about a dll, not an exe. If "it's impossible" is the answer, then that should be part of the answer and not just be mentioned in a comment. – sebrockm Mar 14 '19 at 11:54
  • Link is not available now. Answer without it doesn't have any sense – Alexey Subbota Oct 07 '19 at 10:39
  • Updated link: https://c-sharp-snippet.blogspot.com/2015/02/automatically-choose-32-or-64-bit-mixed.html?view=flipcard – Vladas Cibulskis Sep 10 '20 at 07:59
1

For me solution was following:

  1. Build x64 bit version of C++ library to output file xxx.x64.dll

  2. Build x86 bit (Win32 platform) version of C++ library to output file xxx.x86.dll

  3. Add them as content files to my wrapper C# project. Ex.:

    <ItemGroup>
      <Content Include="..\..\$(Configuration)\xxx.x86.dll" Link="xxx.x86.dll">
        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      </Content>
      <Content Include="..\..\$(Configuration)\xxx.x64.dll" Link="xxx.x64.dll">
        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      </Content>
    </ItemGroup>
    
  4. In C# Import functions from both x86 and x64 library versions. Ex.:

        [DllImport("xxx.x86.dll", EntryPoint = "FunctionName", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
        public static extern void FunctionNamex86();
        [DllImport("xxx.x64.dll", EntryPoint = "FunctionName", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
        public static extern void FunctionNamex64();
    
  5. In C# implement function that calls correct overload based on current platform. Ex.:

        public static void FunctionName()
        {
            if (Environment.Is64BitProcess)
                FunctionNamex64();
            else
                FunctionNamex86();
        }
    
  6. Now C# project can be built as "Any CPU" and used by other multiplatform projects.

  7. To distribute it as NuGet package, I use following NuSpec configuration:

    <?xml version="1.0"?>
    <package>
        <metadata>
            <contentFiles>
                <files include="any/any/xxx.x64.dll" buildAction="None" copyToOutput="true" />
                <files include="any/any/xxx.x86.dll" buildAction="None" copyToOutput="true" />
            </contentFiles>
        </metadata>
        <files>
            <file src="Release/C#Wrapper.dll" target="lib" /> 
            <file src="Release/xxx.x64.dll" target="content" /> 
            <file src="Release/xxx.x86.dll" target="content" /> 
            <file src="Release/xxx.x64.dll" target="contentFiles/any/any" /> 
            <file src="Release/xxx.x86.dll" target="contentFiles/any/any" /> 
        </files>
    </package>
    

Answer is mostly based on: Using a 32bit or 64bit dll in C# DllImport