I am kinda stuck on not being able to dispose .NET 3.5 dlls from the process.
AppDomain support is off in unity, there is no way to unload a dll from the process using the .NET api, because the C# functions are not implemented.
Anyone could get me some hints on how / where should I start to remove the dll from the memory / process somehow, so I can re-load the dll whenever I want?
Alright, after this time I thought that this is some sort of heavy research, and there are no publications related to this. So here is what you have to do. First head over to https://github.com/mono/mono/branches/all and determine which mono version you are going to require. In my case I was doing this for an old game, and I needed the 2014 version of mono.
My project is discontinued so there is no point for me to keep this as a secret. The following examples will show you a way, but It probably won't be enough for you to get what you want on a newer mono version.
Using this way, you are able to unload every dll in a new AppDomain. You can extend It to create multiple appdomains, or unload only a specific dll. If It is working on a newer mono version then please let me know! Credits go to me, and 4g3v.
Once you have done that you are going to need exactly the same environment, and compilers for that version. In my case that was the Visual Studio 2010 compilers, and I didn't need to refactor most of the things.
You will need more than just following my instructions, you will have to play with mono, and get to know how the project works.
So hence as stated: C# API doesn't support AppDomains, but mono by default does. You just need to make some improvements, and extensions for It. Here is what you need to do:
Define two new functions in mono.def for example
mono_rb_create_domain and
mono_rb_unload_domain
The above two functions will be responsible to create, and dispose a domain.
Head over to: mono/metadata/object.c
Find function mono_runtime_unhandled_exception_policy_set and add (We will create the function later below):
mono_add_internal_call("YourDLLNameSpace.Icalls::mono_rb_load_plugin", ves_icall_mono_rb_load_plugin); //Last mono function unity calls before adding their own icalls (mono_runtime_unhandled_exception_policy_set). Adding them at runtime doesn't work, so this should be a pretty good place.
The above code will define a C# function that will be able to handle the loading of a custom DLL loaded in our own AppDomain. Make sure that your C# class, and function is public. Reminder: This should be somewhere in your unity project already. (For example Assembly-CSharp or whatever). It is crucial, because this will be handling the loading of your new dlls, and they will go to a new appdomain.
[MethodImpl(MethodImplOptions.InternalCall)]
public extern Assembly mono_rb_load_plugin(IntPtr data, uint dataLen);
Alright, we have to add some additional checks to avoid crashes in unity/unity_liveness.c Look for function mono_add_process_object and make it look like the following. This might differ in newer mono versions. =) What we have done here is basically ensuring that the received object has a VTable (which should be the class), and It isn't null.
static void mono_add_process_object (MonoObject* object, LivenessState* state)
{
gboolean has_references = 0;
MonoClass* klass; // Define the class
if (object && !IS_MARKED(object))
{
klass = GET_VTABLE(object)->klass; // Get the VTable
// Ensure that the class isn't f***ed up. Read: https://en.wikipedia.org/wiki/Hexspeak
if(klass == NULL || klass == 0xBAADF00D || klass == 0xFEEEFEEE)
{
return;
}
has_references = klass->has_references;
if(has_references || should_process_value(object,state) != LIVENESS_DONT_PROCESS)
{
if (array_is_full(state->all_objects))
array_safe_grow(state, state->all_objects);
array_push_back(state->all_objects, object);
MARK_OBJ(object);
}
// Check if klass has further references - if not skip adding
if (has_references)
{
if(array_is_full(state->process_array))
array_safe_grow(state, state->process_array);
array_push_back(state->process_array, object);
}
}
}
The above code will ensure that the processed class isn't faulty, or points somewhere else where It shouldn't.
Let's create our domain handlers.
Make sure to create this under the mono project.
My header file was named as mono_rustbuster.h, and contained:
static MonoDomain* rustBusterDomain;
GHashTable* pluginHashTable;
void mono_method_info_object();
MonoReflectionAssembly* ves_icall_mono_rb_load_plugin(void* objectPtr, char* data, guint32 dataLen) MONO_INTERNAL;
Then we created mono_rustbuster.c, and wrote the following:
#include "metadata\metadata-internals.h"
#include "metadata\image.h"
#include "metadata\assembly.h"
#include "metadata\debug-helpers.h"
#include "metadata\class-internals.h"
#include "metadata\object-internals.h"
static MonoDomain* rustBusterDomain;
GHashTable* pluginHashTable;
void mono_method_info_object()
{
}
int mono_rb_create_domain()
{
pluginHashTable = g_hash_table_new(g_str_hash, g_str_equal);
rustBusterDomain = mono_domain_create_appdomain("PluginDomain", NULL);
return 0x01;
}
int mono_rb_unload_domain()
{
mono_domain_unload(rustBusterDomain);
return 0x01;
}
MonoReflectionAssembly* ves_icall_mono_rb_load_plugin(void* objectPtr, char* data, guint32 dataLen)
{
MonoAssembly* ass;
MonoImage* img;
MonoImageOpenStatus status;
MonoDomain* current;
char *assNameBuf;
current = mono_domain_get();
mono_domain_set(rustBusterDomain, FALSE);
img = mono_image_open_from_data_full(data, dataLen, TRUE, NULL, FALSE);
ass = mono_assembly_load_from_full(img, "", &status, FALSE);
assNameBuf = (char*)malloc(256);
sprintf(assNameBuf, "%s", ass->aname.name);
g_hash_table_insert(pluginHashTable, (gpointer)assNameBuf, (gpointer)ass);
mono_domain_set(current, FALSE);
return mono_assembly_get_object(rustBusterDomain, ass);
}
After this setup your loaded DLLs might whine about missing references. This mostly happens when you load a new DLL in all by yourself using these functions. We added some layers into mono/metadata/assembly.c for this fix.
Find mono_assembly_load_reference This method works with the assembly's references, find where It is calling the reference variable and add:
if(reference == NULL && strstr(image->name, "data-"))
{
reference = (MonoAssembly*)g_hash_table_lookup(pluginHashTable, aname.name);
}
Hint: Mono add's data- to all of the dll's, so that way we can use that memory pointer to find the references. Basically fixes the unresolved referencess.
Head over to mono/metadata/class.c and find: mono_class_is_assignable_from
Before function would return data using the last function check if the class names are equal.
The last return looks something like this:
return mono_class_has_parent (oklass, klass);
Add:
if(!mono_class_has_parent (oklass, klass))
{
if(strstr(klass->image->name, "data-"))
{
if(!strcmp((oklass)->supertypes [(klass)->idepth - 1]->name, klass->name))
{
//OutputDebugStringA("mono_class_is_assignable_from(): Class names are equal so true is returned");
return TRUE;
}
}
}
The above code will add fixes to ScriptableObject cast exceptions.
You are sort of done. You may encounter additional issues. Here is how it works in C#:
[DllImport("mono.dll")]
internal static extern int mono_rb_create_domain();
[DllImport("mono.dll")]
internal static extern int mono_rb_unload_domain();
Handle loading of a new dll:
var icalls = new Icalls();
int domaincreation = RustBuster.mono_rb_create_domain();
byte[] bytes = getyourdllsbytearraysomehow;
IntPtr pluginMem = Marshal.AllocHGlobal(bytes.Length);
for (int i = 0; i < bytes.Length; i++)
{
Marshal.WriteByte(pluginMem, i, bytes[i]);
}
assembly = icalls.mono_rb_load_plugin(pluginMem, (uint) bytes.Length);
Unload:
int domaincheck2 = RustBuster.mono_rb_unload_domain();
Related
Getting a list of open windows in .Net Framework on Windows was relatively easy. How can I do the same in .Net Core/.Net 5 or later on macOS?
To clarify, I'm looking for a way to retrieve a list of all open windows owned by any running application/process. I don't have much experience of macOS development - I'm a Windows developer - but I've tried to use the NSApplication as suggested by this answer.
I created a .Net 6.0 Console application in VS2022 on macOS Monterey (12.2), added a reference to Xamarin.Mac and libxammac.dylib as described here - which describes doing this in Xamarin rather than .Net, but I don't see any other option to create a Console application. With the simple code:
static void Main(string[] args)
{
NSApplication.Init();
}
I get the output
Xamarin.Mac: dlopen error: dlsym(RTLD_DEFAULT, mono_get_runtime_build_info): symbol not found
I've no idea what this means. I'm not even sure this approach has any merit.
Does anyone know if it's possible to use NSApplication from a .Net Core/6.0 application, and if so whether NSApplication will give me the ability to read a system-wide list of open windows? If not, is there another way to accomplish this?
This is only for my own internal use, it doesn't need to be in any way portable or stable outside of my own environment.
In the link you refer to, there is an important note:
... as Xamarin.Mac.dll does not run under the .NET Core runtime, it only runs with the Mono runtime.
Because you try to run Xamarin.Mac.dll under .net-core, you get this dlopen error.
No System-wide List via NSApplication
The linked answer with NSApplication.shared.windows is incorrect if you want to read a system-wide list of open windows. It can only be used to determine all currently existing windows for the application from which the call is made, see Apple's documentation.
Alternative solution
Nevertheless, there are several ways to access the Window information in macOS. One of them could be a small unmanaged C-lib that gets the necessary information via CoreFoundation and CoreGraphics and returns it to C# via Platform Invoke (P/Invoke).
Native Code
Here is example code for a C-Lib that determines and returns the names of the window owners.
WindowsListLib.h
extern char const **windowList(void);
extern void freeWindowList(char const **list);
The interface of the library consists of only two functions. The first function called windowList returns a list with the names of the window owners. The last element of the list must be NULL so that you can detect where the list ends on the managed C# side. Since the memory for the string list is allocated dynamically, you must use the freeWindowList function to free the associated memory after processing.
WindowsListLib.c
#include "WindowListLib.h"
#include <CoreFoundation/CoreFoundation.h>
#include <CoreGraphics/CoreGraphics.h>
static void errorExit(char *msg) {
fprintf(stderr, "%s\n", msg);
exit(1);
}
static char *copyUTF8String(CFStringRef string) {
CFIndex length = CFStringGetLength(string);
CFIndex size = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
char *buf = malloc(size);
if(!buf) {
errorExit("malloc failed");
}
if(!CFStringGetCString(string, buf, size, kCFStringEncodingUTF8)) {
errorExit("copyUTF8String with utf8 encoding failed");
}
return buf;
}
char const **windowList(void) {
CFArrayRef cfWindowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
CFIndex count = CFArrayGetCount(cfWindowList);
char const **list = malloc(sizeof(char *) * (count + 1));
if(!list) {
errorExit("malloc failed");
}
list[count] = NULL;
for(CFIndex i = 0; i < count; i++) {
CFDictionaryRef windowInfo = CFArrayGetValueAtIndex(cfWindowList, i);
CFStringRef name = CFDictionaryGetValue(windowInfo, kCGWindowOwnerName);
if(name) {
list[i] = copyUTF8String(name);
} else {
list[i] = strdup("unknown");
}
}
CFRelease(cfWindowList);
return list;
}
void freeWindowList(char const **list) {
const char **ptr = list;
while(*ptr++) {
free((void *)*ptr);
}
free(list);
}
CGWindowListCopyWindowInfo is the actual function that gets the window information. It returns a list of dictionaries containing the details. From this we extract kCGWindowOwnerName. This CFStringRef is converted to a dynamically allocated UTF-8 string by the function copyUTF8String.
By convention, calls like CGWindowListCopyWindowInfo that contain the word copy (or create) must be released after use with CFRelease to avoid creating memory leaks.
C# Code
The whole thing can then be called on the C# side something like this:
using System.Runtime.InteropServices;
namespace WindowList
{
public static class Program
{
[DllImport("WindowListLib", EntryPoint = "windowList")]
private static extern IntPtr WindowList();
[DllImport("WindowListLib", EntryPoint = "freeWindowList")]
private static extern void FreeWindowList(IntPtr list);
private static List<string> GetWindows()
{
var nativeWindowList = WindowList();
var windows = new List<string>();
var nativeWindowPtr = nativeWindowList;
string? windowName;
do
{
var strPtr = Marshal.ReadIntPtr(nativeWindowPtr);
windowName = Marshal.PtrToStringUTF8(strPtr);
if (windowName == null) continue;
windows.Add(windowName);
nativeWindowPtr += Marshal.SizeOf(typeof(IntPtr));
} while (windowName != null);
FreeWindowList(nativeWindowList);
return windows;
}
static void Main()
{
foreach (var winName in GetWindows())
{
Console.WriteLine(winName);
}
}
}
}
The GetWindows method fetches the data via a native call to WindowList and converts the C strings to managed strings, then releases the native resources via a call to FreeWindowList.
This function returns only the owner names, such as Finder, Xcode, Safari, etc. If there are multiple windows, the owners will also be returned multiple times, etc. The exact logic of what should be determined will probably have to be changed according to your requirements. However, the code above should at least show a possible approach to how this can be done.
Screenshot
I'm trying to build a a simple C# application to test passing data between languages. I have a simple main in C# calling a function that I wish to pass to C++:
using System;
namespace CS_Console
{
class Program
{
static void Main(string[] args)
{
CPP_Sum(5, 2); //pseudo code
}
}
}
and then the function in a C++ project:
CPP_Sum(int x, int y)
{
return x+y;
}
The problem is, I don't even know where to start on how to pass these between each other.
This is being done via two projects, CS_Console and CPP_Console, in the same solution in Visual Studio.
It has already been mentioned by #steveo314. The easiest way to call a C++-based function from C# is using PInvoke. I assume you can create a DLL without any documentation but this looks like it will give you all the information you need to use your DLL from C#.
Basically:
Put your function as a C++ DLL; then
Use a DllImport attribute to create a function prototype in your C# code.
Make sure you export your C++ function definitions.
Daniel's answer above is also correct, but if you really want to dive into the guts of this, you may want to learn C++/CLI, as that's more or less C++ running on top of the .NET framework, with more precise interop, and depending on where/what your DLL is, communication both ways (P/Invoke is just calling C++, not calling back into .NET... usually).
C++/CLI allows this kind of thing:
public class Program
{
public void main(String [] args)
{
NativeWrapper wrapper = new NativeWrapper(); // C++/CLI ref class
wrapper.doStuff("hey there!");
}
}
In a C++/CLI DLL, in the .h file:
// This is C++/CLI
class NativeClass; // Forward declare
ref class NativeWrapper
{
public:
NativeWrapper(); // Constructor - MUST be in .cpp file, as size of NativeClass not known here
~NativeWrapper(); // Destructor - MUST be in .cpp file, same reason as above
void doStuff(System::String^ inputString);
private:
NativeClass* mp_impl;
};
In a C++/CLI .cpp file:
class NativeClass // True C++ class, could be defined elsewhere!
{
public:
void nativeDoStuff(const std::string& inputString)
{
std::cout << "Previous input was: '" << cachedResult << "'";
cachedResult = inputString;
}
private:
std::string cachedResult{};
};
NativeWrapper::NativeWrapper()
{
mp_impl = new NativeClass();
}
NativeWrapper::~NativeWrapper()
{
delete mp_impl;
mp_impl = nullptr;
}
// Utility function!
std::string _convertClrString(System::String^ instr)
{
marshal_context context;
std::string mystring{ context.marshal_as<const char*>(instr) };
return mystring;
}
void NativeWrapper::doStuff(System::String^ inputString)
{
auto native_string = _convertClrString(inputString);
mp_impl->nativeDoStuff(native_string);
}
It's a toy example (necessary includes removed), but hopefully that gets the idea across of what's possible with C++/CLI. The link I put near the top is the main start of the resources at Microsoft for doing this. Keep in mind what files are compiling as .NET, which aren't (you can do it per-file), and what that all means.
I'm using an Intel Ivy Bridge CPU and want to use the RDRAND opcode (https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide) in C#.
How can I call this CPU instruction via C#? I've seen an example of executing assembly code from c# here: x86/x64 CPUID in C#
But I'm not sure how to use it for RDRAND. The code doesn't need to check whether the CPU executing the code supports the instruction or not.
I've seen this C++ example of executing assembly byte code coming from drng_samples of Intel:
int rdrand32_step (uint32_t *rand)
{
unsigned char ok;
/* rdrand edx */
asm volatile(".byte 0x0f,0xc7,0xf0; setc %1"
: "=a" (*rand), "=qm" (ok)
:
: "edx"
);
return ok;
}
How can the example of executing assembly code in C# be combined with the C++ code coming from the Intel drng sample code?
There are answers out on SO that will generate (unmanaged) assembly code at runtime for managed code to call back into. That's all very interesting, but I propose that you simply use C++/CLI for this purpose, because it was designed to simplify interop scenarios. Create a new Visual C++ CLR class library and give it a single rdrandwrapper.cpp:
#include <immintrin.h>
using namespace System;
namespace RdRandWrapper {
#pragma managed(push, off)
bool getRdRand(unsigned int* pv) {
const int max_rdrand_tries = 10;
for (int i = 0; i < max_rdrand_tries; ++i) {
if (_rdrand32_step(pv)) return true;
}
return false;
}
#pragma managed(pop)
public ref class RandomGeneratorError : Exception
{
public:
RandomGeneratorError() : Exception() {}
RandomGeneratorError(String^ message) : Exception(message) {}
};
public ref class RdRandom
{
public:
int Next() {
unsigned int v;
if (!getRdRand(&v)) {
throw gcnew RandomGeneratorError("Failed to get hardware RNG number.");
}
return v & 0x7fffffff;
}
};
}
This is a very bare-bones implementation that just tries to mimic Random.Next in getting a single non-negative random integer. Per the question, it does not attempt to verify that RDRAND is actually available on the CPU, but it does handle the case where the instruction is present but fails to work. (This "cannot happen" on current hardware unless it's broken, as detailed here.)
The resulting assembly is a mixed assembly that can be consumed by managed C# code. Make sure to compile your assembly as either x86 or x64, same as your unmanaged code (by default, projects are set to compile as "Any CPU", which will not work correctly since the unmanaged code has only one particular bitness).
using System;
using RdRandWrapper;
class Program {
static void Main(string[] args) {
var r = new RdRandom();
for (int i = 0; i != 10; ++i) {
Console.WriteLine(r.Next());
}
}
}
I make no claims as to performance, but it's probably not great. If you wanted to get many random values this way, you would probably want a Next(int[] values) overload to get many random values in one call, to reduce the overhead of interop.
I have a set of C functions that I need to use on an ARM target, in C++ and in C#. I can successfully wrap up the C into a C++ DLL and then into a C# DLL and use all the C functions I've bound successfully. However, I have a debug function that I want to be able to print to the C# GUI and the delegate it uses is being garbage collected rather than left in place for the duration.
Managed Debugging Assistant 'CallbackOnCollectedDelegate' has detected a
problem in 'C:\utm\pc\utm_win32_app\bin\Debug\utm_win32_app.vshost.exe'.
Additional Information: A callback was made on a garbage collected delegate of
type
'utm_dll_wrapper_cs!MessageCodec.MessageCodec_dll+guiPrintToConsoleCallback::
Invoke'. This may cause application crashes, corruption and data loss. When
passing delegates to unmanaged code, they must be kept alive by the managed
application until it is guaranteed that they will never be called.
Here's the snippet of C code that uses and sets up the callback mp_guiPrintToConsole:
#ifdef WIN32
static void (* mp_guiPrintToConsole) (const char*) = NULL;
void logMsg (const char * pFormat, ...)
{
char buffer[MAX_DEBUG_MESSAGE_LEN];
va_list args;
va_start (args, pFormat);
vsnprintf (buffer, sizeof (buffer), pFormat, args);
va_end (args);
#ifdef WIN32
if (mp_guiPrintToConsole)
{
(*mp_guiPrintToConsole) (buffer);
}
#else
// Must be on ARM
printf (buffer);
#endif
}
void initDll (void (*guiPrintToConsole) (const char *))
{
#ifdef WIN32
mp_guiPrintToConsole = guiPrintToConsole;
// This is the signal to the GUI that we're done with initialisation
logMsg ("ready.\r\n");
#endif
}
Here's the C++ code, built into a DLL along with the C code, that can be called from C# and passes in the function pointer printToConsole:
void msInitDll (void (*printToConsole) (const char *))
{
initDll (printToConsole);
}
Here's the snippet code from the C# DLL that calls msInitDll(), passing in guiPrintToConsole(), and defines the delegate onConsoleTrace, which I guess is the thing that is disappearing:
[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
public delegate void _msInitDll([MarshalAs (UnmanagedType.FunctionPtr)] guiPrintToConsoleCallback callbackPointer);
public _msInitDll msInitDll;
public delegate void ConsoleTrace(string data);
public event ConsoleTrace onConsoleTrace;
public void guiPrintToConsole(StringBuilder data)
{
if (onConsoleTrace != null)
{
onConsoleTrace (data.ToString ());
}
}
public void bindDll(string dllLocation)
{
IntPtr ptrDll = LoadLibrary (dllLocation);
if (ptrDll == IntPtr.Zero) throw new Exception (String.Format ("Cannot find {0}", dllLocation));
//...
// All the other DLL function bindings are here
//...
msInitDll = (_msInitDll)bindItem(ptrDll, "msInitDll", typeof(_msInitDll));
msInitDll(guiPrintToConsole);
}
I've looked at the various answers here and the most promising seemed to be to create a static variable in the C# code:
static GCHandle gch;
...and then use that to reference onConsoleTrace in the C# bindDll() function:
gch = GCHandle.Alloc(onConsoleTrace);
However, that doesn't do me any good. I've tried a few other attempts at declaring things static but nothing seems to get me where I want to be. Can anyone suggest another approach to fixing the problem? I have a bug that I need to fix and the lack of any debug is proving quite annoying.
Rob
The following line uses some syntactic sugar:
msInitDll(guiPrintToConsole);
The full syntax is:
msInitDll(new guiPrintToConsoleCallback(guiPrintToConsole));
Hopefully now you see why the delegate can get garbage-collected.
One simple workaround:
var callback = new guiPrintToConsoleCallback(guiPrintToConsole);
msInitDll(callback);
// ... some other code
GC.KeepAlive(callback);
Now the delegate is guaranteed to be alive up to the GC.KeepAlive call.
But you most probably need the delegate to stay alive for longer. As the error message says, simply keep a reference to it. If you need it for the full C# app lifetime duration, turn the callback local to a static field in your class. Static fields are treated as GC roots as their values are always reachable.
And the answer was, in the C# DLL code, add the static variable:
public static guiPrintToConsoleCallback debugCallback;
...and then, in C# bindDLL(), change:
msInitDll(guiPrintToConsole);
...to
debugCallback = new guiPrintToConsoleCallback(guiPrintToConsole);
msInitDll(debugCallback);
Simple when you know how.
I've been working on DLL which uses OpenCV to do some tracking. Got everything working on a C console project using VS 2008 (for testing purposes). Then I made a new DLL project and got it compiled. I setted up everything so I just had put a single function on a Thread Class to call a single function.
Then I created a C# project for GUI and other stuff. The DLL loads fine (using System.Runtime.InteropServices, the method starts (I can see the capture window created by OpenCV) but the tracking is not being done. To verify the DLL was actually working, I loaded on Python and called the method and it ran fine (tracking was being made).
I'm new to working with unmanaged DLL's on managed code. Any ideas on what am I doing wrong or how can I debug this? If you need anything else to help me solve this problem I'll provide it.
Thanks in advance.
Edit:
I'm not using a class on the DLL, I'm using a single function, the Thread class comes from the System.Threading on C#
The way I'm using the DLL is like this.
namespace GUI
{
static class NativeTracking
{
[DllImport(#"__Tracking.dll")]
public static extern void _Tracking();
}
}
The I put it on a thread like his
public GUI()
{
InitializeComponent();
_tracking = new Thread(_Tracking);
_tracking.Start();
}
public _Tracking()
{
while(True)
{
NativeTracking.Tracking();
}
}
Edit: Native Coded
Native Code, sorry for the messy format.
Header File
#include <cv.h>
#include <stdio.h>
#include <ctype.h>
#include <windows.h>
#include <highgui.h>
#include "..\original\project\myheader.h"
#include "..\original\project\myheader1.h"
#include "..\original\project\myheader2.h"
#include "..\original\project\myheader3.h"
#include "..\original\project\myheader4.h"
#ifdef __cplusplus
extern "C"{
#endif
_declspec(dllexport) void Tracking();
#ifdef __cplusplus
}
#endif
Implementation
#include "exposed.h"
void Tracking( )
{
int flag = 1, i=0;
iplImgs imgs;
trackingTool tools;
helperTools helperTools;
CvCapture* capture = 0;
capture = cvCaptureFromCAM( 0 );
cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, 320);
cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, 240);
cvSetCaptureProperty(capture, CV_CAP_PROP_FPS, 20.0f);
imgs.image = 0;
cvNamedWindow( "Window", 0 );
initHelperTools(&helperTools);
initTools(&imgs, &tools);
for(;;){
int c;
IplImage* frame = 0;
frame = cvQueryFrame( capture );
if( !frame )
break;
if( !imgs.image ){
/* allocate all the buffers */
prepareImages(&imgs, &tools, frame);
}
cvCopy( frame, imgs.image, 0 );
cvCvtColor( imgs.image, imgs.grey, CV_BGR2GRAY );
if( flag == 1 || conditionB ){
someOperations(&imgs, &tools);
if (conditionC)
flag = 0;
}
else if( conditionD ){
otherOps(&helperTools, imgs.grey);
someTrack(&imgs, &tools, &helperTools, drawPoints);
someCorrections(&tools);
if ( condition ){
if (!wasReset){
wasReset = 0;
continue;
}
if ( validation )
someMoreOperations(&tools, corretions);
}
}
bufferHandlingOps(&imgs);
c = cvWaitKey(10);
if( (char)c == 27 )
break;
switch ((char)c){
case 'p':
drawPoints ^= 1;
break;
default:
;
}
}
cvReleaseCapture( &capture );
cvDestroyWindow("Window");
}
One problem I see is that you're using the default C calling convention in your native code, which is "__cdecl", and in your P/Invoke "DLLImport" attribute you're letting it use the default calling for C# which is "__stdcall". This is probably throwing a run time exception whenever you invoke your native method.
Also, I realize that you're not passing any arguments or expecting any results, but the calling convention does denote the way the function is decorated, which can lead to name mangling problems.
You can:
Change your native export to "__declspec(dllexport) void __stdcall Tracking();"
Or change the "CallingConvention" property on your DLLImport attribute: [DllImport(#"__Tracking.dll") CallingConvention = CallingConvention=CallingConvention.Cdecl]
One of the problem of DllImport (P/Invoke) is that is not type safe. You knows it's working only runtime because you don't have a way to check it at compile time if:
the signature of the method is the right one;
the mangling of the method is correct.
It's very popular for WIN32 methods because you'll find plenty of examples that work, but for the lib you are using, it's a matter of trying and trying until it works or you find the specific problem that's blocking you (remember to verify the dll is in the same directory, or in system directory, of the executable when it runs).
My advice is to create a proper wrapper in C++/CLI. It doesn't have the problems above and it's esier to debug in case of problems. Here is a more complex example I wrote in the case of a callback in C to expose in C#.