Convert C++ dll exposed function from a C++ dll in C#

bioan 41 Reputation points
2021-09-26T14:04:48.62+00:00

Hi everybody!

I have the following C++ function signature:

unsigned int MyCPlusPlusFunction(IUnknown* document, unsigned int id, const wchar_t* name, IUnknown** ids, unsigned int* flags, unsigned int* size);

which I want to use inside a C# .dll library project using P/Invoke mechanism.

Can you direct me how to use it inside my C# code?

Thank you very much!

C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
11,123 questions
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,787 questions
0 comments No comments
{count} votes

Accepted answer
  1. RLWA32 46,111 Reputation points
    2021-09-27T12:57:30.883+00:00

    It appears that the caller is responsible for passing an array of IUnknown pointers and the number of elements in the array and the MyCPlusPlus function will fill the array and indicate the number of IUnknown pointers that were stored in the array.

    Give this a try to let the .Net handle the marshaling for you -

            [DllImport("ArrayMarshal.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
            static extern uint MyCPlusPlusFunction([MarshalAs(UnmanagedType.IUnknown)] object document, uint id, string name,
                [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.IUnknown)] object[] ids,
                ref uint flags, ref uint size);
    

    And the call the function like this with respect to the IUnknown pointer array -

            uint id = 5;
            uint aSize = 16;
            uint flags = 42;
            object[] aObjects = new object[aSize];
    
            uint ret = MyCPlusPlusFunction(null, id, "test", aObjects, ref flags, ref aSize);
    
    0 comments No comments

5 additional answers

Sort by: Most helpful
  1. Castorix31 86,316 Reputation points
    2021-09-26T16:15:47.02+00:00

    You can define it with something like :

            [DllImport("Your_DLL.dll", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
            public static extern uint MyCPlusPlusFunction(IntPtr document, uint id, string name, ref IntPtr ids, ref uint flags, ref uint size);
    
    0 comments No comments

  2. bioan 41 Reputation points
    2021-09-26T17:47:19.567+00:00

    I tested the proposed solution and it doesn't seems to work.

    Other functions included in the same C++ .dll which use IUnknown* argument and not pointer to pointer IUnknown** pArgument work very well with the following function signature model:

    [DllImport("Your_DLL.dll", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    public static extern uint  OtherFunction([MarshalAs(UnmanagedType.IUnknown)] object document, uint id);
    

    It is possible to express the fourth argument of MyCPlusPlusFunction() above - IUnknown** ids - using [MarshalAs(UnmanagedType.IUnknown)]?
    Thank you!

    0 comments No comments

  3. Castorix31 86,316 Reputation points
    2021-09-26T18:27:42.273+00:00

    [cannot post code in comments]

    I did a test with a random Shell interface (IOpenControlPanel to launch Control Panels) returned by the function and it works :

    C++ :

    __declspec(dllexport) unsigned int MyCPlusPlusFunction(IUnknown* document, unsigned int id, const wchar_t* name, IUnknown** ids, unsigned int* flags, unsigned int* size)
    {
        TCHAR wsMessage[260] = L"";
        (*flags) += 1;
        (*size) *= 10;
        IOpenControlPanel* pOcp;
        HRESULT hr = CoCreateInstance(CLSID_OpenControlPanel, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pOcp));
        *ids = pOcp;
        swprintf(wsMessage, L"Name : %s \nids : %x\nflags : %d\nsize : %d", name, ids, (*flags), (*size));
        MessageBox(NULL, wsMessage, L"Information", MB_OK | MB_ICONINFORMATION);
        return hr;
    };
    

    C# :

            [ComImport]
            [Guid("D11AD862-66DE-4DF4-BF6C-1F5621996AF1")]
            [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
            public interface IOpenControlPanel
            {
                int Open(string pszName, string pszPage, IntPtr punkSite);
                int GetPath(string pszName, StringBuilder pszPath, uint cchPath);
                int GetCurrentView(ref CPVIEW pView);
            }
            public enum CPVIEW
            {
                CPVIEW_CLASSIC = 0,
                CPVIEW_ALLITEMS = CPVIEW_CLASSIC,
                CPVIEW_CATEGORY = 1,
                CPVIEW_HOME = CPVIEW_CATEGORY
            } 
    
            [DllImport("E:\\Sources\\DLLTest\\Debug\\DLLTest.dll", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
            public static extern uint MyCPlusPlusFunction(IntPtr document, uint id, string name, ref IntPtr ids, ref uint flags, ref uint size);
    

    Test function :

                    IntPtr pUnknown = IntPtr.Zero;
                    uint flags = 2;
                    uint size = 5;
                    uint nRet = MyCPlusPlusFunction(IntPtr.Zero, 1, "Test", ref pUnknown, ref flags, ref size);
                    IOpenControlPanel pOcp = Marshal.GetObjectForIUnknown(pUnknown) as IOpenControlPanel;
                    if (pOcp != null)
                    {
                        pOcp.Open("Microsoft.Display", null, IntPtr.Zero);
                        StringBuilder sb = new StringBuilder(260);                   
                        pOcp.GetPath(null, sb, (uint)sb.Capacity);
                    }
    
    0 comments No comments

  4. bioan 41 Reputation points
    2021-09-26T19:28:55.27+00:00

    Bizarre.. I introduce the exact code from your example, but pUnknown has 0x0000000000000000 value all the time ..

    All I can do is to indicate the usage of the C++ MyCPlusPlusFunction () from the C++ side code:

    //param document the current document 
    //param id The id of the operation.
    //param name identifies the geometry to be retrieved. Can be any of these: faces, edges
    
      IUnknown* ids[16]; // fill with host app geometry references
      unsigned int flags[16];
      unsigned int numReferences = sizeof(ids)/sizeof(ids[0]);
      if MyCPlusPlusFunction(document, objectId, "edges", ids, flags, &numReferences) == OKS) {
        // Process references
        for (unsigned int i = 0; i < numReferences; i++) {
          ids[i] ...
          flags[i] ...
        }
      }
    
      //param ids will be filled with a list of host app references.
      //param flags will be filled with a list of corresponding geometry flag values - one for each returned reference.
      //param size is the size of the ids buffer on input, and will be set to the actual number of references returned on output.
    

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.