locked
MarshalDirectiveException, cannot use IntPtr as return type for WinRT C++ / CX component in a C# consumer application.

    Question

  • The following C++ / CX component code is used:

    #include <stdlib.h>
    using namespace Platform;
    namespace IntPtrTestComponent
    {
       public ref class IntPtrTest sealed
       {
       public: IntPtrTest(){}
       public: IntPtr GetIntPtr(){ return IntPtr(malloc(1024)); }
       };
    }

    When trying to consume this in a C# application, MarshalDirectiveException will be thrown:

    Cannot marshal 'return value': Windows Runtime marshaler does not support the type.

    Tuesday, August 28, 2012 1:37 PM

Answers

  • Hi,

    All the public return value should be the fundamental types. The IntPtr is fundamental type, but it only use for internal. It means that you can not call it out of DLL.

    Please check this document.
    http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh700121.aspx 

    Best regards,
    Jesse


    Jesse Jiang [MSFT]
    MSDN Community Support | Feedback to us

    Wednesday, August 29, 2012 9:23 AM
  • WinRT does not allow passing raw pointers across an ABI boundary, so you won't be successful at passing IntPtr.

    Alternatively, if you want to pass an opaque identifier/token across the ABI, that should work. Take a look at how event registration works: when you subscribe, you get back a EventRegistrationToken and to unsubscribe, you need to pass that EventRegistrationToken back to the component.

    Similarly, you could create your own value type YourCppNamespace::InternalTypeToken.

    public value struct InternalTypeToken {
       uint32 __value;
    };

    Each instance of this value type can uniquely identify in your underlying implementation an address of memory you allocated. You should be able to pass this type around to other WinRT languages and they can pass it back to your component. But your component would be the only one who would know how to interpret this type.

    If you want to allow other clients/languages to also manage the lifetime of this memory, you can alternatively author a reference type that supports deterministic destruction/dispose and pass that around:

     public ref class InternalType sealed {
     public:
      InternalType() { mem = malloc(1000); }
      virtual ~InternalType() { free(mem); mem = nullptr; }
     internal:
      void* get_Mem() { return mem; }
     private:
      void* mem;
     };

    Hope this helps,
    Marian Luparu
    Visual C++

    • Marked as answer by Jesse Jiang Monday, September 03, 2012 7:09 AM
    Thursday, August 30, 2012 10:25 PM

All replies

  • I am having the same issue. But i'm doing the revers, trying to pass in an IntPtr from C# to C++/CX method parameter.

    I get the same error "Windows Runtime marshaler does not support the type"

    Wednesday, August 29, 2012 3:47 AM
  • Hi,

    All the public return value should be the fundamental types. The IntPtr is fundamental type, but it only use for internal. It means that you can not call it out of DLL.

    Please check this document.
    http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh700121.aspx 

    Best regards,
    Jesse


    Jesse Jiang [MSFT]
    MSDN Community Support | Feedback to us

    Wednesday, August 29, 2012 9:23 AM
  • If you want to allocate a buffer of bytes, you can always use Platform::Array<uint8>^ as the return value type. This type would project correctly to C# as well.

    Is this want you want to achieve?

    Marian Luparu
    Visual C++

    Thursday, August 30, 2012 5:06 AM
  • No, what I want, is to pass an unmanaged pointer back to C#, something like

    C# code:

    IntPtr value = CppClass1.GetValue();
    // Don’t use Value, just pass to another C++ class
    CppClass2.SetValue(value);
    

    Thursday, August 30, 2012 6:40 PM
  • WinRT does not allow passing raw pointers across an ABI boundary, so you won't be successful at passing IntPtr.

    Alternatively, if you want to pass an opaque identifier/token across the ABI, that should work. Take a look at how event registration works: when you subscribe, you get back a EventRegistrationToken and to unsubscribe, you need to pass that EventRegistrationToken back to the component.

    Similarly, you could create your own value type YourCppNamespace::InternalTypeToken.

    public value struct InternalTypeToken {
       uint32 __value;
    };

    Each instance of this value type can uniquely identify in your underlying implementation an address of memory you allocated. You should be able to pass this type around to other WinRT languages and they can pass it back to your component. But your component would be the only one who would know how to interpret this type.

    If you want to allow other clients/languages to also manage the lifetime of this memory, you can alternatively author a reference type that supports deterministic destruction/dispose and pass that around:

     public ref class InternalType sealed {
     public:
      InternalType() { mem = malloc(1000); }
      virtual ~InternalType() { free(mem); mem = nullptr; }
     internal:
      void* get_Mem() { return mem; }
     private:
      void* mem;
     };

    Hope this helps,
    Marian Luparu
    Visual C++

    • Marked as answer by Jesse Jiang Monday, September 03, 2012 7:09 AM
    Thursday, August 30, 2012 10:25 PM
  • Hi, is it a valid workaround ?

    #if defined( _WIN64 ) // Compile time.
    	typedef uint64 VarPtr;
    #else
    	typedef uint32 VarPtr;
    #endif
    
    class MyNativeClass
    {
    
    };
    
    public ref class MyRefClass sealed
    {
    
    private :
    		
    	MyNativeClass* m_ptr;
    
    public :
    
    	VarPtr GetPtr( ) { return reinterpret_cast< VarPtr >( m_ptr ); }
    };

    Friday, August 31, 2012 12:34 AM