none
Concerns while writing C++/CLI warpper for C# Managed library RRS feed

  • Question

  • I need to call managed C# library functions from unmanaged C++ code. I chose C++/CLI simple wrapper above C# library for interoperability to work. I have some questions related to that:

    1. What I understand from reading numerous sites that in C++/CLI - a function would entirely be compiled to either managed or unmanaged code. Also, there is a performance penalty whenever we cross the managed/unmanaged boundary. I have the requirement to copy a tree (it represents a folder hierarchy) data structure from C# managed to C++ unmanaged. Everywhere it is said that while working in mixed mode - we must try to be chunky, rather than chatty. So, I want to cross the managed/unmanaged boundary only once to transfer the data. One way of doing this is to serialize the tree in string/buffer and parse that string/buffer in C++ unmanaged to get the tree back - but it seems overkill to me. Since I already have the objects in C# managed memory I am looking for better way of copying that tree to unmanaged C++ directly. Another way I was thinking is to write a function in C++/CLI in which I copy the entire tree into C++ counterpart structure - but for this I will need to call 'new' to allocate memory from unmanaged heap. So my questions are


    a> If a function is entirely compiled into managed code - and if inside the function we allocate memory from unmanaged heap by calling new - would it result in crossing the boundary from managed to unmanaged each time we call new?


    b> If a function is totally managed and we access unmanaged memory from inside the function - would it result in crossing the managed/unmanaged boundary


    c> vice-a-versa if a function is totally unmanaged and we access managed memory from inside the function - would it result in crossing the unmanaged/managed boundary


    d> If I allocate memory in C++/CLI by calling new in a function (whether in managed or unmanaged compiled), can I deallocate that memory in unmanaged C++ code?

    2. If I use CRT in C++/CLI, do I need to take special care of anything - like explicit initialization of CRT? If yes, how do I do it? If I don't use CRT in C++/CLI dll, then do I need to take care of anything special? In many websites I read things like application hanging in many scenarios in mixed mode apps.

     

    Thursday, May 19, 2011 9:07 AM

Answers

  • Hi, which of the three DLL/EXE can you change? I suppose that you cannot change the native EXE but can change both DLLs or atleast the wrapper.

    It has been a while when I was using CLI C++, now I have mastered all the features of C# including unsafe/fixed and realized that I no longer need CLI C++, that I can do all the things in C# - my examples are in C#.

    A1+2: Well, as I understand it, the GC requieres specific func/stack signature to be able to change pointers as needed (GC works this way). Managed code is just-in-time-compiled to native code, thus there is no problem having "unmanaged" in managed, but you cannot have managed in unmanaged (no signature).

    A3+4: I see you need C++ to compile unmanaged/native functions to be called from native EXE.

     

    1a: As I already mentioned, unmanaged memory allocation is naturally slow, it needs to walk throuh heap-list of free blocks and find the smallest that is large anough to satisfy your request. I would not be afraid of P/Invoke added overhead, it should be nothing copared to LocalAlloc complexity, but I am not an expert in that. (Managed allocation in contrast is very simple - it reserves top block, O(1) operation.)

    1b: The function itself does not need to cross managed/unmanaged as it is managed and can acces unmanaged memory. The problem would rise when calling the function from native code - but that is not about memory, that is about calling convention. Here I am not expert to tell you exactly how much it costs to call managed from unmanaged and vice versa.

    1c: I thing that your function would simply not compile. I remember using calling conventions, not pragmas... compiler should report error on this (C++ code)
    void __stdcall native(Object^ it) {}
    ..but this cannot be called from native:
    void __clrcall managed(Object^ it) {}

    I have found this article http://support.microsoft.com/kb/828736/en-us
    you may find more info in it. I remember that it was not possible to call managed from native - I wanted to call managed event OnNewSms from native code - it was not possible, I had to implement some IPC - native blocking message queue, native thread pushed messages, managed thread was sleeping until some message was put there. You'll probably need something similar, e.g. MessageQueue (Window Handle - SendMessage etc.).

    Remember, that managed pointers need managed func/stack signature/description for the GC. Theoretically, there could be some special C++ class like managed_ptr<T> that GC could recognise, but I am afraid that there is no such thing - thus you cannot pass managed pointers to native.

    ...but I can be wrong, that is really specific, I am not such expert.

     

    1d: I suppose it is safe - but I would not rely on it, I would use LocalAlloc to be absolutelly sure of it.
    I know that native C++ is using two heaps - one for "big" blocks and one for "small" blocks - that is optimalisation.
    The "new" keyword should do the same thing in both native C++ and managed/CLI C++ ... but who knows... and who can assure you that they don't change it in the future ... again, I strongly advise to use LocalAlloc on both sides.

    • Marked as answer by Anil Padia Monday, May 30, 2011 11:41 AM
    Tuesday, May 24, 2011 9:12 AM

All replies

  • Hi,

     

    Thank you for your questions, we're doing research on this case, it might take some time before we get back to you.


    Eric Yang [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Monday, May 23, 2011 1:24 AM
  • If you find formatting and data structure into a string and then parsing the string a laborious task, How about using Xml as the mode to communicate your data structures across managed and unmanaged. You could use the standard Xml parsers to read and write. This maynot be as difficult.

    Also what do you mean by allocating onto unmanaged memory inside managed code. Are you doing this using the [unsafe] construct ? Or is it done some other way ?

    Monday, May 23, 2011 6:05 AM
  • I may have some hints for you:
    Learn eabout fixed and unsafe C# contexts + Marshal.AllocHGlobal/FreeHGlobal (or AllocCoTaskMem/FreeCoTaskMem).

    Once I was rewriting my C++ native GSM/SMS driver for C# application use. That is excatly the opposite way, but that gave me some experience.
    You are talking about new, C++ and C# - I got lost in it. C# have new, C++ equivalent is gcnew, C++ new should be equivalent to C# Marshal.AllocHGlobal or Marshal.AllocCoTaskMem. Maybe you can create a wrapper for C# classes that would use unmanaged memory and pass this... but I don't know if you can alter that C# library or not - or class hierarchy could be another problem.

    1a) You are probably talking about managed C++, allright, that should be same as calling Marschal.AllocHGlobal which is implemented using this method:

    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail), DllImport("kernel32.dll", EntryPoint="LocalAlloc")]
    internal static extern IntPtr LocalAlloc_NoSafeHandle(int uFlags, IntPtr sizetdwBytes);
     
    
    

    - well, that is P/Invoke of "LocalAlloc", but I would not be afraid of it, unmanaged memory allocation can by slow itself and the P/Invoke would not make it worse.

     

    1b) No, accessing unmanaged memory in managed context is OK.

    unsafe void foo(byte* ptr) {
     *ptr = 0;
    }
    unsafe void bar(byte[] arr) {
     fixed(byte *ptr = arr) foo(ptr);
    }
    

    - the fixed statement will pin the managed array in memory, that could slow GC a bit but method foo does not add any overhead.

     

    1c) cannot be done, you simply cannot acces managed memory from unmanaged code, impossible (without memory pinning - fixed statement).

    1d) well, you probably can, but I'll rather use same pair of functions. If you really need this, use functions like LocalAlloc (and declare your own P/Invoke for that in managed code - do not rely on current Marschal.AllocHGlobal implementation).

     

    2. I hope that loading .NET DLL is enough.

    Monday, May 23, 2011 11:24 AM
  • Hi All,

     

    Thanks for the replies.

    Sezhiyan Thiagarajan : Is XML not a string by itself? It is actaully Verbose String - serializing the tree into an XML would require more on processing and memory than serializing into some compact buffer.I am looking for high performance solution.

    firda.cz: Thanks for your answers. There is little confusion though.

    Let me make my requirement clear:

    I have three modules (dlls/exes). One is native (unmanaged) C++ application. Another is C++/CLI managed Interop dll, it is a simple wrapper only. The last one is C# managed dll. From the application dll I need to call a method in C# dll. The call is done via C++/CLI dll. The function in C# dll returns a tree data structure which I need to pass back to the application dll. Here emphasize is on the tree data structure - it is a rooted tree and can have many number of nodes (~10000). The tree represents a folder hierarchy and need to get that information back in application dll such that the there is minimal performance penalty.

    Now let me make my assumptions clear:

    1. In C++/CLI dll, each function is either totally compiled in managed code or unmanaged code

    2. A function cannot be mixed mode function - i.e. it can't have some part of it compiled into managed and some part compiled into unmanaged code

    3. We can control (using pragma directives) which function will be compiled into managed and which will be into unmanaged.

    4. As per MSDN, there are special conditions (like writing assembly code in a function) when pragma directives would be ignored by the compiler and function would be either forced to compile into unmanaged or managed. But for this thread, I don't think that we are falling in those conditions.

    Now let me make my questions clear:

    1. a> If a function is entirely compiled into managed code - and if inside the function we allocate memory from unmanaged heap by calling new - would it result in crossing the boundary from managed to unmanaged each time we call new?

     

    So, suppose there is a function in C++/CLI which is compiled into managed code.

    void SomeFunc()

    {

        Tree* tree = new Tree();

    }

    Tree is a struct defined in C++ and its definition is shared between C++/CLI  dll and application dll through a header.

    The question is : how would the above line executed? 'new' would allocate memory from native heap, so will it lead to crossing the managed/unmanaged boundary? As per firda.cz, it will be done through P/Invoke of LocalAlloc. That means, it will cross the boundary and will have associated performance penalty? Am I getting it right?

    1. b> If a function is totally managed and we access unmanaged memory from inside the function - would it result in crossing the managed/unmanaged boundary

    So, suppose there is a function in C++/CLI dll which is a managed function. This function is called from application dll

    void CopyParam(char* text, int len) // char* is passed from application dll

    {

    char x;

       for(int i=0; i<len; i++)

          x = text[i];    // accessing native memory

    }

    Since char* text is passed from application dll, and by writing x = text[i] in a managed function we are accessing that memory, will it result in crossing the managed/unmanaged boundary? Also, does the memory from application dll is passed as is or is it wrapped in some structure before passing to managed world?

    c> vice-a-versa if a function is totally unmanaged and we access managed memory from inside the function - would it result in crossing the unmanaged/managed boundary

    So, suppose there is a function in C++/CLI dll which is a UNMANAGED function

    #pragma unmanaged

    void funcUnmanaged(Node^ node)

    {

      char* name = new char[100]; // call it line 1

      strcpy(name, node.name); // call it line 2

    }

     

    Node is class defined in C# dll. The above function is called from a managed function. Here we are accessing managed memory in an unmanaged function. Will it lead to cross the boundary?

     

    d> If I allocate memory in C++/CLI by calling new in a function (whether in managed or unmanaged compiled), can I deallocate that memory in unmanaged C++ code?

     

    In the example above (1.c) in line 1, we are allocating memory in C++/CLI dll from native heap. Also in the example in 1.a we are allocating memory from native heap. The question is - is it allowed/safe to free/delete that memory in application dll.

     

    I am new to C++/CLI and Managed world - so not well versed with the syntax. I am right now focusing only on concept and logical thinking so that before writing the app I can upfront foresee any issues mainly related to performance.

     

    Thanks again.

     

    Tuesday, May 24, 2011 6:52 AM
  • Hi, which of the three DLL/EXE can you change? I suppose that you cannot change the native EXE but can change both DLLs or atleast the wrapper.

    It has been a while when I was using CLI C++, now I have mastered all the features of C# including unsafe/fixed and realized that I no longer need CLI C++, that I can do all the things in C# - my examples are in C#.

    A1+2: Well, as I understand it, the GC requieres specific func/stack signature to be able to change pointers as needed (GC works this way). Managed code is just-in-time-compiled to native code, thus there is no problem having "unmanaged" in managed, but you cannot have managed in unmanaged (no signature).

    A3+4: I see you need C++ to compile unmanaged/native functions to be called from native EXE.

     

    1a: As I already mentioned, unmanaged memory allocation is naturally slow, it needs to walk throuh heap-list of free blocks and find the smallest that is large anough to satisfy your request. I would not be afraid of P/Invoke added overhead, it should be nothing copared to LocalAlloc complexity, but I am not an expert in that. (Managed allocation in contrast is very simple - it reserves top block, O(1) operation.)

    1b: The function itself does not need to cross managed/unmanaged as it is managed and can acces unmanaged memory. The problem would rise when calling the function from native code - but that is not about memory, that is about calling convention. Here I am not expert to tell you exactly how much it costs to call managed from unmanaged and vice versa.

    1c: I thing that your function would simply not compile. I remember using calling conventions, not pragmas... compiler should report error on this (C++ code)
    void __stdcall native(Object^ it) {}
    ..but this cannot be called from native:
    void __clrcall managed(Object^ it) {}

    I have found this article http://support.microsoft.com/kb/828736/en-us
    you may find more info in it. I remember that it was not possible to call managed from native - I wanted to call managed event OnNewSms from native code - it was not possible, I had to implement some IPC - native blocking message queue, native thread pushed messages, managed thread was sleeping until some message was put there. You'll probably need something similar, e.g. MessageQueue (Window Handle - SendMessage etc.).

    Remember, that managed pointers need managed func/stack signature/description for the GC. Theoretically, there could be some special C++ class like managed_ptr<T> that GC could recognise, but I am afraid that there is no such thing - thus you cannot pass managed pointers to native.

    ...but I can be wrong, that is really specific, I am not such expert.

     

    1d: I suppose it is safe - but I would not rely on it, I would use LocalAlloc to be absolutelly sure of it.
    I know that native C++ is using two heaps - one for "big" blocks and one for "small" blocks - that is optimalisation.
    The "new" keyword should do the same thing in both native C++ and managed/CLI C++ ... but who knows... and who can assure you that they don't change it in the future ... again, I strongly advise to use LocalAlloc on both sides.

    • Marked as answer by Anil Padia Monday, May 30, 2011 11:41 AM
    Tuesday, May 24, 2011 9:12 AM
  • Hi Firda.cz,

     

    Thank you for solving it further. I think I now have the answers - based on the replies by you.

     

    Anil Padia

    Monday, May 30, 2011 11:41 AM