Lucky Luke2
Lucky Luke2

Reputation: 2116

Calling delphi function in c#

I am calling a delphi function within a dll from c#. Here is the delphi function signature -

function CALinkDecode(sCode: PWideChar; SubscriberID, MailshotID, LinkID: PDWORD):
  PAnsiChar; stdcall;

And here is how i am calling it.

 string input = "qvoxyetvr7wksss2zbrmr";
        int cas, cam, cal;
        var errorString = CALinkDecode(input, out cas,
                                         out cam, out cal);

But when i run the app its evaluating this condition as true in delphi function -

if (sCode = nil) or (Length(sCode) <> 21) or (SubscriberID = nil) or (MailshotID = nil) or (LinkID = nil) then
begin
  Result := 'E_INVALIDARG';
end

I would just like to know if i am passing in the correct datatypes for the function signature and what is the correct way to do it. Thanks.

Upvotes: 3

Views: 779

Answers (1)

David Heffernan
David Heffernan

Reputation: 612794

Your immediate problem is likely that you are passing ANSI text to the sCode parameter. Unfortunately you omitted your p/invoke declaration which would have made that clear.

That's easy to fix, but you have much bigger problems. This function is hard to call because it has been designed incorrectly. It's only viable if it returns constant strings that are allocated by the compiler rather than heap allocated strings. You could force it to work with that signature if you:

  1. Exported a deallocator function from the native code.
  2. Allocated the string using a shared heap, e.g. the COM heap.

It looks like you are returning COM error codes in string form. That's perverse. Instead you could make your life very easy by returning HRESULT.

Anyway, to answer the direct question you declare the p/invoke like this:

[DllImport(@"Mylib.dll", CharSet=CharSet.Unicode)]
static extern IntPtr CALinkDecode(
    string sCode,
    out uint SubscriberID,
    out uint MailshotID,
    out uint LinkID
);

Then call like this

IntPtr retvalptr = CALinkDecode(...);

Then marshal the pointer to a string

string retval = Marshal.PtrToStringAnsi(retvalptr);

But remember that this can only work if the returned string data is statically allocated. Otherwise you will have leaks, or access violations.

My primary advice is to redesign the interface. Don't return an error code in a string.

On a general note, if you must pass strings from Delphi to C# do it like this:

  • Declare an out parameter of type WideString in the Delphi code. This wraps a COM BSTR.
  • On the managed side declare an out parameter of type string. Decorate that parameter with [MarshalAs(UnmanagedType.BStr)].

Note that you cannot marshal a WideString return value to MS tools because Delphi uses a different ABI for return values. That's why you use an out parameter.

Upvotes: 4

Related Questions