Search code examples
c#c++pinvokeaccess-violation

Using P/Invoke causes system AccessViolationException


I am having trouble with using a C++ function from C# code using P/Invoke. I have used the tutorial on http://www.codeproject.com/Articles/403285/P-Invoke-Tutorial-Basics-Part as a basic example, and once I got that to work, I adapted it for my own code.

This is producing a System.AccessViolationException, with additional information: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.'

My C++ header file 'NativeLib.h' is as follows:

#include <string>

#ifndef _NATIVELIB_H_
#define _NATIVELIB_H_

#ifndef MYAPI
#define MYAPI
#endif

#ifdef __cplusplus
extern "C" {
#endif

    MYAPI float modelScore(std::string word);

#ifdef __cplusplus
}
#endif

#endif // _NATIVELIB_H_

where MYAPI is a preprocessor definition defined as 'MYAPI=__declspec(dllexport)'. The .cpp file, 'NativeLib.cpp' is as follows:

#include "NativeLib.h"
#include <stdio.h>
#include "lm/model.hh"
#include <iostream>
#include <string>

MYAPI float modelScore(std::string word) {
    using namespace lm::ngram;
    Model model(---MODEL FILE LOCATION---);

    State state(model.BeginSentenceState()), out_state;
    const Vocabulary &vocab = model.GetVocabulary();

    return model.Score(state, vocab.Index(word), out_state);

}

I am accessing this from C# using the code below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace PInvokeTest
{
    class Program
    {
        static void Main(string[] args)
        {
            modelScore("a");
            Console.WriteLine("Press enter to close...");
            Console.ReadLine();
        }

        [DllImport("NativeLib.dll", CallingConvention = CallingConvention.Cdecl)]
        private static extern float modelScore(string word);
    }
}

The code is building without failing with all the appropriate libraries linked and included in the header paths. The C++ code works well from C++ itself so my problem lies in linking the code with C# but I cannot see where the problem is. Any help would be really appreciated.


Solution

  • P/Invoke marshals your C# string into a C string, by default. Your C function's parameter should be a const char*, not an std::string.

    In general, you should avoid exporting functions with signatures that depend on non-POD types, like std::string. The consumer (in this case, C#) has no idea about the memory layout of the std::string used by your DLL, so it can't even create one to invoke your function with.