views:

361

answers:

4

EDIT: I just realized this is defined as a MACRO, not a function. How the heck would I import a macro from a DLL to C#? (this may have to be a new question).

This is related to a question I just asked:

How Do I Properly Return A Char From An Unmanaged Dll To C#?

Some of the answers suggested I change the function signature to IntPtr or StringBuilder. I also saw these solutions on a few sites in my googling, most notably here. (There were others, but I don't have time to hunt down the links).

Function signature:

[DllImport("api.dll")]       
internal static extern char[] errMessage(int err);

If I change the return type, my call throws the following exception:

"Unable to find an entry point named 'errMessage' in DLL"

I can't imagine people would suggest this if it didn't work. Am I doing something wrong here? Is there something missing? Sad to say, but my C/C++ skills are terrible, so I could be missing something really simple. Thanks for any help.

EDIT: Function signature from documentation:

char * errMessage(int err);
A: 

You may need to change your C/C++ method to return a pointer, AFAIK I have not seen a DllImport returning back an array of characters from a function. I would suggest you change around your API code within the function errMessage, perhaps instead of returning back char[], you could return back a pointer to char in your api.dll library...i.e.

void errMessage(int err, char *ptr);

On second note...are you using this to return back a standard Win API error code, there is a Marshal.Win32Error class that would do just the job for you? What do you think? Tom.

tommieb75
I am trying to write a C# wrapper for a third party API. I've edited the post above to show exactly what I am trying to call. This method takes an int and returns their specific error messages. A lot of their methods have char * as the return type.
IronicMuffin
A: 

Looks like the calling convention is not stdcall (which is the default one used by DllImport). C/C++ use the CDECL calling convention if not declared otherwise. So you have to add this calling convention to the DllImport call.

Robert Giesecke
+1  A: 

EDIT: updated to reflect importing a macro:

You can't.

Now, what you can do is look at what the macro does, then implement that with P/Invoke. The below advice holds for that as well.


When working with Platform Invoke (P/Invoke) it is best to be as explicit as possible:

internal static class NativeMethods
{
    private static string DllName = @"api.dll";

    // This uses 'string' assuming you do not have to free the memory.
    [DllImport(DllName, EntryPoint = "errMessage",
         CharSet = YourCharacterSet,              // CharSet.Ansi? .Unicode?
         CallingConvention = DllCallingConvention // .StdCall? .Cdecl?
    )]
    public static string errMessage(int errorCode);
}

Moreover, it is best to provide a managed entry-point that makes the method more ".Net". This is where you would ensure that Caller allocated memory gets held the appropriate amount of time (you may have to implement a SafeHandle) or that other hand-waving gets handled.

Assuming errMessage returns a string we aren't responsible for deallocating:

public static class ManagedMethods
{
    public static string ErrorMessage(ErrorCode errorCode)
    {
        return NativeMethods.errMessage((int)errorCode);
    }
}
sixlettervariables
+2  A: 

Write a C++/CLI wrapper. It is an amazingly easy and powerful way to bridge the gap between C# and unmanaged (old) C/C++.

danbystrom