-1

This is my first question here. I'm also new to programming, so I'll do my best to make it as clear as possible.

There's this DLL that was originally written in Delphi. One of its functions requires an int (Integer in Delphi) as its argument and in turn returns a string (PWideChar in Delphi).

The DLL documentation does not bring many details, so I'm trying to explore different possibilities in order to get the proper results. Also, there's no issue with the DLL, as I'm able to successfully call this function from Python by using restype. E.g.:

DLLName.FunctionName.restype = c_wchar_p 

When I call the callback function below from C# (5.0) though, I'm able to see something whenever I use any number type, being that either sbyte, int or long, for example. However, I get an error when I try to use string, which I'd think would be the appropriate data type to be placed as its return data type:

[DllImport(dll_path, CallingConvention = CallingConvention.StdCall)]
    public static extern sbyte GetNameByID(int nID);

This was my first trial:

public static string GetName()
    {   
       string result = GetNameByID(1)
       return result;
    }

But, as I mentioned, I'd only see something being returned when I use numerical data types instead of string. I also (naively) gave it a trial with .ToString() or casting the result with (string)...


SOLUTION AFTER INVESTIGATION:

For those that may be facing similar issues, this is how the issue was solved, after some tentatives to figure it out:

The function call returns a pointer, so:

    [DllImport(dll_path, CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr GetNameByID(int nID);

And then, when hadling its return, marshalling the pointer to Unicode string:

public static string GetName()
    {   
       string result = Marshal.PtrToStringUni(GetNameByID(1)));
       return result;
    }
roccaforte
  • 81
  • 5
  • 1
    Who is responsible for the memory that the DLL function returns a pointer to? If the caller is responsible for freeing the memory, that will complicate the C# code a bit, as you would have to use `IntPtr` for the return value, and then marshal the character data manually. – Remy Lebeau Nov 21 '21 at 00:05
  • 1
    We need to see how the Delphi code is allocating the string. If it uses `SysAllocString` then `BStr` will work fine. But it may not be doing that. – Charlieface Nov 21 '21 at 01:57
  • Hi @RemyLebeau, there are a couple of other functions in this DLL in which IntPtr needed to be used, but that's not the case of this specific one. It returns a string with less then 10 characters. – roccaforte Nov 21 '21 at 03:18
  • @roccaforte the length of the string doesn't matter. The way the string is allocated matters greatly. – Remy Lebeau Nov 21 '21 at 05:21
  • We can't answer this until we see the Delphi code and there learn how the memory is managed for the return value – David Heffernan Nov 21 '21 at 09:12

1 Answers1

0

I had a similar problem using a C++ DLL. Maybe this will work in your case as well:

[DllImport(dll_path, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string getNameById(int nID);

public static string GetName()
{
    string result = GetNameByID(1);
    return result;
}

EDIT: Found this link with a similar issue already on stack overflow.

  • 2
    `CharSet.Ansi` is not compatible with Delphi's `PWideChar`. And `UnmanagedType.BStr` requires a COM `BSTR`, which Delphi strings are not. Use `UnmanagedType.LPWStr` instead. – Remy Lebeau Nov 21 '21 at 00:03
  • Hello Jakub. Thanks for helping. This gave me some direction, as a matter of fact. However, the application suddenly closes and it has been difficult to track what is happening, as there is no exception being raised and I can't see any specific errors. What @RemyLebeau mentioned also helped a lot, as UnmanagedType.LPWStr seems to be the correct one to use. Unfortunately, I'm not sure where to go from here. I know that's an open question: do you have any idea on what may be happening? It's quite tricky, as I can do it with Python with no issues. – roccaforte Nov 21 '21 at 03:10
  • Just complementing: the application started to suddenly close after making the changes you suggested accordingly. This happens with both UnmanagedType.Bstr and UnmanagedType.LPWStr. I also tried to use a StringBuilder instead of string itself, with no success though. – roccaforte Nov 21 '21 at 03:25
  • @roccaforte do you have any Delphi code for the DLL? Either the implementation itself, or at least a demo? If not, any document, at least? You are leaving out a lot of important details that matter when interfacing with a native DLL in C#. – Remy Lebeau Nov 21 '21 at 05:23
  • @roccaforte I found a link to a similar issue, posted it on this answer. – Jakub Kalinowski Nov 21 '21 at 12:06
  • @JakubKalinowski I've included the final solution in the original post. Thanks for sheding some light. It helped a lot. – roccaforte Nov 22 '21 at 14:10
  • @rocca almost certainly that's not the right solution. – David Heffernan Nov 22 '21 at 17:10
  • @David Heffernan, do you mean the one I included in the original post as an update? It worked fine for me. – roccaforte Nov 22 '21 at 17:41
  • @rocca probably not. It's unlikely that the library returns a pointer to null terminated string that doesn't need deallocating, or that it will deallocate. It probably just looks like it works but it leaks. Or even worse the library returns a Delphi string that is declared locally. – David Heffernan Nov 22 '21 at 20:03