I am currently programming in C++ and C#. Using native C++ for the numerical computing part.
Originally I intended to use C++/CLI to make a wrapper to the native C++ classes, but I found it would result in a 2 to 4 times slowdown.
So I decided to compile my native C++ to a DLL and call in .NET/C# via P/Invoke. I will do data preprocessing in C# and number crunching in the native DLL.
The problem is that when I deliver my work for others to use, I'd like it to be a single DLL.
Is this possible? BTW, I have all source code to the native C++.
You can mix languages in the same assembly in .NET but not naturally. I would avoid this unless absolutely necessary.
See:
http://www.hanselman.com/blog/MixingLanguagesInASingleAssemblyInVisualStudioSeamlesslyWithILMergeAndMSBuild.aspx
I keep assemblies from different languages separate.
You can do this quite easily, and entirely supportedly, by producing netmodules. Compile your combined C++ and C++/CLI code into a .obj (C++ netmodules have file extension .obj, C# netmodules have file extension .netmodule) and then link this into your C# project.
Details: http://blogs.msdn.com/junfeng/archive/2005/05/19/420186.aspx.
Worked example: http://blogs.msdn.com/b/junfeng/archive/2006/05/20/599434.aspx.
Another option I have used in the past is have your program/dll act as a self extracting archive. Load the managed dll as a compressed resource and decompress it in the temporary folder of whatever machine you are running on.
I have an app that uses AlphaVSS which has a few native DLL's built in to it. I wanted the app to be fully portable as a single exe and not require an install. Here is how I handled it.
public static void ExtractResources()
{
Directory.CreateDirectory(Path.Combine(Program.DataPath, FolderName));
//Extract the runtime in case it is not installed on the destination computer
if (NativeMethods.Is64BitOperatingSystem)
{
FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\Microsoft.VC90.CRT.manifest"),
Resources.x64.X64Resources.Microsoft_VC90_CRT_manifest);
FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\msvcm90.dll"), Resources.x64.X64Resources.msvcm90_dll);
FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\msvcp90.dll"), Resources.x64.X64Resources.msvcp90_dll);
FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\msvcr90.dll"), Resources.x64.X64Resources.msvcr90_dll);
FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\AlphaVSS.52.x64.dll"), Resources.x64.X64Resources.AlphaVSS_52_x64_dll);
FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\AlphaVSS.60.x64.dll"), Resources.x64.X64Resources.AlphaVSS_60_x64_dll);
}
else
{
FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\Microsoft.VC90.CRT.manifest"),
Resources.x86.X86Resources.Microsoft_VC90_CRT_manifest);
FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\msvcm90.dll"), Resources.x86.X86Resources.msvcm90_dll);
FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\msvcp90.dll"), Resources.x86.X86Resources.msvcp90_dll);
FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\msvcr90.dll"), Resources.x86.X86Resources.msvcr90_dll);
FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\AlphaVSS.51.x86.dll"), Resources.x86.X86Resources.AlphaVSS_51_x86_dll);
FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\AlphaVSS.52.x86.dll"), Resources.x86.X86Resources.AlphaVSS_52_x86_dll);
FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\AlphaVSS.60.x86.dll"), Resources.x86.X86Resources.AlphaVSS_60_x86_dll);
}
FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\AlphaVSS.Common.dll"), Resources.VssResourcesCommon.AlphaVSS_Common_dll);
FileUtils.DecompressFile(Path.Combine(Program.DataPath, FolderName + @"\ClientDataSnapshotVssAgent.exe"), Resources.VssResourcesCommon.ClientDataSnapshotVssAgent_exe);
}
Now in my code I had a agent exe running in the temp folder that was referencing those extracted dlls, however you could easily use your P/Invoke and specify the full path you extracted it to in the P/Invoke signature.
As an extra tip, I wrote a small program that will simply gzip whatever file you pass in to it, I then put the following in the Post-build event for the agent program.
"$(SolutionDir)\DataCompressor\DataCompressor.exe" "$(TargetPath)" "$(SolutionDir)\ClientDataSnapshot\VSS\Resources\$(TargetFileName).gz"
You could do something similar for your post build event for your native dll, just have it update the gz file in your managed project every time it finishes building and make sure the native dll comes before the managed dll in the build order and you will always have the most up to date copy as a resource in your managed project.
using System;
using System.IO;
using System.IO.Compression;
namespace DataCompressor
{
class Program
{
//This program is used to compress the compiled VssAgent for storage in the snapshot program
static void Main(string[] args)
{
//Will quit directly if any args are invalid.
ValidateArgs(args);
using (var sourceFile = File.OpenRead(args[0]))
using (var destFile = new FileStream(args[1], FileMode.Create))
using (var gz = new GZipStream(destFile, CompressionMode.Compress))
{
sourceFile.CopyTo(gz);
}
}
private static void ValidateArgs(string[] args)
{
if (args.Length == 2)
{
if (File.Exists(args[0]) == false)
{
Console.Error.WriteLine("The source file did not exist.");
Environment.Exit(-2);
}
if (Directory.Exists(Path.GetDirectoryName(args[1])) == false)
{
Console.Error.WriteLine("The destination directory did not exist.");
Environment.Exit(-3);
}
}
else
{
Console.Error.WriteLine("You must pass two arguments.");
Environment.Exit(-1);
}
}
}
}
I recently went through a similar issue where Native C code inside a C++/CLI DLL was running very slowly. Turns out Visual Studio was compiling my C functions as Managed.
To resolve this we used Pragma unmanaged to force native compilation. This restored the speed without the need to move Native code into its own DLL without CLI
// Force native compilation
#pragma managed(push, off)
// Your native code
void Foo()
{
}
// Restore managed compilation
#pragma managed(push, on)
There is no way that I know of that you can use to combine managed code written in C# and native code written in C++ (or any language). The closest you can come is to use native C++ with managed C++ in a single assembly, which you say caused a substancial slowdown in your code.