Puppy
Puppy

Reputation: 147036

Marshalling simple string in C#

I've totally failed this really simple P/Invoke.

    [System.Runtime.InteropServices.DllImport(
        "temp.dll"
    )]
    private static extern System.IntPtr parser_alloc();

    [System.Runtime.InteropServices.DllImport(
        "temp.dll", 
        CharSet = System.Runtime.InteropServices.CharSet.Ansi
    )]
    private static extern void parser_add_file(
        System.IntPtr p,
        [MarshalAs(UnmanagedType.LPStr)]string f,
        [MarshalAs(UnmanagedType.LPStr)]string s);

    [System.Runtime.InteropServices.DllImport(
        "temp.dll"
    )]
    private static extern void parser_free(
        System.IntPtr p);

    [System.Runtime.InteropServices.DllImport(
        "temp.dll"
    )]
    private static extern void parser_emit_results(
        System.IntPtr p);

    [System.Runtime.InteropServices.DllImport(
        "temp.dll",
        CharSet = System.Runtime.InteropServices.CharSet.Ansi
    )]
    private static extern void parser_file_updated(
        System.IntPtr p,
        [MarshalAs(UnmanagedType.LPStr)]string f,
        int offset,
        int added);

Creating the object seems to go fine. However, when I try to call parser_add_file, then the runtime tells me I have a mismatched signature. This is my existing C++ code:

class Parser {
public:
    void add_file(std::string filename, std::string source) {
        std::cout << "add_file | " << filename << " | " << source << "\n";
    }
    void clear_contents() {
        std::cout << "clear_contents\n";
    }
    void emit_results() {
        std::cout << "emit_results\n";
    }
    void file_updated(std::string filename, int offset, int added) {
        std::cout << "file_updated | " << filename << " | " << offset << " | " << added << "\n";
        // added should be negative to signify removing
    }
    Parser() {
        std::cout << "I made a Parser!\n";
    }
};

extern "C" __declspec(dllexport) Parser* parser_alloc() {
    return new Parser;
};

extern "C" __declspec(dllexport) void parser_add_file(Parser* p, const char* filename, const char* string) {
    p->add_file(filename, string);
}

extern "C" __declspec(dllexport) void parser_free(Parser* p) {
    delete p;
}

 extern "C" __declspec(dllexport) void parser_emit_results(Parser* p) {
    p->emit_results();
}

 extern "C" __declspec(dllexport) void parser_file_updated(Parser* p, const char* filename, int offset, int added) {
    p->file_updated(filename, offset, added);
}

Should I just skip P/Invoke and go to C++/CLI for this job?

Upvotes: 2

Views: 416

Answers (2)

SRM
SRM

Reputation: 1377

If you have access to the c++ source (which it looks like you do) you can change the calling convention at the c++ declaration level as well. Here is an example function for a library I wrote:

int _stdcall GeoConvertLatLongToUTM(double latitude, double longitude, char *szOutput)

Upvotes: 0

Puppy
Puppy

Reputation: 147036

Turns out that I manually had to set the calling convention to cdecl- the default is stdcall.

Upvotes: 1

Related Questions