Disclaimer: C++/CLI Noob question
I'm attempting to use a PInvoke on a C++ DLL that has a std::string in the signature. At the moment I just testing: my goal is to pass a string to the native DLL, and return it.
The native export looks like this:
#define NATIVE_CPP_API __declspec(dllexport)
NATIVE_CPP_API void hello_std(std::string inp, char* buffer)
{
    const char* data = inp.data();
    strcpy(buffer, data);
}
I'm attempting to PInvoke it in the normal way, with a custom marshaler:
[DllImport("native_cpp.dll", EntryPoint = "?hello_std@@YAPADV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z", CallingConvention = CallingConvention.Cdecl)]
private static extern void hello_std(
    [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(clr_wrapper.string_marshaler))]
        String inp,
        StringBuilder buffer);
static void Main(string[] args)
{
    var buffer = new StringBuilder(100);
    hello_std("abcdefg", buffer);
    Console.WriteLine(buffer);
    Console.ReadLine();
}
The custom marshaler specified here, clr_wrapper.string_marshaler, is an ICustomMarshaler in a C++/CLI project, and is intended to take the System::String input and convert it to the native std::string.  My MarshalManagedToNative implementation is a stab in the dark.  I've tried a few things, but this is my best guess:
IntPtr string_marshaler::MarshalManagedToNative( Object^ ManagedObj )
{
    String^ val = (String^) ManagedObj;
    size_t size = (size_t)val->Length;
    char* ptr = (char*) Marshal::StringToHGlobalAnsi(val->ToString()).ToPointer();
    std::string * str = new std::string(ptr, size);
    IntPtr retval = (IntPtr) str;
    return retval;
}
Unfortunately, when I attempt to run this, PInvoke call triggers an AccessViolationException.
What am I doing wrong, or is this entire venture ill-conceived?
First Edit, Complete Listing
1. C# Console App
class Program
{
    static void Main(string[] args)
    {
        var buffer = new StringBuilder(100);
        hello_std("abcdefg", buffer);
        Console.WriteLine(buffer);
        Console.ReadLine();
    }
    [DllImport("native_cpp.dll", EntryPoint = "?hello_std@@YAXV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@PAD@Z", CallingConvention = CallingConvention.Cdecl)]
    private static extern void hello_std(
        [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(clr_wrapper.string_marshaler))]
        [In]
        String inp,
        StringBuilder buffer
    );
}
2. Native C++ DLL project "native_cpp"
native_cpp.h
#ifdef NATIVE_CPP_EXPORTS
#define NATIVE_CPP_API __declspec(dllexport)
#else
#define NATIVE_CPP_API __declspec(dllimport)
#endif
#include <string>
NATIVE_CPP_API void hello_std(std::string inp, char* buffer);
native_cpp.cpp
#include "native_cpp.h"
void hello_std(std::string inp, char* buffer)
{
    const char* data = inp.data();
    strcpy(buffer, data);
}
3. C++/CLI project "clr_wrapper"
clr_wrapper.h
#pragma once
using namespace System;
using namespace System::Runtime::InteropServices;
namespace clr_wrapper {
    public ref class string_marshaler : public ICustomMarshaler
    {
    public:
        string_marshaler(void);
        virtual Object^ MarshalNativeToManaged( IntPtr pNativeData );
        virtual IntPtr MarshalManagedToNative( Object^ ManagedObj );
        virtual void CleanUpNativeData( IntPtr pNativeData );
        virtual void CleanUpManagedData( Object^ ManagedObj );
        virtual int GetNativeDataSize();
        static ICustomMarshaler ^ GetInstance(String ^ pstrCookie)
        {
            return gcnew string_marshaler();
        }
    private:
        void* m_ptr;
        int m_size;
    };
}
clr_wrapper.cpp
#include "clr_wrapper.h"
#include <string>
using namespace clr_wrapper;
using namespace System::Text;
string_marshaler::string_marshaler(void)
{
}
Object^ string_marshaler::MarshalNativeToManaged( IntPtr pNativeData )
{
    return Marshal::PtrToStringAnsi(pNativeData);
}
IntPtr string_marshaler::MarshalManagedToNative( Object^ ManagedObj )
{
    String^ val = (String^) ManagedObj;
    size_t size = (size_t) val->Length;
    char* ptr = (char*) Marshal::StringToHGlobalAnsi(val->ToString()).ToPointer();
    std::string * str = new std::string(ptr, size);
m_size = sizeof(str*);
    m_ptr = (void*) str;
    IntPtr retval = (IntPtr) str;
    return retval;
}
void string_marshaler::CleanUpNativeData( IntPtr pNativeData )
{
    //Marshal::FreeHGlobal(pNativeData);
    delete (std::string*) m_ptr;
}
void string_marshaler::CleanUpManagedData( Object^ ManagedObj )
{
}
int string_marshaler::GetNativeDataSize()
{
    return m_size;
}
End First Edit