I have a solution that contains C# and managed C++ projects.
It compiles in the solution platform x64 and x86. Since it is managed C++ I wanted to create a 'Any CPU' solution and get rid of the old ones.
I changed the C++ project linker settings to Force Safe IL Image for both x64 and x86.
Next, using the Configuration Manager, I created a new solution platform called 'Any CPU'. Next I added a project platform also called 'Any CPU'.
I proceeded to set all the C# projects to 'Any CPU', but for the C++ I can't do that. The project platform 'Any CPU' is not in the drop down, and there is also no option 'New...'.
VS is adement about it, so I kept it like it was and started a build. To my surprise the result DLL (from the C++ project) was MSIL even though the platform for C++ was x64. Same happens when compiling x32, the resulting DLL is in MSIL.
What gives?
Why can't I set the C++ project to 'Any CPU'?
As far as I know, you cannot create an "AnyCPU" project type in Visual Studio for a C++/CLI project. However, you can configure your C++/CLI project (under the "Win32" project type) so that it compiles as pure, safe MSIL, without a target platform. Doing so will allow your C++/CLI DLL assembly to be used with an "AnyCPU" C# project. I.e. it's effectively "AnyCPU", even though that's not its actual name in the Configuration Manager.
In the "C/C++" project settings:
Common Language RunTime Support: Safe MSIL Common Language RunTime Support (/clr:safe)
In the "Linker" project settings:
CLR Image Type: just make sure this isn't set explicitly to IJW or PURE
Notes:
By using the "safe" project type, a few of the compiler and linker options which appear to affect platform type will be ignored. I.e. you don't have to go through and set everything to a non-specific platform type. Just the above. But you may set the other options to something appropriate, if it makes you feel better. :)
"Safe" will prevent the use of pointers. If this is an important issue, it is apparently possible to do albeit with a more complicated process. See Creating a pure MSIL assembly from a C++/CLI project? for details.
Don't forget that by default, Visual Studio will create C# projects that even though they are "AnyCPU" and even though they are executed on a 64-bit OS, will start up as a 32-bit process. This can hide platform-mismatch issues, if a dependency is x86 instead of pure/safe MSIL as intended. Just something be aware of (you can control this by unchecking the "Prefer 32-bit" option in the C# project's "Build" project properties page).
In order for the C++ functionality to be consumed by a C# dll, the C++ project must produce both x86 and x64 versions of the dll. It is not possible to reference just a x86 or a x64 dll from a C# dll compiled with the AnyCPU setting.
The trick to getting the AnyCPU dll to play with the C++ dll, is at runtime make sure the assembly cannot load the C++ dll and then subscribe to the AppDomain AssemblyResolve event. When the assembly tries to load the dll and fails, then your code has the opportunity to determine which dll needs to be loaded.
Subscribing to the event looks something like this:
System.AppDomain.CurrentDomain.AssemblyResolve += Resolver;
Event handler looks something like this:
System.Reflection.Assembly Resolver(object sender, System.ResolveEventArgs args)
{
string assembly_dll = new AssemblyName(args.Name).Name + ".dll";
string assembly_directory = "Parent directory of the C++ dlls";
Assembly assembly = null;
if(Environment.Is64BitProcess)
{
assembly = Assembly.LoadFile(assembly_directory + #"\x64\" + assembly_dll);
}
else
{
assembly = Assembly.LoadFile(assembly_directory + #"\x86\" + assembly_dll);
}
return assembly;
}
I have created a simple project demonstrating how to access C++ functionality from an AnyCPU dll.
https://github.com/kevin-marshall/Managed.AnyCPU
Related
code is here https://github.com/Layty/cppnetdll
I have check the cpp dll and c# all is on x86, my pc is x64 ,I have also use both cpp and C# with x64
but it still tell me like
Unhandled exception. System.BadImageFormatException: Could not load file or assembly 'DLLMYX86, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. 试图加载格式不正确的程序。
File name: 'DLLMYX86, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' ---> System.BadImageFormatException: 试图加载格式不正确的程序。 (0x8007000B)
at MYNETX86.Program.Main(String[] args)
In .NET when you compile a managed assembly the compiler generate a intermediate code which is then compile at the fly by the JIT (just in time compiler) for the machine architecture where you are running your application.
A native (C++) dll is compiled for a specific architecture (could be x86 or x64) and the binary image file is specific for that architecture.
If you want to use a native dll from a .NET app you have to be sure that the JIT compiles the code for the same architecture the native dll was made for. The only way to be sure that everything is coherent is to compile the .NET assembly for that specific architecture. In Visual Studio this is possible switching the target architecture from Any CPU to the same of the native dll.
Your native dll is referenced as a regular managed dll. This will not work. Your options are
Rewrite the functionality in managed code
Use C++/CLI to produce a managed dll
Use PInvoke to call a native dll
I would prefer option 1 if possible. Interacting with unmanaged code is kind of a pain in the ass for anything except the most trivial signatures.
Your C# project targets .Net Core 3.1, so you need update your C++ project to target the same version.
In Project Properties > Advanced > C++/CLI Properties.
Set Common Language Runtime Support to .Net Core Runtime Support (/clr:netcore), then click Apply.
Set .Net Target Framework Version to netcoreapp3.1 or .NET Core 3.1.
I have solve this problem. I remove the pch.h and the pch.cpp and dllmain , then every thing run well .
But I dont know why ? it is just pre combile , and I have clean obj dir and clean project befor .
I have update my git code . just fix https://github.com/Layty/cppnetdll/blob/master/DLLMYX86/DLLMYX86/dllmain.cpp
or del this file.
I have make other both include cpp and C# .
othrs if have problem can download it. it works well
now I can new cpp object in c#
I have a problem when compiling a managed DLL project. The solution consists of two projects, the first is a .NET DLL written in C# and the other is Managed C++ DLL that directly references the C# project.
Both projects/DLLs are strongly named with an snk file on disk. The C# dll has a target framework of "AnyCPU" while the Manage C++ project is compiled twice, one for an x86 target and the other for an x64.
My problem is that when I compile the Managed C++ project to target the x86 platform, the result DLL has a PublicKeyToken = null as reported by ILSpy. When compiling to target an x64 platform, the DLL has the correct PublicKeyToken. I have checked my project properties, the snk file is referenced correctly for both platform targets under Configuration Properties -> Linker->Advanced->Key File with no delay signing; the Target Machine option is also set correctly based on the desired compilation target.
Here is the information shown by ILSpy when I load my DLL.
For the x64 dll:
// MyDll.x64, Version=1.1.1000.1, Culture=neutral, PublicKeyToken=XXXXXXXXX
// Architecture: x64
// This assembly contains unmanaged code.
// Runtime: .NET 2.0
For the x86 dll:
// MyDll.x86, Version=1.1.1000.1, Culture=neutral, PublicKeyToken=null
// Architecture: AnyCPU (64-bit preferred)
// This assembly contains unmanaged code.
// Runtime: .NET 2.0
What concerns me is the Architecture description for the x86 assembly: AnyCPU (64-bit preferred)
I am not sure why its using the AnyCPU configuration and what the 64-bit preferred annotation means exactly?
I also like to mention that my project is built against .NET Framwork 2.0 for the C# project while the Managed c++ project is built against the v90 Platform Toolset. I am using Visual Studio 2010 running on a Windows 7 64-bit machine.
Can someone tell me why this is happening and how can I solve this issue?
It is simply a consequence of how the COR header in the assembly can indicate what processor architecture is desired. You can see the declarations in the CorHdr.h SDK header file, you'll find it in your Windows SDK directory on your machine. You can use the CorFlags.exe utility to display the values.
The only flag available is COMIMAGE_FLAGS_32BITREQUIRED. When set, it indicates to the CLR that you want to run the program in 32-bit mode, even on a 64-bit operating system. An additional flag got added in .NET 4.5, COMIMAGE_FLAGS_32BITPREFERRED, it resolves an ambiguity on ARM cores. Too many assemblies around where 32BITREQUIRED actually means "x86 required" instead of "32-bit required".
So there is nothing similar to a "64 bit required" flag, an assembly can only indicate "32-bit" or "doesn't matter". With the jitter providing the "doesn't matter" glue, it generates the architecture dependent machine code at runtime. Since the 32BITREQUIRED option isn't turned on in your assembly, the disassembler cannot display anything else but AnyCPU.
The next detail is the IMAGE_FILE_HEADER.Machine field in the PE header of the executable file, it indicates what kind of machine the executable can run on. That's a weak signal for .NET assemblies since they don't normally contain any executable code, just MSIL. And it is readily ignored by the Windows loader, .NET assemblies normally have this field set to IMAGE_FILE_MACHINE_I386 to indicate x86. You still get a 64-bit process out of such an EXE assembly, some pretty heroic loader structure patching occurs when such an EXE is loaded. The job of mscoree.dll, the "loader shim". More about that in this post.
Since you targeted x64 in your C++/CLI project, the IMAGE_FILE_HEADER.Machine was set to IMAGE_FILE_MACHINE_AMD64 by the linker. The disassembler saw that, thus producing the "64-bit preferred" annotation.
Don't be fooled by the word "preferred" here. The disassembler didn't look deep enough to see that your assembly actually contains machine code, generated by the C++/CLI compiler. They don't like to, there isn't any disassembler that will decompile the machine code back to C++/CLI source code. The assembly isn't ever going to run on a 32-bit operating system. Kaboom on a 32-bit OS, the program fails with error 11, ERROR_BAD_FORMAT, "An attempt was made to load a program with an incorrect format".
This answers your question, it doesn't otherwise have anything to do with a strong name.
I have a project where I reference a third party assembly.
That assembly has x86 and x64 versions. I can only reference one, since the third party system relies on a specific one to work. (Depending on wich machine the system is installed)
Until now, I could manage both versions in a single project using this answer. So, I set the configuration manager option to x86 or x64 and it knows wich version to reference.
Targeting both 32bit and 64bit with Visual Studio in same solution/project
But then I came with the following problem: Needed to use a method from that assembly having version specific parameter type.
The x64 version has an IdToObject(long ID) method that doesn't exist in the x86.
The x86 version has an IdToObject(int ID) method that doesn't exist in the x64.
The ID is provided by another assembly of the same third party (that is not version specific). The ObjectID class gives me the ToInt32() and the ToInt64() methods.
If I try the IdToObject(ID.ToInt32()) in a x64 version, I get an overflow at runtime.
If I try the IdToObject(ID.ToInt64()) in a x86 version, doesn't compile (trying to pass long as int).
I've tried this, runs fine for the x64 version, but doesn't compile for the x86.
if (System.Reflection.Assembly.GetExecutingAssembly().GetName().ProcessorArchitecture == System.Reflection.ProcessorArchitecture.X86)
return Document.IdToObject(ObjectID.ToInt32()); //returns an object
else
return Document.IdToObject(ObjectID.ToInt64()); //returns an object
So, all I need is to avoid compiling a single line when project is set to x86, and another if project is set to x64. What can I do?
Looks like you will need to use a preprocessor directive. In your project properties for your 64-bit build configuration, add WIN64 under Build, Conditional compilation symbols. Then change your code to this:
#if WIN64
return Document.IdToObject(ObjectID.ToInt64()); //returns an object
#else
return Document.IdToObject(ObjectID.ToInt32()); //returns an object
#endif
Here's my sample scenario. I have a Console application and a class library dll (call it libraryA). The LibraryA dll has a reference to Oracle.DataAccess.dll version 4.112.2.0. The Oracle DLL is in the GAC. The reference to the Oracle DLL in LibraryA is "Copy Local = false." So far so good. If you build the libraryA dll, then Oracle.DataAccess.dll does not show up in its output directory. OK. Now I reference the libraryA dll in my console app. The reference to libraryA dll is "copy local = true". Now, when I build the console application, the Oracle.DataAcess.dll does show up in the output directory for the console application. However, it appears that the ONLY DLL that acts this way is the Oracle dll. Here is the complete code from LibraryA
public void Foo() {
Oracle.DataAccess.Client.OracleConnection c = new Oracle.DataAccess.Client.OracleConnection();
WebMatrix.WebData.OAuthAccountData x = new WebMatrix.WebData.OAuthAccountData("asd", "asd");
DevExpress.Web.ASPxCallback.ASPxCallback cvv = new DevExpress.Web.ASPxCallback.ASPxCallback();
}
WebMatrix and DevExpress are also in the GAC just like the Oracle DLL. However, neither of those DLLs are output to the output directory, only the Oracle dll. Why? What's happening here?
For that matter, you can create another class library, call it libraryB, DON'T put libraryB in the GAC, reference LibraryB from LibraryA and set copy local = false. Even when you do that, libraryB is not copied to the output directory of the console app. Of course, in this case, the program blows up because it can't find library B, but at least Visual Studio is respecting the copy local flag = false. What is different about this stupid Oracle DLL?
Oh, one other thing that's funny. If, in my console application, I explicitly add a reference to the Oracle.DataAccess.dll, and say copy local = false, then it doesn't show up in the output directory. It seems kind of hilarious that to not have a DLL show up in the output directory, I have to actually reference it :)
Edit:
Another clue. Oracle, in order to torture developers, do not have one DLL built for AnyCPU. They have an x86 and and an x64 version. In my case, I am referencing an x86 version and building for AnyCPU. However, if I build for x86 (to match the oracle dll), then the Oracle DLL is not copied to the output directory. when building in AnyCPU, MSBUILD says:
"warning MSB3270: There was a mismatch between the processor architecture of the project being built "MSIL" and the processor architecture of the reference "Oracle.DataAccess, Version=4.112.2.0, Culture=neutral, PublicKeyToken=89b483f429c47342, processorArchitecture=x86", "x86". This mismatch may cause runtime failures. Please consider changing the targeted processor architecture of your project through the Configuration Manager so as to align the processor architectures between your project and references, or take a dependency on references with a processor architecture that matches the targeted processor architecture of your project." So, it almost looks like Msbuild is ultimately deciding that, ok, you have a mismatch, so let me go ahead and copy this dll to your output directory, thereby guaranteeing that your application will blow up. :)
By not referencing it, you are letting it use implicit rules. Well, the implicit default here is "copy it local", because most dlls are not in the GAC. The IDE has the crazy notion of defaulting to "make it work", which means "assume it isn't going to be in the GAC".
If you want to use explicit rules, then yes: you need to tell it what you want. And the way you do that is by adding a reference, and then by setting the options you want.
After a good amount of investigation on what seems to be the same issue in a solution I work on, we've found that this issue appears to be caused by a new check which was introduced with the release of .NET 4.5, as mentioned here: at build time, references are now being checked to make sure that their processor type matches that of the projects.
While previously we didn't see any such error in the build log, on affected machines we now see a message indicating that the projects are building for MSIL and the DLL is built for x86 - rather than making available one DLL built for all CPUs, the third-party creator provides two separate DLLs for x86 and x64, both of which are in our GAC. Prior to this check being introduced, the system would use the DLL from the GAC and select the appropriate version, but it seems that after installing .NET 4.5, the build process is determining that neither version in the GAC is acceptable so it's pulling in a local copy.
We have not been able to identify any way to disable this behavior, so our resolution options are going to have to be to either just add a post-build event to delete the local copies, or (as mentioned in the question) to add references to the problem DLL to all projects which reference our "Library A" and then set those references to "copy local = false", which seems to also prevent copying a non-working local version into the output.
when we write
Assembly asm = AppDomain.CurrentDomain.Load(SomeByteArray);
when SomeByteArray read from .net .exe all is ok, and when from c++, error.
for this function is important using .net exe ?
if yes please other way to do this.
thanks
Are the calling assembly and the one you're loading both the same kind of build (x86, x64 or AnyCpu)? You can check the build by looking at them in ILDASM (look at the manifest, scroll to bottom and look for corflags).
If you built both assemblies yourself, then just check the property pages of the project for how you built it.
You either have to make sure they are the same, or if the .exe is AnyCpu, then the assembly you are loading must match whatever the OS is. Meaning it must be x86 on a 32-bit OS or x64 on a 64-bit OS.
(this is all assuming that SomeByteArray was loaded from the file correctly)