2

I have the following function in C++ native dll, and I want to use it in a C# app.

DWORD __cdecl Foo(
        LPCTSTR             Input,
        TCHAR**             Output,          
        DWORD               Options,        
        ErroneousWord**     List = NULL,
        LPDWORD             Count = 0
        );

Using Pinvoke

[DllImport("dllName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
        public static extern UInt32 Foo(string InputWord, out string Output, UInt32 Options, out object List,out UInt32 Count);

Calling code:

            string output;
            object dummyError = null;
            uint dummyCount = 0;
            uint x = 0;
            Foo(Text, out output, x | y,out  dummyError,out dummyCount);

I got the following exception

Attempted to read or write protected memory. This is often an indication that other memory is corrupt

P.S: ErroneousWord is struct and I do not need its output, so I marshal it as object

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Ahmed
  • 7,148
  • 12
  • 57
  • 96
  • 1
    What is `ErroneousWord`? No matter what it is, I can't imagine that it can be marshalled into a C# object. And what about the `Output` parameter? What is the protocol for the marhsalling of that? – David Heffernan May 16 '11 at 14:21
  • Output is created from the native code, and there is exposed function to free it, so the native code allocate the output and expose a function to free it – Ahmed May 16 '11 at 14:31
  • I think for Output you are going to need to use `Marshal.PtrToStructure`. I'm less optimistic about `ErroneousWord`. – David Heffernan May 16 '11 at 14:32
  • @David: Why would Output be a problem? It's a pointer to `char*`, and `char*` is a string. That *should* marshal as a `string` without any problems. – Cody Gray - on strike May 16 '11 at 14:34
  • For the string `Output` I would recommend using a `BSTR` and so avoiding having to export the deallocator. See http://stackoverflow.com/questions/5308584/how-to-return-text-from-native-c-code – David Heffernan May 16 '11 at 14:34
  • @Cody `string` marshalling works fine in the other direction. I don't believe it works from native -> managed. – David Heffernan May 16 '11 at 14:35
  • ErroneousWord is struct and I'm not interested in – Ahmed May 16 '11 at 14:36
  • @David: Ah, yes. That's correct. You need to use a `StringBuilder` if the string is an output parameter. – Cody Gray - on strike May 16 '11 at 14:37
  • I dont have code of the dll, I'm only using it from C# – Ahmed May 16 '11 at 14:38

4 Answers4

3

That error more than likely means that you have a marshaling problem.

You don't show us what the ErroneousWord type is, but I assume it's some kind of class defined in your C++ code. My guess is that it's not being marshaled correctly to a .NET object.

Considering that it's a pointer (or a pointer to a pointer), try changing that parameter to an IntPtr type to represent a pointer, instead. It shouldn't matter, since you're simply passing NULL for the argument anyway, easily represented using the static IntPtr.Zero field.

You probably also want to marshal Output the exact same way. If you change the parameter to an IntPtr type, you'll receive a pointer to a TCHAR*, which you can then pass to the other unmanaged functions however you see fit (e.g., to free it).

Try the following code:

[
DllImport("dllName",
CharSet = CharSet.Unicode,
CallingConvention = CallingConvention.Cdecl)
]
public static extern UInt32 Foo(
                                string InputWord,
                                out IntPtr Output,     // change to IntPtr
                                UInt32 Options,
                                out IntPtr List,       // change to IntPtr
                                out UInt32 Count);

IntPtr output;
IntPtr dummyError = IntPtr.Zero;
uint dummyCount = 0;
uint x = 0;
Foo(Text, out output, x | y, out dummyError, out dummyCount);

You might also need to use the Marshal.AllocHGlobal method to allocate unmanaged memory from your process that is accessible to the C++ code. Make sure that if you do so, you also call the corresponding Marshal.FreeHGlobal method to release the memory.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • I find it hard to believe that `out string Output` could work. – David Heffernan May 16 '11 at 14:34
  • The Output parameter is created from the native dll and the dll exposes another function to free it, so why I need to use StringBuilder?? – Ahmed May 16 '11 at 14:44
  • @Ahmed: Uhh, Output is *created* by the native DLL? Then why are you passing it *back* to the native DLL? If you don't care about its return value, just pass it as an `IntPtr` type, same as you do for `List`. Treat it as an opaque handle. – Cody Gray - on strike May 16 '11 at 14:47
  • @Cody: output is pointer to TCHAR* (string) and it is created by dll and after the caller finsihing its work with the output it uses another function exposed by the dll to free it (deallocate) – Ahmed May 16 '11 at 14:52
  • @Ahmed: I don't really understand what that means, but I don't understand why you can't simply treat it as an `IntPtr` type, which would represent a pointer to a string. You can then pass that pointer *back* to the function responsible for freeing the string in the unmanaged DLL. – Cody Gray - on strike May 16 '11 at 14:53
  • @Cody: I think this is the answer – Ahmed May 16 '11 at 14:55
  • @Ahmed: I edited the answer. Have you tried it to make sure it works? – Cody Gray - on strike May 16 '11 at 14:57
  • @Cody: Yes it works fine, I may need to test if there is memory leaks (if the free method could not free the *output*) – Ahmed May 16 '11 at 14:59
2

Given Cody's answer and the comments, you will have to do it this way:

[DllImport("dllName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
extern static UInt32 Foo(string InputWord, out IntPtr Output, UInt32 Options, out IntPtr List, out UInt32 Count);

Now to get the string value in Output marshalled over to managed memory you will do:

string outputValue = Marshal.PtrToStringAnsi(Output);

You must know if TCHAR is Ansi or Unicode and use the appropriate marshal.

Remember to hang onto the Output IntPtr so you can pass that to the native Free method.

Tergiver
  • 14,171
  • 3
  • 41
  • 68
2

Thanks Cody for your answer but I want to make a seperate one, first Output is created by Foo from the native side, and I call FreeFoo to free the allocated memory by Foo. The following is the code

[DllImport("dllname", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
        public static extern UInt32 Correct(string InputWord, out IntPtr Output, UInt32 Options, out object List,out UInt32 Count);

        [DllImport("dllname", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
        public static extern void FreeFoo(IntPtr Output);

}

To use it:

    public string FooWrapper(string Text)
    {
        IntPtr output;
        object dummyError = null;
        uint dummyCount = 0;
        uint x = 0;
        Foo(Text, out output, x,out  dummyError,out dummyCount);
        string str = Marshal.PtrToStringUni(output);
        FreeFoo(output);
        return str;
    }
Ahmed
  • 7,148
  • 12
  • 57
  • 96
1

Whatever the ErroneousWord type is, you can't marshal an array as a single out object. If it is at all possible to marshal as an object...

Pontus Gagge
  • 17,166
  • 1
  • 38
  • 51