Call native code specified at runtime - c#

I'm developing an application that will allow users to call external code from both managed and native .dlls. The users will be able to specify what library/method/function to call at runtime (it will be stored in a configuration file).
I know how to do this using pinvoke for native libraries if I know what dll/function I want to call at compile time, but I can't find any information on how to do this at runtime.
Essentially what I'd like to do is call a method:
int result = ExecuteNativeFunction("someLibrary.dll", "foo");
and have it do something equivalent to:
[DllImport("someLibrary.dll")]
static extern int foo();
...
int result = foo();

Would this be what you are looking for? Using System.Reflection.Emit, you can dynamically compile code that defines a new PInvoke method. See the class DllRegServer in the linked file for details.

Related

C# wrapper (pinvoke) for Debian apt library libapt-pkg

I need to retrieve package information in my C# application running on Linux.
I've tried to use Python.NET which wrap python-apt which wrap libapt-pkg.so, and it works but I would prefer to go more direct by pinvoke libapt-pkg.
But I need help on my DLLImport statement.
When using python-apt I first initialize the apt module, then instantiat the Cache() class and then called the Cache.keys() function which retrieve all package-names as a string array.
If I can get this to work using pinvoke I believe I have a good chance getting the rest working too (retrieve the more advanced stuff)
Just to show some code which doesn't work, but just to get started:
[DllImport("libapt-pkg")]
public static extern dynamic GetPkgCache();
dynamic AptCache = GetPkgCache();
I get the error:
System.Runtime.InteropServices.MarshalDirectiveException: 'Cannot
marshal 'return value': Invalid managed/unmanaged type combination
(Marshaling to and from COM VARIANTs isn't supported).'
I've been looking in the header file cachefile.h of the apt project. Maybe these lines are useable for my DllImport statement?
..
inline operator pkgCache &() const {return *Cache;};
inline operator pkgCache *() const {return Cache;};
..
inline pkgCache* GetPkgCache() { BuildCaches(NULL, false); return Cache; };
..
But I'm new to C/C++ so it doesn't make much sense for me at present.
UPDATE:
Thanks for the feedback.
[DllImport("libapt-pkg")]
public static extern IntPtr GetPkgCache();
Now I get the error
System.EntryPointNotFoundException: 'Unable to find an entry point
named 'GetPkgCache' in shared library 'libapt-pkg'.'

Fetching C# Logs in Python

I have built a python library that uses C# code(which is built and stored as a dll), using pythonnet. In that library, I generate logs using the python logger.
mylibrary.py
logger = logging.getLogger('mylibrary')
logger.info('logging from my library')
The root logger is configured from the user code. For example, the handlers for the root logger is set by the user using logger's "addhandler()" method specifying the format, output file etc. Inside my library, I just log (logger.info()...) without configuring anything and the root handler set by the user takes care of writing this to the file.
usercode.py
root_logger = getLogger()
root_logger.addHandler(FileHandler('abc.log'))
root_handler.setFormat(...)
The user can control what my library can log by setting the level of the logger used by my library. The line below in usercode.py sets the logging level of my library's logger to critical so that the library can't log anything below it (logger.info() won't get into abc.log).
getLogger('mylibrary').setLevel(CRITICAL)
The problem comes now. Since I am using C# code in my library,
I want to capture the C# logs into abc.log
I also want to configure the C# log just like I did for python logs
So the the line
getLogger('mylibrary').setLevel(CRITICAL)
in usercode.py should now make sure that only the critical logs in both the python as well as C# get into abc.log
Is there a way to achieve this?
No, you cannot log from both Python and C# at the same time to the same file. The reason for this is that Python's logging (and likely the C# logging too) is not equipped for concurrent logging - even if the log file is not 'locked' by one of them, there is a chance of getting different logs mixed together due to multiple writers.
If you do not own the C# dll you're probably out of luck - unless it would allow you to configure the log file/level from a C# program, there is no magic that Python can do to fix it. However, if you control the source and can build a new dll, consider changing the C# class to allow you to pass in a delegate/lambda (assuming this is implemented in PythonNet), which will simply call back into Python's logger function.
Example:
c# code:
public class CoolImportedFeature
{
private readonly Action<string> LogCallback;
public CoolImportedFeature(string inputA, int inputB, Action<string, string> logCallback)
{
LogCallback = logCallback;
// do other constructor stuff
}
public void SomeMethod()
{
// do something
LogCallback("critical", "An error occurred");
}
}
python code:
def log_callback(log_level, message):
getattr(logger, log_level)(message)
import CoolImportedFeature
feat = CoolImportedFeature("hello", 1, log_callback)
feat.SomeMethod()
Something like that - there is no magic translation between Python's log levels and C#'s, so you will need to do some translation there (or the getattr reflection I used above).

Java JNI call to C# COM fails, when COM is registered without codebase option of regasm

Function calls from Java to C# through JNI-C++/CLI are failing when the C# COM is not registered using regasm with the codebase option. I've built a sample following the instructions in P2: Calling C# from Java with some changes.
Numero uno: C#
Change the C# dll into a COM by creating an interface, IRunner, and making the library assembly COM-visible.
namespace RunnerCOM
{
public interface IRunner
{
String ping();
}
public class Runner:IRunner
{
static void Main(string[] args)
{
}
public Runner() { }
public String ping()
{
return "Alive (C#)";
}
}
}
Numero due: Java
No changes made to the Java section.
Numero tre: C++
This part was changed to create a new instance of the RunnerCOM.Runner class and use that result. Here is a good tutorial on how to call managed code from unmanaged code: http://support.microsoft.com/kb/828736
#include "stdafx.h"
#include "Runner.h"
#pragma once
#using <mscorlib.dll>
#import "RunnerCOM.tlb"
JNIEXPORT jstring JNICALL Java_Runner_ping(JNIEnv *env, jobject obj){
RunnerCOM::IRunnerPtr t = RunnerCOM::IRunnerPtr("RunnerCOM.Runner");
BSTR ping = t->ping();
_bstr_t temp(ping, true);
char cap[128];
for(unsigned int i=0;i<temp.length();i++){
cap[i] = (char)ping[i];
}
return env->NewStringUTF(cap);
}
Now to my questions,
The code above fails with a _com_error exception, Class not registered (0x80040154) unless the codebase option is enabled during regsitration of RunnerCOM.dll, with regasm.exe. Why is this? If the code is not ran from JNI, I tested it as an exe, it works fine. The RunnerCOM.dll is simply found in the working directory.
Type casting _bstr_t temp to char* fails. For example, char *out = (char*) temp; Similar to the issue above, it works fine when it's built and executed as an exe but crashes the JVM when it's a JNI call.
By the way this is what I used to run it as an executable:
int main(){
RunnerCOM::IRunnerPtr t = RunnerCOM::IRunnerPtr("RunnerCOM.Runner");
BSTR ping = t->ping();
_bstr_t temp(ping, false);
printf(temp);
return 0;
}
Codebase creates a Codebase entry in the registry. The Codebase entry specifies the file path for an assembly that is not installed in the global assembly cache, so when you specify the codebase, the system will find the DLL based on the path. If not, it will try to locate the dll in the GAC and current working directory. In JNI, I think the current working directory is not the folder where the DLL is. You can use process explorer to find what is the current working directory, also, you can use process monitor to find out which directories the exe is looking into to find the dll.
The code converting _bstr_t to char*, the char* string cap is not ended with '\0', I think this might cause problem in JNI. Uses the _bstr_t operator (char *), you can obtain a null terminated string from the _bstr_t object. Please check the msdn example for details.
You mentioned C++/CLI, C++/Cli and COM warpper are two different ways to interop with C# code. If you're using C++/CLI as a bridge, you doesn't need to register C# DLL as COM, please see this: Calling .Net Dlls from Java code without using regasm.
If you're using COM, you should call CoInitialize() to init COM first in your code.

How to use dll created using C# in Visual C++?

Here are the steps what I have done.
I have created a .dll file using C# with the content as
public static int MyFunction(int dummy)
{
MessageBox.Show("I am in dll");
return 0;
}
I have also created an MFC application (exe) with content as
int main()
{
int a = MyFunction(0);
return 0;
}
Is this right way to do the call ?
Note:
i. I have changed my MFC application to /cl (common language run time support)
ii. I have also added C# file in the MFC's Reference.
Problems I faced:
Error 1 error C3861: 'MyFunction': identifier not found
Warning 2 warning C4793: 'MyDialog::`vcall'{132}'' : function compiled as native :
I have used the following command in the MyDialog.cpp file as a last line, the warning is solved.
#pragma unmanaged
Now how to solve the Error?
The C# code is placed inside a class, say MyClass. If you want to call a static member of this class in C++/CLI, you need to use MyClass::MyFunction(0).
Finally, you would need to add the namespace: MyNamespace::MyClass::MyFunction(0).
You can compile your C++ program with the /clr flag and use C++/CLI to simply call any .NET code (as long as you include a reference to it).
You could also use a COM callable wrapper for your C# code (compiled in a DLL) -> check this article

Namespace problems in QuickFix's .Net DLL wrapper

I'm battling with the QuickFix engine in .Net (using the C++ DLL wrapper) to craft a TradeCaptureReportRequest message:
var req = new QuickFix44.TradeCaptureReportRequest();
req.set(new QuickFix.SubscriptionRequestType(QuickFix.SubscriptionRequestType.SNAPSHOT_PLUS_UPDATES)); // 263
req.set(new QuickFix.TradeRequestID("testing" + DateTime.Now.Second.ToString())); // 568
var nodates = new QuickFix44.TradeCaptureReportRequest.NoDates();
nodates.set(new QuickFix.TradeDate("20130201"));
req.set(nodates); // 580
Everything seems to look good until I call req.set(nodates), which causes a compiler error saying that "NoDates cannot be converted to a NoDates".
This boggles my mind since when I navigate to the metadata of the TradeCaptureRequest within the QuickFix dll, i am shown this.
public void set(NoDates value);
// as a member of QuickFix44.TradeCaptureReportRequest
if I go to the definition of NoDates it sends me to the QuickFix44.TradeCaptureReportRequest.NoDates Class defined within the QuickFix44.TradeCaptureReportRequest class.
however there is a NoDates Class defined within the QuickFix namesapace which compiles just fine when I do the following.
req.set(new QuickFix.NoDates(1));
I'm using Quickfix v4.0.30128 and the .Net wrapper for the C++ DLL.
If you look at the C# code for TradeCaptureReportRequest.set you'll find that it would like a QuickFix.NoDates type for the NoDates value:
// line: 1993
public void set(QuickFix.NoDates value)
{ setField(value); }
So change your C# to the following:
var nodates = new QuickFix.NoDates();
nodates.set(new QuickFix.TradeDate("20130201"));
req.set(nodates);
It appears you're using the QuickFix .Net wrapper over the C++, which is an abomination of .Net programming guidelines. I highly recommend you switch to QuickFIX/N, which is less horrible (but still awful looking).

Categories

Resources