See the question and my original answer on StackOverflow

You can't define a C++/WinRT object pointer at the dll frontier, as at binary level, it's not a COM object (so C# is passing a COM object that's mapped to a C++/WinRT object, which is bad). What you can do instead is something like this:

void __stdcall Direct3DSurfaceAccess(
    IUnknown* surface, // pass a raw COM object (here I use IUnknown)
    char* message,
    size_t messageLength
) {
    try {
        // map to a C++/WinRT object here
        auto d3dSurface = convert_from_abi<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DSurface>(surface);

        winrt::com_ptr<::Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess> dxgiInterfaceAccess;
        if (!d3dSurface.try_as(dxgiInterfaceAccess)) {
            ::sprintf_s(message, messageLength, "d3dSurface->try_as failed.");
            return;
        }

        // Call other d3dSurface method as a test.
        auto desc = d3dSurface.Description();

convert_from_abi comes from here convert_from_abi function

and in this case, you can define the interop function like this:

[DllImport(@"Direct3DSurfaceAccess.dll")]
private static extern void Direct3DSurfaceAccess([MarshalAs(UnmanagedType.IUnknown)] object d3dSurface);