hi! I'm trying to get the following C++ class (contained in a DLL):

__declspec(dllexport) class ThreadManager {
	public:
__declspec(dllexport) static UINT32 setThreadPriority(UINT32 tPriority);

__declspec(dllexport) static UINT32 setPriorityClass(UINT32 pClass);

__declspec(dllexport) static UINT32 setProcessorMask(UINT32 pAffinity);

__declspec(dllexport) static UINT32 changeMATLABSeed(UINT32 inputSeed);

					  static BOOL setHandles(HANDLE* thread, HANDLE* process);

__declspec(dllexport) static BOOL testFun(UINT32 testInt);

					  static UINT initializeProgram(ostream* consoleStream);
};

into C#... in which I have the following class:

class ThreadManagerWrapper
    {
        [DllImport("MATLABCPPThreadLibrary.dll", CharSet = CharSet.Auto)]
        public static extern UInt32 setThreadPriority(UInt32 tPriority);

        [DllImport("MATLABCPPThreadLibrary.dll", CharSet = CharSet.Auto)]
        public static extern UInt32 setPriorityClass(UInt32 pClass);

        [DllImport("MATLABCPPThreadLibrary.dll", CharSet = CharSet.Auto)]
        public static extern UInt32 setProcessorMask(UInt32 pAffinity);

        [DllImport("MATLABCPPThreadLibrary.dll", CharSet = CharSet.Auto)]
        public static extern UInt32 changeMATLABSeed(UInt32 inputSeed);

        [DllImport("MATLABCPPThreadLibrary.dll", CharSet = CharSet.Auto)]
        public static extern Boolean testFun(UInt32 testInt);

    };

everything compiles fine... but when I call ThreadManagerWrapper.testFun(1); everything crashes, and VS 2005 informs me:
Unable to load DLL 'MATLABCPPThreadLibrary.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)

I've been reading a selection of tutorials on creating wrapper classes or doing p/invokes... and I think I've created a monster...

anyone out there familiar with C++ -> C# interop wanna give me a step-by-step on how to wrap my C++ code? or perhaps want to give me a link to a /good/ tutorial? (seriously... most of them out there are utter crap ;_; )

Recommended Answers

All 14 Replies

You'd be better off (if possible) creating a mixed managed/native DLL in C++/CLI and adding that DLL as a reference to your C# project.

You'd be better off (if possible) creating a mixed managed/native DLL in C++/CLI and adding that DLL as a reference to your C# project.

okay. Now I've created a CLI/CLR class library... most of my issues before were due to not properly updating my DLL reference in C# *D'OH!*

Now I've just got one little problem, the following is an unmanaged method in a managed class (is that allowed?)

UINT MATLABNamespace::MATLABFunctionThreader::createMATLABThread(LPVOID printString)
{  
  mwArray mxInversions, mxSample, mxTime;

  inverterFunction(3, mxInversions, mxSample, mxTime);

 return 0;
}

Basically I want to be able to send out mxInversions mxSample and mxTime to my C# GUI... but I'm having trouble doing it, it seems like every time I try and pass something out through printString I get in trouble with the managed code compiler -- it tells me stuff like "you can't use unmanaged code inside managed code"

Am I going to have to wrap my unmanaged functions in order to call them? I thought CLR solved this nonsense and allowed me to have unmanaged code?

I don't want to export the unmanaged code to C#, I just want my managed DLL to do some unmanaged work behind the scenes before passing out a managed output string...

thanks for your help thus far.

Look at these two pieces of code, they mix managed and unmanaged just fine. More than likely you've not included a header file that you need in order to compile with managed code. make sure that you have

#include "stdafx.h"

in your .cpp file and

#include <vcclr.h>
using namespace System;

in your .h file.

String ^ Namespace::NativeToString( const char * charArray ) {
    String ^ returnValue = String::Empty;

    while ( *charArray != '\0' ) {
        returnValue += (char)*charArray;
        charArray++;
    }

    return returnValue;
}

const char * Namespace::StringToNative( String ^ str ) {
    array<Char, 1> ^ charArray = str->ToCharArray();
    size_t size = charArray->Length + 1;
    char * returnValue = (char *)malloc(size * sizeof(char));

    for ( int i = 0 ; i < charArray->Length ; i++ ) {
        returnValue[i] = (char)charArray[i];
    }
    // allocated an extra char for null
    returnValue[charArray->Length] = '\0';

    return returnValue;
}

Look at these two pieces of code, they mix managed and unmanaged just fine. More than likely you've not included a header file that you need in order to compile with managed code. make sure that you have

#include "stdafx.h"

in your .cpp file and

#include <vcclr.h>
using namespace System;

in your .h file.

I didn't have my MATLABlib.lib included... I can use the managed code now.


Do you have a reference to any tutorials on these weirdo managed pointer things?

What I'm really trying to do is use MFC's AfxBeginThread(functionHandle, LPVOID, threadPriority) .

I am allowed to pass in a LPVIOD for my function parameters, and I'd like that to be a pointer to a data-containing struct, then within the function referred to by functionHandle I want to populate that struct, and make the pointer point to it...

Does that make sense?

Good catch on the .lib file, after using C# for a while you tend to forget about stuff like that.

I don't have any where to point you for C++/CLI information other than MSDN. I've learned mostly through trial and error, and various books I've picked up. For the most part managed pointers behave just like native pointers, but they point to a different heap, and you don't need to call delete on them.

As for AfxBeginThread, what I think you're looking for is this:
The managed heap and native heaps are kept seperate, therefore you cannot get a void * to point into the managed heap. What you need to do is copy the struct from the managed to the native heap, then cast your void * and call AfxBeginThread with the pointer to the native heap.

I know it's annoying, but just think about how the memory is physically structured and you'll quickly understand why you need to do this. The overhead should be minimal. G'luck!

Good catch on the .lib file, after using C# for a while you tend to forget about stuff like that.

I don't have any where to point you for C++/CLI information other than MSDN. I've learned mostly through trial and error, and various books I've picked up. For the most part managed pointers behave just like native pointers, but they point to a different heap, and you don't need to call delete on them.

As for AfxBeginThread, what I think you're looking for is this:
The managed heap and native heaps are kept seperate, therefore you cannot get a void * to point into the managed heap. What you need to do is copy the struct from the managed to the native heap, then cast your void * and call AfxBeginThread with the pointer to the native heap.

I know it's annoying, but just think about how the memory is physically structured and you'll quickly understand why you need to do this. The overhead should be minimal. G'luck!

woah hey! That makes sense... I'll try it and close the thread if it works. :)

Good catch on the .lib file, after using C# for a while you tend to forget about stuff like that.

I don't have any where to point you for C++/CLI information other than MSDN. I've learned mostly through trial and error, and various books I've picked up. For the most part managed pointers behave just like native pointers, but they point to a different heap, and you don't need to call delete on them.

As for AfxBeginThread, what I think you're looking for is this:
The managed heap and native heaps are kept seperate, therefore you cannot get a void * to point into the managed heap. What you need to do is copy the struct from the managed to the native heap, then cast your void * and call AfxBeginThread with the pointer to the native heap.

I know it's annoying, but just think about how the memory is physically structured and you'll quickly understand why you need to do this. The overhead should be minimal. G'luck!

Okay... I think I've almost got it... I just need to figure out what delegates are...

Here's what I've done so far:

MATLABDataWrapper is an unmanaged class who holds information I get from calling a MATLAB function

class MATLABDataWrapper
{
public:

//data members... I know these guys should be private, but nobody'll really access em'
 mwArray mxInversions; 
 mwArray mxSample;
 mwArray mxTime;

 String^ MATLABNamespace::MATLABDataWrapper::toString(void)
 {
 char buffer[100]; //my data out should never be larger than 100 spaces... I /think/

 //annoying hoop to jump through to turn my mwStrings into strings.
  sprintf_s ( buffer, 100, "%s\t%s\t%s", 
          (const char*)this->mxInversions.ToString(),
          (const char*)this->mxSample.ToString(),
          (const char*)this->mxTime.ToString());

        //copy the buffer to a new string?  gcnew creates a garbage collected string...
	 String ^outputStr = gcnew String(buffer,0,100);

        /return the garbage collected string, but trim the blanks off the end.
	 return outputStr->Trim();
 }

};

the following is the function that will be used within AfxBeginThread

//must return UINT and can only have one param of type LPVOID
UINT MATLABNamespace::MATLABFunctionThreader::createMATLABThread(LPVOID pointerToDataWrapper)
{

  //create a new pointer on the inside the function 
 //and point it to the data location 'outside'
  MATLABDataWrapper *pointer = (MATLABDataWrapper *)pointerToDataWrapper;

//create data arrays
  mwArray mxInversions, mxSample, mxTime;
  
//call MATLAB function
  inverterFunction(3, mxInversions, mxSample, mxTime);

//access the memory outside the function, 
//and store the data values in appropriate slots.
  pointer->mxInversions = mxInversions;
  pointer->mxSample = mxSample;
  pointer->mxTime = mxTime;

//exit with 'success'
 return 0;
}

And here's where I call AfxBeginThread to create my worker thread:

//create a data struct to store data in...
MATLABNamespace::MATLABDataWrapper dataContainer;

//start a worker thread
AfxBeginThread(MATLABNamespace::MATLABFunctionThreader::createMATLABThread,
               &dataContainer,//address of data container is passed in as LPVOID
               THREAD_PRIORITY_NORMAL); //assume normal thread priority...

Currently my compiler is complaining:

error C3374: can't take address of 'MATLABNamespace::MATLABFunctionThreader::createMATLABThread'
unless creating delegate instance

Aside from not making a delegate, is there anything glaringly wrong with my approach thus far? I think I've done everything right (like creating a pointer on the inside for the separate heap?) But I'm new to this managed code world...

I'll check out the delegate stuff myself, but any tips you wanna send my way would be appreciated as well. :)

Is createMATLABThread managed or native? If it's managed, you need to create a delegate to pass.

Delegates are manage codes version of function pointers. So, I don't think you're getting from using one.

Otherwise I think it looks OK. Not sure about casting a System::String as a (const char *) like that, especially since managed strings use utf-16 and not 8bit chars. The compiler may be smart enough to handle this correctly for you however. I've always used the methods I posted above to do string conversion, mostly because I hate strings. :-P

I have this page bookmarked from a long time ago, I found it fairly useful then; and you might now.

http://msdn.microsoft.com/en-us/magazine/cc301810.aspx

commented: Extremely helpful guy so far. Has been helping me with managed/unmanaged code. +1

Is createMATLABThread managed or native? If it's managed, you need to create a delegate to pass.

Delegates are manage codes version of function pointers. So, I don't think you're getting from using one.

Otherwise I think it looks OK. Not sure about casting a System::String as a (const char *) like that, especially since managed strings use utf-16 and not 8bit chars. The compiler may be smart enough to handle this correctly for you however. I've always used the methods I posted above to do string conversion, mostly because I hate strings. :-P

I have this page bookmarked from a long time ago, I found it fairly useful then; and you might now.

http://msdn.microsoft.com/en-us/magazine/cc301810.aspx

I am not casting System::String as (const char*) that is a special proprietary class called mwString being cast into a char array via the sprintf_s function.

I moved UINT createMATLABThread(LPVOID pointerToDataWrapper); outside of my managed class definition to try and get around the delegate thing... but now it appears now that I am unable to call my MFC DLL in that code bit due to DLL access errors...

I think I'll move it back in and try to use a delegate, and I'll read that link as soon as I get home from work (time to clock out :) )

it may be useful to note that I am relying on another DLL to provide my MATLAB functions... is there any special hoop I've got to jump through to allow my managed class to call a function from an unmanaged DLL? Or can CLR not directly consume unmanaged functions from a DLL?

CLR Managed code can call methods in another DLL via P/Invoke, but I recommend not doing that and instead having the native portion of you code handle dealing with other unmanaged code blocks.

C++/CLI is primarily designed to be a bridge language between managed and unmanaged code, so using it accordingly will save you a lot of time.

CLR Managed code can call methods in another DLL via P/Invoke, but I recommend not doing that and instead having the native portion of you code handle dealing with other unmanaged code blocks.

C++/CLI is primarily designed to be a bridge language between managed and unmanaged code, so using it accordingly will save you a lot of time.

And to access a regular ol' unmanaged DLL with unmanaged code? Any special tricks like P/Invoke? Or can I just call it like normal?

You still need to set up your methods accordingly, but I find using C/C++ LoadLibrary and GetProcAddress to get the other native code and then calling the calling code with C++/CLI is much easier way to wrap things.

i also have the same problem , as i have DLL in C++ and i wanna create C# wrapper for this dll to use it in C# project. so if some one can help me with better details for how exactly i can do this, i would greatly appreciate.

Please open a new thread about it.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.