C++/CLI code porting problem

I'm trying to port a C++.NET (managed extensions) application to C++/CLI. However I'm not very strong with the syntax yet.

What I'm trying to do is to create a wrapper for a C Dll file.

To do this I'm using DllImport but I failed to found documentation on it's use. There are some problems due to changes of the syntax but I couldn't find out why yet.

The C++.NET line looks like this:

  [DllImport("my.dll", CharSet = Ansi,  CallingConvention = Cdecl, EntryPoint = "#10")]
 static MY_STATUS CPPInit(MY_HANDLE *pLmxHandle);

The idea is to pass a reference of MY_HANDLE to the function which initializes it. One problem is that the keywords Ansi and Cdecl are unknown. I expect I need to put some class infront of them but it's a little hard without docs or samples.

The other thing I have is a function which returns a static string:

char *MyFunc();

Can I assume that it can be mapped to String^

Thanks in advance.


Answer this question

C++/CLI code porting problem

  • Lloyd Rice

    To declare the function, you were looking for the C++ equivalent of C#'s ref keyword. As you know, you cannot use native pointers on managed objects. This is because the garbage collector keeps shuffling the data types across the heap. To get a pointer to a managed object, you either need to lock the object in position (with a pin_ptr), or use a smart pointer.

    C++/CLI offers two ways of declaring Pass-by-ref types. The first way you have already discovered, is to make use of interior_ptr<>. The interior_ptr (tracking pointer) is a kind of smart pointer that can traverse through the GC heap, allowing you to perform pointer arithmetic on the GC heap (normal pointer arithmetic doesn't work with GC types). interior_ptr is the more advanced version of the reference type.

    The other way to have a pass-by-ref is a tracking reference. You denote this via the % modifier. In other words, using this code:

    [DllImport(...)]
    CPPInit(MY_HANDLE %pMyHandle);

    In your case, this line of code and the interior_ptr version are equivalent. The main difference is that for the interior_ptr version, you must explicitly dereference the tracking pointer (it acts more like the * pointer of normal C++), whereas with the % operator, you don't need to dereference the handle (% acts more like the & reference of normal C++).

    However, the % reference cannot do pointer arithmetic, unlike interior_ptr. In your case you didn't need to perform pointer arithmetic, so that's why both lines of code were equivalent.

    An overview of interior_ptrs in C++/CLI (by Nish)
    http://www.codeproject.com/managedcpp/interiorpointers.asp

    Note that because you are using a pointer directly in .NET, the DllImport will produce unsafe code. And that pretty much nullifies the only reason to use C++ PInvoke: verifiable code. If you want verifiable code, you'll need to use my IntPtr construct.

    The easiest way to port your class to .NET is to write it in unmanaged C++, copy-paste the relevant bits of code into your class library, then compile as .NET.

    As for the 32/64 bit issues, you can perform a stopgap solution by removing the "Detect 64-bit portability issues" (under Project Properties -> C/C++ -> General), that is unless you plan to go to 64-bit, in which case, don't do that!



  • marcelv

    Thanks for the explanation. Lot of it was quite helpful.

    The class library will have to wait some time. If theres atleast 6 months before .NET goes into real-world action theres no hurry for me.

    It would be interesting though if you could force x64 to load .NET binaries as 32 bit instead of 64 bit (atleast while I'm in test phase of my program).

    The specific problem I tried to describe I didn't solve yet though.
    Your answers were not really helpful but I think it's because of lack of good explanation from my side. So let me try again:

    I have a dll file written in C++ (unmanaged) exporting ordinals.

    I map these ordinals into functions through DllImport:

    [DllImport("my.dll", CharSet = CharSet::Ansi, CallingConvention = CallingConvention::Cdecl, EntryPoint = "#10")]

    static MY_STATUS CPPInit(MY_HANDLE *pMyHandle);

    [DllImport("my.dll", CharSet = CharSet::Ansi, CallingConvention = CallingConvention::Cdecl, EntryPoint = "#4")]

    static void CPPFree(MY_HANDLE MyHandle);

    MY_HANDLE is:

    typedef void * MY_HANDLE;

    So the init functions expects a pointer to a pointer or the address of the pointer. The pointer will the contain allocated memory which is setup from within the dll.

    All this code is inside a class which I expose to the .NET user. So the user of my library won't use the handle directly. Only indirectly through the class.

    When I initialize the library I run:

    CPPInit(&m_MyHandle);

    However C++/CLI doesn't like that construction and spits out:

    cannot convert parameter 1 from 'cli::interior_ptr<Type>' to 'MY_
    HANDLE *'

    It seems it doesn't like the reference parameter. This used to work fine in managed C++.NET though.

    Can someone maybe explain me why it can't accept a pointer to a void pointer into that function Not even typecasting to (MY_HANDLE *) works. In C++.NET I used __gc keyword in the prototype of CPPInit though.

    Regards,
    Henrik Goldman
    X-Formation http://www.x-formation.com


  • Tom Harwood

    Thank you for your new explanation. This one was much more to the point Smile

    I understand what you're saying now and I already said to myself that I need to build a mixedmode library, just like you mentioned to avoid p/invoke. This just takes some time though.

    Actually I solved the compile error in another way. Although I havn't tested it yet because I'm facing some 32/64 bit issues which I can't solve because of other bugs in Whidbey beta2.

    My solution was to write the protype in the following way:

    interior_ptr<MY_HANDLE> pMyHandle;

    From what I understood it should give the function a reference (hence an address) to the dll function. Once I get to try it out I will see if my idea works. Otherwise I will go for yours:

    [Out] IntPtr p_MyHandle

    Anyway I think the problem is solved because one of those should work.

    Can anyone comment on the interior_ptr<T> though

    -- Henrik Goldman
    X-Formation (http://www.x-formation.com)


  • stratos13

    Thanks for the explanation. It helped med getting forward.

    One problem with my code is that I need to build the same code for both C#, VB.NET, managed C++ (eventually it will disapear) and C++/CLI and possibly other .NET languages in future. The biggest obstacle is that I created my unmanaged code as a dll and then need to create wrappers to interface it.
    Maybe someone got an article on this issue on how to do something more efficient

    Also I need that the code works on both Win32 and Win64 (AMD64) in future. This is also a problem right now since I am not sure if I can force my x64 machine to run as 32bit to load my current 32 bit dll.

    Personally I'd like to create a managed library so I get rid of the having to edit my wrappers for each language. But still I would need 2 librarys for 32 bit and 64 bit right

    Still if I write a complete wrapper in C++/CLI and make it into a library I probably cannot assume that users can use it with their existing .NET 1.1 compiler. Since .NET 2.0 probably takes atleast 1 more year before it goes into the open for real, it's a bit long time to wait.

    So far I got another problem with C++/CLI.

    I declared my class as:

    ref class myclass
    {
    public:
    ... functions

    private:

    private:
     MY_HANDLE m_Handle;
    };

    MY_HANDLE is actually just a typedef'ed "void *".

    In the member function:
    MY_STATUS Init()
      {
        return CPPInit(&m_Handle);
      }

    I get the following error:

    cannot convert parameter 1 from 'cli::interior_ptr<Type>' to 'MY_HANDLE *'

    Since the class is a reference type and I want it to be a CLR type the member variable handle becomes a managed type too. However can I declare it as a unmanaged type

  • Poofius

    Thanks once again.

    I think your last answers pretty much sums up the doubts I've had on this issue.

    -- Henrik
    X-Formation
    (http://www.x-formation.com)

  • Neo2000

    > One problem with my code is that I need to build the same code for both C#,
    > VB.NET, managed C++ (eventually it will disapear) and C++/CLI and possibly
    > other .NET languages in future.


    That's a reasonable use of C++/CLI. I too primarily use C++/CLI to create class wrapper libraries for use with other languages (I didn't bother learning managed C++, because I got into .NET the week MSDN told me it was going to be obsoleted). In this case, you will find that using mixed C++/CLI code and IJW will be faster and more type-safe than using P/Invoke.

    > The biggest obstacle is that I created my unmanaged code as a dll and then
    > need to create wrappers to interface it. Maybe someone got an article on this
    > issue on how to do something more efficient

    IMO, the best source of information you are going to get on C++/CLI are Nishant S's articles at Codeproject. You can find them in the C++/CLI section in
    http://www.codeproject.com/managedcpp/ .

    > Also I need that the code works on both Win32 and Win64 (AMD64) in future.
    > This is also a problem right now since I am not sure if I can force my x64
    > machine to run as 32bit to load my current 32 bit dll. Personally I'd like to create
    > a managed library so I get rid of the having to edit my wrappers for each
    > language. But still I would need 2 librarys for 32 bit and 64 bit right

    From the very little I have read on .NET for 64-bit (mostly from
    http://msdn.microsoft.com/netframework/programming/64bit/default.aspx), since your .NET assemblies are written in MSIL, the 64-bit JIT compiler will automatically compile that code into 64-bit code with no recompile needed. Only one version of your DLL is needed, and it will turn into 32-bit code for .NET32, and 64-bit code for .NET64. As long as you compile verifiable .NET code (which C#/VB does by default, but not C++), you'll be safe.

    Your primary obstacle to 64-bit portability is your unmanaged DLL. That WILL need a 64-bit version to work, since you cannot load 32-bit DLLs into a 64-bit process (and P/Invoke won't help either, because it relies on LoadLibrary). That will essentially make your C++ wrapper useless as well.

    > Still if I write a complete wrapper in C++/CLI and make it into a library I probably
    > cannot assume that users can use it with their existing .NET 1.1
    > compiler. Since .NET 2.0 probably takes atleast 1 more year before it goes into
    > the open for real, it's a bit long time to wait.

    This is true, C++/CLI code requires .NET 2.0. You may be able to expose a COM interface from your class library for VS.NET developers, but you still need the 2.0 runtime on the end user's machine, so it looks like a 6-12 month wait.

    On your final problem, it looks like you are facing one of three problems.

    You seem to have transitioned to IJW and C++ interop. In C++ interop, there's no need to redeclare your MY_HANDLE type as a managed type. Just use the normal definition and let your C++/CLI class library handle all the gory details of that MY_HANDLE.

    If you do need the managed version of MY_HANDLE (say the C#/VB clients are going to create it), you can either make a wrapper for it that derives from one of the Microsoft.Win32.SafeHandles classes, or you can declare your handle as an IntPtr:

    IntPtr m_handle;
    ...
    void *nakedHandle = m_handle.ToPointer();
    /* Do stuff with nakedHandle. */
    ...

    If you need to convert a managed "array" to an unmanaged pointer, you can do so with the help of a pinning pointer (like what Nish said in his Codeproject articles).

    /* pretend your void* is a char* */
    pin_ptr<char> pinner = m_Handle;
    char *yourHandle = pinner;

    personally I'd make your class library encapsulate the management of MY_HANDLE. Do the C#/VB clients really need to know what a MY_HANDLE is


  • jethro 1955

     OShah wrote:

    To get a pointer to a managed object, you either need to lock the object in position (with a pin_ptr), or use a smart pointer.



    I started to try your suggestion and import my code directly into .NET without using PInvoke.

    So I went back to my original problem of how to pass arguments to a C-function with the following prototype:

    MY_STATUS MyInit(MYHANDLE *pHandle);

    Trying pin_ptr I got the code to compile with the following piece of code:

    MY_STATUS Init()
    {
    pin_ptr<MYHANDLE> pMyHandle = &m_Handle;
    return MyInit(pMyHandle);
    }

    This code compiles fine ... but the question is if it's safe. This is just a trick to be able to do the good old reference of a variable (func(&var)). Unfortunatly I cannot test it yet due to the other 32/64 bit problems I have in my code.

     OShah wrote:

    Note that because you are using a pointer directly in .NET, the DllImport will produce unsafe code. And that pretty much nullifies the only reason to use C++ PInvoke: verifiable code. If you want verifiable code, you'll need to use my IntPtr construct.


    What does it mean by unsafe or verifiable code What difference does it really make
    I am using a handle .NET. Yes, correct this handle is a pointer but my destructor of this class will call my free function. This would be no different then calling malloc and free from inside .NET.

     OShah wrote:

    The easiest way to port your class to .NET is to write it in unmanaged C++, copy-paste the relevant bits of code into your class library, then compile as .NET.


    I am not hoping you're saying I should rewrite all my code to make it possible This is just impossible to do. I have this C library which is a lib file and it has a C interface exported so the best I can do is use that and expose the .NET wrapper to the users. Instead of having access to the handle from .NET code each object created will have it's own handle.

    Or did you mean that I should use "class" instead of "ref class" This will probably create problems when trying to use gcnew, no

     OShah wrote:

    As for the 32/64 bit issues, you can perform a stopgap solution by removing the "Detect 64-bit portability issues" (under Project Properties -> C/C++ -> General), that is unless you plan to go to 64-bit, in which case, don't do that!


    My code is already 64 bit compatible. It already compiles with the AMD64 cross compiler. I am not sure why you say don't go for 64 bit

    -- Henrik


  • S. Craig

    I'm very sorry my post wasn't more helpful. When I was figuring out an answer to your problem, I manufactured a similar problem to yours. The problem I made up was: Port the fopen/fread/fclose functions to .NET. I assumed your problem was identical to this. This was the reason for the confusion.

    The primary problem you are facing is that .NET PInvoke doesn't do pointers. If you try to pass native pointers to a DllImport function, the compiler will complain vociferously about it. In Managed C++, when you declared your function as above, you were actually passing a GC handle to your function, not a pointer (in managed C++, it was harder couldn't tell the difference between a GC handle and a native pointer). In C++/CLI, the difference is now made clear. Now, the reason the compiler didn't complain about your mistaken function declaration was due to the second problem with DllImport: DllImport does not perform type-checking.

    Because you can't do pointers in .NET, you need to find something else. .NET developers workaround this by using another type: the int. Since a pointer is just 4 bytes of data, you can represent pointer types as simply an int! When you return, you'll be handling the int as an opaque number you just shuffle between the PInvokes.

    Take fopen() for example: we can't do FILE*s in .NET, so we use this DllImport:

    [DllImport("msvcrt.dll", CharSet=CharSet::Ansi, CallingConvention=CallingConvention::CDecl)]
    static int fopen(System::String ^fileName, System::String ^fileMode);
    /* redefine FILE* to int */

    Or for your function:

    [DllImport("my.dll", CharSet=CharSet::Ansi, CallingConvention=CallingConvention::CDecl, EntryPoint="#10")]
    static MY_STATUS CPPInit(int p_MyHandle);

    To test for NULL pointers, simply do if(p_MyHandle == 0)! Now you will find out your function works. And for my fopen port, we can now do: fopen("filename.txt", "r");. With DllImport, you're going to do much of this. Get used to it.

    DllImport does not check the types to see if they're declared correctly. My CPPInit declaration, although it compiles, it suffers from a flaw. When we return from CPPInit, you'll find that p_MyHandle wasn't changed! This is because the declaration is wrong! A better declaration would be:

    [DllImport("my.dll", CharSet=CharSet::Ansi, CallingConvention=CallingConvention::CDecl, EntryPoint="#10")]
    static MY_STATUS CPPInit([Out] int p_MyHandle);
    /* Marshal it as an out parameter */

    Actually, this still won't work (in Win64). You already know that a pointer is only 4 bytes in 32-bit Windows. In 64-bit Windows, pointers are 8 bytes in length. You'll need the IntPtr type to properly represent the data type. The equivalent type is IntPtr. So where I've written int, replace it with IntPtr.

    This is the journey you need to take to PInvoke functions in .NET. The moral  Don't bother with DllImport if you can help it. Instead of DllImporting fopen, simply include its header file, and use that:

    /* This is both .NET code AND native code. */
    #include <cstdio>
    /* No DllImport for us */
    FILE *fptr = fopen("filename.txt", "r");

    Your code is exactly like the fopen scenario. Why bother DllImporting fopen when you already have it in cstdio Similarly, Why bother DllImporting CPPInit when you already have it in "MyDLLHeader.h".



  • BillForShort

    If you haven't already done so, make sure the following is present:

    using namespace System::Runtime::InteropServices;

    It probably isn't necessary, but you may want to declare managed versions of MY_STATUS and MY_HANDLE (this is required if you are making a class library for external clients). Try using this:

    [DllImport("my.dll", CharSet=CharSet::Ansi, CallingConvention=CallingConvention::CDecl, EntryPoint="#10"]
    static MY_STATUS CPPInit(MY_HANDLE *pLmxHandle);

    Question 2:
    You should be okay if you just swap char* for a string. That is, unless you are required to free it afterward (or delete it, or LocalFree it, or ...).
    In this case, you may need to use an IntPtr/SafeHandle, convert that into a System::String (with Marshal::PtrToStringAnsi), and free the pointer. There may be something else, but I'm not sure.


    Unless you are compiling with pure/verifiable managed code, then you may want to consider ditching PInvoke (DllImport) and use C++ interop instead (just call it directly).

    void SomeFunction(String ^dummyString)
    {
      ...
      MY_HANDLE LmxHandle = CreateMy_Handle();
      MY_STATUS nStatus = CPPInit(&LmxHandle);
      /* Or however you did this in native C++ */
      ...
    }


    http://msdn2.microsoft.com/library/zbz07712(en-us,vs.80).aspx
    (especially good information available under "Pinvoke in C++").

  • Justin Shen

    Again I missed the reply. Maybe I should sign up for Microsoft Alerts to get quicker notification.


     Henrik Goldman wrote:


    MY_STATUS Init()
    {
    pin_ptr<MYHANDLE> pMyHandle = &m_Handle;
    return MyInit(pMyHandle);
    }

    This code compiles fine ... but the question is if it's safe.



    The pin_ptr method is fine (I use the pin_ptr quite often whenever I need to expose a native structure to the managed world). pin_ptr essentially locks the GC object in position, so that a GC cycle won't move the block of memory, and invalidate any native pointers. With a pin_ptr, you can safely obtain a native pointer to the block of memory, and manipulate it with native functions.

    There could be a performance loss with using pinning pointers. Since they hinder the GC cycle, it means the GC cannot do its job properly and defragment your heap. You could be left with a fragmented heap. However, since you're only using a pin_ptr for a one time initialisation, and the pin_ptr is shortlived, I doubt you would see any performance loss due to pin_ptr.

    Nishant S again:
    http://www.codeproject.com/managedcpp/pinnedpointers.asp

     Henrik Goldman wrote:

    What does it mean by unsafe or verifiable code What difference does it really make


    Unsafe code means the .NET framework will not be able to verify the datatypes passed into your code for safety, and that boxing/unboxing of types becomes impossible. This leads to Code Access Security Issues, and the possible requirement that your code may need to run with FullTrust (or UnmanagedCodeSecurity at least).

    If you create verifiable code, the .NET framework will automatically ensure that what's passed to your function is a valid type. you can impose stricter security on your assemblies (maybe even to the point where it runs under the restricted zone). It may also be required for SQL server programs, or ASP.NET on the internet.

    By default, C# and VB produce verifiable code. If C#/VB are to interface with unsafe code (that is, code marked unsafe in MSIL), the client must change the project settings so that the /unsafe is added to the compiler.

    Personally, I don't produce safe code using C++/CLI (if I wanted to do that, I would have programmed in C#). I only use C++/CLI to produce wrapper classes that can expose native functions to the managed world. That is also why I don't use P/Invoke.

     Henrik Goldman wrote:

    Or did you mean that I should use "class" instead of "ref class" This will probably create problems when trying to use gcnew, no


    Step back. Pretend there is no such thing as .NET. Pretend that PInvoke, managed C++, C++/CLI does not exist..and you can only use ordinary C. In fact, pretend this is Linux. How would you interface to your library then


     Henrik Goldman wrote:

    My code is already 64 bit compatible. It already compiles with the AMD64 cross compiler. I am not sure why you say don't go for 64 bit


    This is a case of a misunderstood subject-verb agreement. When I said "Don't do that!", I meant: "Don't remove the /Wp64 compiler switch". Don't turn off the 64-bit port warnings.


  • C++/CLI code porting problem