Using C# DLL in managed C++ (command line) - c#

I've looked over a few questions with a title just like this one, but they either do not talk about command line, or don't seem to work for me for some reason. From what I have read, it seemed as if I could "simply" do the following:
The dummy code (C#):
using System;
public static class Foo {
public static void Bar() {
Console.WriteLine("o3o");
}
}
More dummy code (Visual C++):
#using <test.dll>
int main() {
Foo::Bar();
return 0;
}
C# DLL compiled using:
csc /out:test.dll /t:library src\cs\Foo.cs
Visual C++ object file compiled using:
cl /Ox /clr /AI. /c src\vc\test.cpp
Executable compiled using:
link /out:test.exe test.obj
The following exception is thrown upon running the executable:
Unhandled Exception: System.TypeLoadException: Could not load type 'Foo' from assembly 'test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
at main()
at mainCRTStartup()
I have a strong feeling that I was supposed to also reference the DLL in the link phase, but I couldn't find any option for linking a DLL similar to GCC's -l option. Attempting to pass the DLL along with the .obj to link causes it to tell me that linking assemblies is not supported. Interestingly, though, if I build a .netmodule instead of a DLL, i.e. by:
csc /out:test.dll /t:module src\cs\Foo.cs
and changing the #using directive to
#using <test.netmodule>
the executable runs without error. This feels a bit weird, for I don't think anybody packages code in .netmodules (what are .netmodules, anyway?).

All of your steps should have worked, but one very simple issue is preventing your program from running.
Namely: Your C# DLL assembly name is test, and your C++/CLI exe assembly has the same name. They both have the same identity.
So when looking for Foo::Bar in the test assembly, the loader first checks if the assembly is loaded in the AppDomain. It is - it's your C++/CLI exe, and you can't have several assemblies with the same identity loaded simultaneously within the same AppDomain. Your C# dll wasn't even given a try.
Just change either one of them and everything will work fine:
link /out:test2.exe test.obj
As for what's a .netmodule, it's the format used for linking managed code statically, that's why you managed to link your C# code with your C++/CLI code without issues. It's roughly the equivalent of a .lib file for managed code.
And you're right, it's not used very often.

Related

C# wrapper library around unmanaged DLL requires unmanaged DLL to be in same directory during build

Usually when referencing a managed DLL that wraps an unmanaged DLL with PInvoke, you have to reference those two DLLs separately - the managed one as a standard <Reference/> in your csproj and the unmanaged one as linked <content/> (as outlined here). However, I recently came across a managed wrapper library which not only auto-copies the unmanaged DLL along with it during the build, but actually produces a build error when the unmanaged DLL is not present in the same directory! This is the Microsoft.Z3 library, which has a managed DLL (Microsoft.Z3.dll) wrapping an unmanaged DLL (libz3.dll) with PInvoke so you can use the library in C#.
If you put the two Z3 DLLs together in a directory, referencing only Microsoft.Z3.dll, then compile your project with msbuild, you'll get both DLLs in the output directory without referencing libz3.dll at all! Looking in the output produced by msbuild /verbosity:diag, I see the following references to libz3.dll:
Primary reference "Microsoft.Z3, Version=4.7.1.0, Culture=neutral, PublicKeyToken=9c8d792caae602a2". (TaskId:9)
Resolved file path is "C:\Users\ahelwer\source\test\Framework\lib\z3\Microsoft.Z3.dll". (TaskId:9)
Reference found at search path location "{HintPathFromItem}". (TaskId:9)
Found embedded scatter file "libz3.dll". (TaskId:9)
The ImageRuntimeVersion for this reference is "v4.0.30319". (TaskId:9)
...
Output Item(s):
_ReferenceScatterPaths=
C:\Users\ahelwer\source\test\Framework\lib\z3\libz3.dll
CopyLocal=true
FusionName=
HintPath=lib\z3\Microsoft.Z3.dll
OriginalItemSpec=C:\Users\ahelwer\source\test\Framework\lib\z3\Microsoft.Z3.dll
ResolvedFrom={HintPathFromItem}
Version=4.7.1.0 (TaskId:9)
Which somehow leads to it being copied:
Task "Copy" (TaskId:22)
Task Parameter:
SourceFiles=
C:\Users\ahelwer\source\test\Framework\lib\z3\Microsoft.Z3.dll
CopyLocal=true
FusionName=Microsoft.Z3, Version=4.7.1.0, Culture=neutral, PublicKeyToken=9c8d792caae602a2
HintPath=lib\z3\Microsoft.Z3.dll
ImageRuntime=v4.0.30319
OriginalItemSpec=Microsoft.Z3
ReferenceSourceTarget=ResolveAssemblyReference
ResolvedFrom={HintPathFromItem}
Version=4.7.1.0
C:\Users\ahelwer\source\test\Framework\lib\z3\libz3.dll
CopyLocal=true
FusionName=
HintPath=lib\z3\Microsoft.Z3.dll
OriginalItemSpec=C:\Users\ahelwer\source\test\Framework\lib\z3\Microsoft.Z3.dll
ResolvedFrom={HintPathFromItem}
Version=4.7.1.0 (TaskId:22)
Task Parameter:
DestinationFiles=
bin\Debug\Microsoft.Z3.dll
CopyLocal=true
FusionName=Microsoft.Z3, Version=4.7.1.0, Culture=neutral, PublicKeyToken=9c8d792caae602a2
HintPath=lib\z3\Microsoft.Z3.dll
ImageRuntime=v4.0.30319
OriginalItemSpec=Microsoft.Z3
ReferenceSourceTarget=ResolveAssemblyReference
ResolvedFrom={HintPathFromItem}
Version=4.7.1.0
bin\Debug\libz3.dll
CopyLocal=true
FusionName=
HintPath=lib\z3\Microsoft.Z3.dll
OriginalItemSpec=C:\Users\ahelwer\source\test\Framework\lib\z3\Microsoft.Z3.dll
ResolvedFrom={HintPathFromItem}
Version=4.7.1.0 (TaskId:22)
It gets more mysterious, because if I take libz3.dll out of the directory, the build fails with the following error:
"C:\Users\ahelwer\source\test\Framework\FrameworkTest.csproj" (default target) (1) ->
(_CopyFilesMarkedCopyLocal target) ->
C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\bin\Microsoft.Common.CurrentVersion.targe
ts(4358,5): error MSB3030: Could not copy the file "C:\Users\ahelwer\source\test\Framework\lib\z3\libz3.dll" because it
was not found. [C:\Users\ahelwer\source\test\Framework\FrameworkTest.csproj]
Even if I reference libz3.dll in the standard way with <content/> in my csproj!
Questions:
What is special about Microsoft.Z3.dll that enables it to require libz3.dll be in the same directory during build? Was it compiled with certain flags?
How can I add this effect to a managed wrapper library of my own?
Is there any way to remove this effect from Microsoft.Z3.dll, or would I have to recompile it in a different way?
This is what's CSC (the C# compiler) calls a "link resource". It works with any type of file.
So for example, if you have this kind of code in a DLL project:
using System.Runtime.InteropServices;
namespace Microsoft.Z3
{
public static class DoSomething
{
[DllImport("libz3.dll")]
public static extern int ReturnValue(int value);
}
}
And this C code exported from a Windows DLL:
#include "stdafx.h"
STDAPI ReturnValue(HRESULT value)
{
return value;
}
you can build the .NET DLL like this:
"<path to csc.exe>\csc.exe" DoSomething.cs -out:Microsoft.Z3.dll -target:library -linkresource:<path to libz3.dll>\libz3.dll
Now, when you reference this new Microsoft.Z3.dll, it will behave the same way as the real Z3 thing, it will copy the libz3.dll aside automatically.
Note AFAIK, Visual Studio has no support for this linkresource stuff.
Plus one other drawback is if you want to support multiple bitnesses, you'll have to ship two .NET DLL, one for x64 and one for x86, each one embedding its native counterpart (or you'll have to duplicate all DllImport stuff etc.).

Why can't my C# application load my C++ dll?

I want to use C++ code in a C# application, so I wrote a DLL in C++.
Because it's the first time that I create a C++ DLL, I started by creating a 'test' dll. Here's the code
extern "C"
{
__declspec(dllexport) void DisplayHelloFromDLL()
{
printf("Hello from DLL !\n");
}
}
Than I made a very simple C# application:
class DllTest {
[DllImport("Test.dll")]
public static extern void DisplayHelloFromDLL();
public static void UseDll() {
DisplayHelloFromDLL();
}
}
After, I built both applications and moved the executable and the dll in the same folder. But when I ran the executable, it showed the following message:
Unhandled exception: System.DllNotFoundException: Unable to load DLL 'Test.dll': The specified module could not be found
you need to store the DLL in debug folder.
and try this command to check that method is exported properly
dumpbin /exports Test.dll
you have to check the target platform of both dll and exe.
Edit: When Target are different the exception is BadImageFormatException
You will have the DllNotFoundException when the dll or one of its dependencies is not found.
For example, when you compile a debug version of the DLL with VisualStudio, it needs the debug runtimes (which are not installed on machine without the IDE).
you can check your dll dependencies using the Dependency Walker
This tools will also helps you to see the targetted system (x86/x64) and the exported functions (use it on the dll, the program will not show you the [DllImport] dependencies)

FileNotFoundException: Could not load file or assembly

I have a dll (Tracker.dll) compiled from C# and need to use it in native C++. I have that problem solved; since I cannot modify the C#, I am writing a managed C++ wrapper and exporting the classes accordingly. For a (simplified) example:
TrackerWrapper.h
#pragma once
#include <memory>
class __declspec(dllexport) TrackerWrapper {
public:
TrackerWrapper();
~TrackerWrapper();
void Track();
private:
struct Impl;
std::unique_ptr<Impl> pimpl;
};
TrackerWrapper.cpp
#using "Tracker.dll"
#include "TrackerWrapper.h"
#include <msclr\auto_gcroot.h>
using namespace System::Runtime::InteropServices;
struct TrackerWrapper::Impl {
msclr::auto_gcroot<Tracker^> tracker;
Impl() : tracker(gcnew Tracker()) {}
~Impl() {}
};
TrackerWrapper::TrackerWrapper() : pimpl(new Impl()) {}
TrackerWrapper::~TrackerWrapper() {}
void TrackerWrapper::Track() {
pimpl->tracker->Track();
}
Main.cpp
#include "TrackerWrapper.h"
int main() {
TrackerWrapper tracker;
tracker->Track();
return 0;
}
As long as all of the objects and binaries are in the same directory, after compiling with
cl /clr /LD TrackerWrapper.cpp
cl Main.cpp TrackerWrapper.lib,
everything runs perfectly. However, ideally we need the Tracker.dll as well as TrackerWrapper.lib and TrackerWrapper.dll to be in a separate directory, e.g. bin.
So, the directory structure might look like:
bin\
Tracker.dll
TrackerWrapper.dll
TrackerWrapper.lib
<other objects>
Main.cpp
TrackerWrapper.h
TrackerWrapper.cpp
I can get everything to compile by adding bin to my %PATH%, %LIB% and %LIBPATH% environment variables (or on the command line at compile time via /link and /AI), but when I execute the resulting executable I get the following error:
Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'Tracker, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
at TrackerWrapper.Impl.{ctor}(Impl* )
at TrackerWrapper.{ctor}(TrackerWrapper* )
I have tried changing #using "Tracker.dll" to a relative as well as an absolute path, but I get the same problem.
Any ideas?
Two things you can try:
Check if there's inner exception in FileNotFoundException, if does,
it might give you details.
Monitor the process with process monitor from System internal, it can will log all file activity of the the process, from the log, you can tell which file is missing. Remember to the set the filter to only monitor your process, other wise it will monitor all processes.
Your library is loaded by the .net infrastructure and it only searches by default at the app directory or at the GAC.
If your app were .net then you can specify a library path in the App.config but as your app is native don't know if the mixed dll will load the App.config or not, may be you can try.
From MSDN:
You can use the < probing > element in the application configuration
file to specify subdirectories the runtime should search when locating
an assembly.
If that does not work then your last option is to add the libraries to the GAC, but the library will not really be in the specified folder but copied to the GAC's folder.

JSON.net functions no longer working in VS2012 [duplicate]

I have added third party reference (Json newtonsoft) dll in my script component (using edit script option), but when i run the package, I am getting an error
Could not load file or assembly 'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The system cannot find the file specified.
Any suggestions?
I will not be able to add the dll in GAC.
I am using SQL Server 2008.
By "Running," I assume running from agent/command-line is failing? It should work from within BIDS/SSDT. The short answer is the DLL must be registered with the GAC or you can download the source code and add that project into the script task and then reference said project.
Looking at the project, it should be a strongly signed DLL (based on presences of Dynamic.snk) and thus capable of being added to the GAC. Oh, but you state you will not be able to add it into the GAC, implying it's a permission not a capability issue.
If that's the case, either compile the project in with the source or surround it with a web service wrapper and then reference the service.
I also saw this answer, seems you can try loading the references dynamically.
Automated deployment of mixed SSIS / DLL solution
You can using Reflection to load dll at runtime from file system without needing to install in GAC . This is helpful if permission to install in GAC is not availaible .
//Add a Static Constructor which is guaranteed to be called exactly once
// “before the first instance is created or any static members are referenced.”,
// so therefore before the dependent assemblies are loaded.
static ScriptMain()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
//Provide path to dll stored in folder on file system
static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string path = #"D:\DLL\";
return System.Reflection.Assembly.LoadFile(System.IO.Path.Combine(path, "Newtonsoft.dll"));
}
Ofcourse you need to also Add Reference to dll in script task .

IronPython, importing Modules

I follow the example from the best answer here to a T, compiling with Pyc.py.
Build Python scripts and call methods from C#
I get an exception at pyScope = pyEngine.ImportModule("MyClass");
no module named MyClass
I believe this to be a bug as sometimes recompilation with Pyc.py will produce a dll ImportModule recognizes, but other times it doesn't.
CONCLUSION: As noted below by digEmAll, compiling modules with Pyc.py to be used in this fashion produces random results. Call clr.CompileModules manually instead.
OK,
I got it.
The module name is the (case sensitive) name of the original .py module, not the compiled dll.
I mean, if your original module name was myClass.py, then you compiled it in MyClass.dll, you must ImportModule("myClass") not ImportModule("MyClass")
EDIT:
the previous code refers to the following compile method:
import clr
clr.CompileModules("CompiledScript.dll", "script.py")
On the contrary, using pyc.py, the generated dll contains a module called __main__ instead of the .py file name.
That's very strange...
IIRC, in python a module call itself __main__ if it's running standalone (i.e. not called by another), but I still don't grasp the connection...

Categories