4

I have a below DLL source code.

library Project1;

uses
  System.SysUtils,
  System.Classes;

type

  IStringFunctions = interface
    ['{240B567B-E619-48E4-8CDA-F6A722F44A71}']
    function GetMethodValueAsString():PAnsiChar; stdcall;
  end;

  TStringFunctions = class(TInterfacedObject, IStringFunctions)
  public
    function GetMethodValueAsString():PAnsiChar; stdcall;
  end;

{$R *.res}

function  TStringFunctions.GetMethodValueAsString():PAnsiChar; stdcall;
begin
  Result := 'test';
end;


procedure GetImplementation(out instance:IStringFunctions); stdcall; export;
begin
  instance := TStringFunctions.Create;
end;

exports GetImplementation;

begin
end.

I want to using in C# like this

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    [ComVisible(true)]
    [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("240B567B-E619-48E4-8CDA-F6A722F44A71")]
    public interface IStringFunctions
    {
        [MethodImplAttribute(MethodImplOptions.PreserveSig)]
        [return: MarshalAs(UnmanagedType.AnsiBStr)]
        string GetMethodValueAsString();
    }

    class Program
    {
        [DllImport("kernel32.dll", EntryPoint = "LoadLibrary", CallingConvention = CallingConvention.StdCall)]
        static extern int LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpLibFileName);

        [DllImport("kernel32.dll", EntryPoint = "GetProcAddress", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
        static extern IntPtr GetProcAddress(int hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);

        [DllImport("kernel32.dll", EntryPoint = "FreeLibrary", CallingConvention = CallingConvention.StdCall)]
        static extern bool FreeLibrary(int hModule);

        [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
        delegate void GetImplementation([MarshalAs(UnmanagedType.Interface)] out IStringFunctions instance);

        static void Main(string[] args)
        {
            const string dllName = "Project1.dll";
            const string functionName = "GetImplementation";

            int libHandle = LoadLibrary(dllName);
            if (libHandle == 0) throw new Exception(string.Format("Could not load library \"{0}\"", dllName));

            var delphiFunctionAddress = GetProcAddress(libHandle, functionName);
            if (delphiFunctionAddress == IntPtr.Zero) throw new Exception(string.Format("Can't find function \"{0}\" in library \"{1}\"", functionName, dllName));

            GetImplementation getImplementation = (GetImplementation)Marshal.GetDelegateForFunctionPointer(delphiFunctionAddress, typeof(GetImplementation));

            if (getImplementation != null)
            {
                IStringFunctions instance = null;
                getImplementation(out instance);

                if (instance != null)
                {
                    //!!! don't return value !!!!
                    String result = instance.GetMethodValueAsString();
                    Console.WriteLine(result);
                }
            }
            Console.ReadLine();
        }
    }
}

But instance.GetMethodValueAsString method doesn't working. And exit code.

I want to use returning value from dll function(GetMethodValueAsString) in c#.

I don't understand.

Where's my fault?

Thank you so much

2 Answers 2

7
[return: MarshalAs(UnmanagedType.AnsiBStr)]

This is wrong. You are not returning an ANSI encoded string, allocated on the COM heap. You are returning a plain C string, a pointer to null-terminated array of ANSI characters.

Your interface declaration should be:

[MethodImplAttribute(MethodImplOptions.PreserveSig)]
IntPtr GetMethodValueAsString();

Calling the method must be done like this:

IntPtr ptr = instance.GetMethodValueAsString();
string result = Marshal.PtrToStringAnsi(ptr);

Of course, your interface design becomes rather impractical when you need to return a dynamically allocated string. You'd need to export a deallocator too. The clean way to deal with this is to use a BSTR. Like this:

Delphi

IStringFunctions = interface
  ['{240B567B-E619-48E4-8CDA-F6A722F44A71}']
  procedure GetMethodValueAsString(out value: WideString); stdcall;
end;

C#

[MethodImplAttribute(MethodImplOptions.PreserveSig)]
void GetMethodValueAsString([MarshalAs(UnmanagedType.BStr)] out string result);
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks a lot for reply and interest. @David Heffernan Now I understood the logic.I will try.
Hi @David Heffernan How to implement that type TByteArray = array of Byte; in c# ?
That's a byte array. byte[] in C#. The Delphi byte array can't be used for interop.
1

Is that Delphi DLL visible to your C# code for COM Interop? If not, the easiest way to do this would be to attach the dll to the C# class library project using the "Add Existing Item" menu option. Then in the properties window for this dll set "BuildAction" to None, and "Copy to Output Directory" to "Copy always"

Then you could do something like this in the C# code.

[DllImport("Project1.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern string GetMethodValueAsString();

Then wherever you need to call that function you could do

var outputMessage = GetMethodValueAsString();
Console.WriteLine(outputMessage);

2 Comments

There is no COM here. Well, there's an unmanaged function that returns a COM interface. But why would anything need to be registered?
@DavidHeffernan My bad. I was trying to ascertain if the DLL from Delphi was made visible the way a .NET dll would have to be made visible via the ComVisible attribute. But I stand corrected and have edited the answer to reflect what I truly meant.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.