从C#.NET应用程序调用Delphi DLL [英] Calling a Delphi DLL from a C# .NET application

查看:149
本文介绍了从C#.NET应用程序调用Delphi DLL的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编辑:我已经发布了一个更好的实现,在下面。我在这里离开这里,所以答案是有道理的。



我在Delphi中编写了一个DLL的正确方法,从C#调用它,传递和返回字符串。很多信息不完整或不正确。经过多次尝试和错误,我找到了解决方案。



这是使用Delphi 2007和VS 2010编译的。我怀疑它可以在其他版本中正常工作。 p>

这是Delphi代码。请记住在项目中包含版本信息。

 库DelphiLibrary; 

使用SysUtils;

//使用Delphi 2007编译

//注意:如果您的项目没有包含版本信息,您可能会
//收到错误在编译C#应用程序时,ResolveManifestFiles任务意外失败
//。

{$ R * .res}

//示例函数接受一个输入整数和输入字符串,并返回
// inputInt + 1和inputString + ''+ IntToStr(outputInt)作为输出
//参数。如果成功,返回结果为nil(null),否则为
//异常消息字符串。


//注意:我在下面发布了一个更好的版本。你应该使用它。

函数DelphiFunction(inputInt:integer; inputString:PAnsiChar;
out outputInt:integer; out outputString:PAnsiChar)
:PAnsiChar;标准出口;
var s:string;
begin
outputInt:= 0;
outputString:= nil;
try
outputInt:= inputInt + 1;
s:= inputString +''+ IntToStr(outputInt);
outputString:= PAnsiChar(s);
结果:= nil;
除了
对e:exception do结果:= PAnsiChar(e.Message);
结束
结束

//我会以为在函数声明结尾处有export
//(上面)已经足够导出函数,但是我无法得到它
//在没有这行的情况下工作。
导出DelphiFunction;

begin
end。

这是C#代码:

 使用系统; 
使用System.Runtime.InteropServices;

命名空间CsharpApp
{
类程序
{
//我将DelphiLibrary.dll添加到我的项目中(不是参考文献,而是
//添加现有文件)。在dll的属性中,我将BuildAction
//设置为None,将Copy to Output Directory复制到Always always。
//确保您的Delphi DLL包含版本信息。

[DllImport(DelphiLibrary.dll,
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern
string DelphiFunction(int inputInt,string inputString,
out int outputInt,out string outputString);

static void Main(string [] args)
{
int inputInt = 1;
string inputString =这是一个测试;
int outputInt;
string outputString;


//注意:我在下面发布了一个更好的版本。你应该使用它。


Console.WriteLine(inputInt = {0},intputString = \{1} \,
inputInt,inputString);
var errorString = DelphiFunction(inputInt,inputString,
out outputInt,out outputString);
if(errorString!= null)
Console.WriteLine(Error = \{0} \,errorString);
else
Console.WriteLine(outputInt = {0},outputString = \{1} \,
outputInt,outputString);
Console.Write(按回车:);
Console.ReadLine();
}
}
}

我希望这些信息有助于某人否则不必像我一样拉头发。

解决方案

根据对我的帖子的回复,我创建了一个新的示例,它为返回的字符串使用字符串缓冲区,而不是返回PAnsiChars。



Delphi DLL源代码:

 库DelphiLibrary; 

使用SysUtils;

//使用Delphi 2007编译

//注意:如果您的项目没有包含版本信息,您可能会
//收到错误在编译C#应用程序时,ResolveManifestFiles任务意外失败
//。

{$ R * .res}

//保留字符串的注释。我原来写这个,所以
//输出字符串只是一个PAnsiChar。但是有几个人指出,
//因为Delphi字符串是引用计数的,这是一个坏主意,因为
//字符串的内存在使用之前可能会被覆盖。
//
//因此,我重写了该示例,以便您必须传递
//结果字符串的缓冲区。我看到一些如何做到这一点的例子,他们在哪里
//也返回了实际的字符串长度。这不是必需的,因为
// string是空值终止的,实际上这些示例本身从未使用
//返回的字符串长度。


//示例函数接受一个输入整数和输入字符串,并返回
// inputInt + 1和inputString ++ IntToStr(outputInt)。如果成功,
//返回结果为true,否则errorMsgBuffer包含
//异常消息字符串。
函数DelphiFunction(inputInt:integer;
inputString:PAnsiChar;
输出Int:integer;
outputStringBufferSize:integer;
var outputStuffBuffer:PAnsiChar;
errorMsgBufferSize :integer;
var errorMsgBuffer:PAnsiChar)
:WordBool;标准出口;
var s:string;
begin
outputInt:= 0;
try
outputInt:= inputInt + 1;
s:= inputString +''+ IntToStr(outputInt);
StrLCopy(outputStringBuffer,PAnsiChar(s),outputStringBufferSize-1);
errorMsgBuffer [0]:=#0;
结果:= true;
除了
对于e:exception do
begin
StrLCopy(errorMsgBuffer,PAnsiChar(e.Message),errorMsgBufferSize-1);
结果:= false;
结束
结束
结束

//我会以为在函数声明结尾处有export
//(上面)已经足够导出函数,但是我无法得到它
//在没有这行的情况下工作。
导出DelphiFunction;

begin
end。

C#代码:

 使用系统; 
使用System.Runtime.InteropServices;

命名空间CsharpApp
{
类程序
{
//我将DelphiLibrary.dll添加到我的项目中(不是参考文献,而是
//添加现有文件)。在dll的属性中,我将BuildAction
//设置为None,将Copy to Output Directory复制到Always always。
//确保您的Delphi DLL包含版本信息。

[DllImport(DelphiLibrary.dll,
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern bool
DelphiFunction(int inputInt,string inputString,
out int outputInt,
int outputStringBufferSize,ref string outputStringBuffer,
int errorMsgBufferSize,ref string errorMsgBuffer);

static void Main(string [] args)
{
int inputInt = 1;
string inputString =这是一个测试;
int outputInt;
const int stringBufferSize = 1024;
var outputStringBuffer = new String('\x00',stringBufferSize);
var errorMsgBuffer = new String('\x00',stringBufferSize);

if(!DelphiFunction(inputInt,inputString,
out outputInt,
stringBufferSize,ref outputStringBuffer,
stringBufferSize,ref errorMsgBuffer))
Console.WriteLine (Error = \{0} \,errorMsgBuffer);
else
Console.WriteLine(outputInt = {0},outputString = \{1} \,
outputInt,outputStringBuffer);

Console.Write(按回车:);
Console.ReadLine();
}
}
}

这里还有一个额外的类显示如何动态加载DLL(对于长行而言):

  using System; 
使用System.Runtime.InteropServices;

命名空间CsharpApp
{
静态类DynamicLinking
{
[DllImport(kernel32.dll,EntryPoint =LoadLibrary)]
static extern int LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpLibFileName);

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

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

[UnmanagedFunctionPointer(CallingConvention.StdCall,CharSet = CharSet.Ansi)]
委托bool DelphiFunction(int inputInt,string inputString,
out int outputInt,
int outputStringBufferSize ,ref string outputStringBuffer,
int errorMsgBufferSize,ref string errorMsgBuffer);

public static void CallDelphiFunction(int inputInt,string inputString,
out int outputInt,out string outputString)
{
const string dllName =DelphiLib.dll;
const string functionName =DelphiFunction;

int libHandle = LoadLibrary(dllName);
if(libHandle == 0)
throw new异常(string.Format(无法加载库\{0} \,dllName));
try
{
var delphiFunctionAddress = GetProcAddress(libHandle,functionName);
if(delphiFunctionAddress == IntPtr.Zero)
throw new异常(string.Format(can not find function \{0} \in library \{1} \ ,functionName,dllName));

var delphiFunction =(DelphiFunction)Marshal.GetDelegateForFunctionPointer(delphiFunctionAddress,typeof(DelphiFunction));

const int stringBufferSize = 1024;
var outputStringBuffer = new String('\x00',stringBufferSize);
var errorMsgBuffer = new String('\x00',stringBufferSize);

if(!delphiFunction(inputInt,inputString,out outputInt,
stringBufferSize,ref outputStringBuffer,
stringBufferSize,ref errorMsgBuffer))
throw new Exception(errorMsgBuffer);

outputString = outputStringBuffer;
}
finally
{
FreeLibrary(libHandle);
}
}
}
}

Dan


EDIT: I've posted a better implementation of this, below. I left this here so the responses would make sense.

I've done numerous searches for the correct method for writing a DLL in Delphi, and being able to call it from C#, passing and returning strings. A lot of the information was incomplete or incorrect. After much trial and error, I found the solution.

This was compiled using Delphi 2007 and VS 2010. I suspect it will work fine in other versions as well.

Here's the Delphi code. Remember to include version information in the project.

library DelphiLibrary;

uses SysUtils;

// Compiled using Delphi 2007.

// NOTE: If your project doesn't have version information included, you may
// receive the error "The "ResolveManifestFiles" task failed unexpectedly"
// when compiling the C# application.

{$R *.res}

// Example function takes an input integer and input string, and returns
// inputInt + 1, and inputString + ' ' + IntToStr(outputInt) as output
// parameters. If successful, the return result is nil (null), otherwise it is
// the exception message string.


// NOTE: I've posted a better version of this below. You should use that instead.

function DelphiFunction(inputInt : integer; inputString : PAnsiChar;
                        out outputInt : integer; out outputString : PAnsiChar)
                        : PAnsiChar; stdcall; export;
var s : string;
begin
  outputInt := 0;
  outputString := nil;
  try
    outputInt := inputInt + 1;
    s := inputString + ' ' + IntToStr(outputInt);
    outputString := PAnsiChar(s);
    Result := nil;
  except
    on e : exception do Result := PAnsiChar(e.Message);
  end;
end;

// I would have thought having "export" at the end of the function declartion
// (above) would have been enough to export the function, but I couldn't get it
// to work without this line also.
exports DelphiFunction;

begin
end.

Here's the C# code:

using System;
using System.Runtime.InteropServices;

namespace CsharpApp
{
    class Program
    {
        // I added DelphiLibrary.dll to my project (NOT in References, but 
        // "Add existing file"). In Properties for the dll, I set "BuildAction" 
        // to None, and "Copy to Output Directory" to "Copy always".
        // Make sure your Delphi dll has version information included.

        [DllImport("DelphiLibrary.dll", 
                   CallingConvention = CallingConvention.StdCall, 
                   CharSet = CharSet.Ansi)]
        public static extern 
            string DelphiFunction(int inputInt, string inputString,
                                  out int outputInt, out string outputString);

        static void Main(string[] args)
        {
            int inputInt = 1;
            string inputString = "This is a test";
            int outputInt;
            string outputString;


// NOTE: I've posted a better version of this below. You should use that instead.


            Console.WriteLine("inputInt = {0}, intputString = \"{1}\"",
                              inputInt, inputString);
            var errorString = DelphiFunction(inputInt, inputString,
                                             out outputInt, out outputString);
            if (errorString != null)
                Console.WriteLine("Error = \"{0}\"", errorString);
            else
                Console.WriteLine("outputInt = {0}, outputString = \"{1}\"",
                                  outputInt, outputString);
            Console.Write("Press Enter:");
            Console.ReadLine();
        }
    }
}

I hope this information helps someone else to not have to pull their hair out as much as I did.

解决方案

Based on responses to my post, I have created a new example that uses string buffers for the returned strings, instead of just returning PAnsiChars.

Delphi DLL source:

library DelphiLibrary;

uses SysUtils;

// Compiled using Delphi 2007.

// NOTE: If your project doesn't have version information included, you may
// receive the error "The "ResolveManifestFiles" task failed unexpectedly"
// when compiling the C# application.

{$R *.res}

// A note on returing strings. I had originally written this so that the
// output string was just a PAnsiChar. But several people pointed out that
// since Delphi strings are reference-counted, this was a bad idea since the
// memory for the string could get overwritten before it was used.
//
// Because of this, I re-wrote the example so that you have to pass a buffer for
// the result strings. I saw some examples of how to do this, where they
// returned the actual string length also. This isn't necessary, because the
// string is null-terminated, and in fact the examples themselves never used the
// returned string length.


// Example function takes an input integer and input string, and returns
// inputInt + 1, and inputString + ' ' + IntToStr(outputInt). If successful,
// the return result is true, otherwise errorMsgBuffer contains the the
// exception message string.
function DelphiFunction(inputInt : integer;
                        inputString : PAnsiChar;
                        out outputInt : integer;
                        outputStringBufferSize : integer;
                        var outputStringBuffer : PAnsiChar;
                        errorMsgBufferSize : integer;
                        var errorMsgBuffer : PAnsiChar)
                        : WordBool; stdcall; export;
var s : string;
begin
  outputInt := 0;
  try
    outputInt := inputInt + 1;
    s := inputString + ' ' + IntToStr(outputInt);
    StrLCopy(outputStringBuffer, PAnsiChar(s), outputStringBufferSize-1);
    errorMsgBuffer[0] := #0;
    Result := true;
  except
    on e : exception do
    begin
      StrLCopy(errorMsgBuffer, PAnsiChar(e.Message), errorMsgBufferSize-1);
      Result := false;
    end;
  end;
end;

// I would have thought having "export" at the end of the function declartion
// (above) would have been enough to export the function, but I couldn't get it
// to work without this line also.
exports DelphiFunction;

begin
end.

C# Code:

using System;
using System.Runtime.InteropServices;

namespace CsharpApp
{
    class Program
    {
        // I added DelphiLibrary.dll to my project (NOT in References, but 
        // "Add existing file"). In Properties for the dll, I set "BuildAction" 
        // to None, and "Copy to Output Directory" to "Copy always".
        // Make sure your Delphi dll has version information included.

        [DllImport("DelphiLibrary.dll", 
                   CallingConvention = CallingConvention.StdCall, 
                   CharSet = CharSet.Ansi)]
        public static extern bool 
            DelphiFunction(int inputInt, string inputString,
                           out int outputInt,
                           int outputStringBufferSize, ref string outputStringBuffer,
                           int errorMsgBufferSize, ref string errorMsgBuffer);

        static void Main(string[] args)
        {
            int inputInt = 1;
            string inputString = "This is a test";
            int outputInt;
            const int stringBufferSize = 1024;
            var outputStringBuffer = new String('\x00', stringBufferSize);
            var errorMsgBuffer = new String('\x00', stringBufferSize);

            if (!DelphiFunction(inputInt, inputString, 
                                out outputInt,
                                stringBufferSize, ref outputStringBuffer,
                                stringBufferSize, ref errorMsgBuffer))
                Console.WriteLine("Error = \"{0}\"", errorMsgBuffer);
            else
                Console.WriteLine("outputInt = {0}, outputString = \"{1}\"",
                                  outputInt, outputStringBuffer);

            Console.Write("Press Enter:");
            Console.ReadLine();
        }
    }
}

And here's an additional class that shows how to load the DLL dynamically (sorry for the long lines):

using System;
using System.Runtime.InteropServices;

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

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

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

        [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
        delegate bool DelphiFunction(int inputInt, string inputString,
                                     out int outputInt,
                                     int outputStringBufferSize, ref string outputStringBuffer,
                                     int errorMsgBufferSize, ref string errorMsgBuffer);

        public static void CallDelphiFunction(int inputInt, string inputString,
                                              out int outputInt, out string outputString)
        {
            const string dllName = "DelphiLib.dll";
            const string functionName = "DelphiFunction";

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

                var delphiFunction = (DelphiFunction)Marshal.GetDelegateForFunctionPointer(delphiFunctionAddress, typeof(DelphiFunction));

                const int stringBufferSize = 1024;
                var outputStringBuffer = new String('\x00', stringBufferSize);
                var errorMsgBuffer = new String('\x00', stringBufferSize);

                if (!delphiFunction(inputInt, inputString, out outputInt,
                                    stringBufferSize, ref outputStringBuffer,
                                    stringBufferSize, ref errorMsgBuffer))
                    throw new Exception(errorMsgBuffer);

                outputString = outputStringBuffer;
            }
            finally
            {
                FreeLibrary(libHandle);
            }
        }
    }
}

-Dan

这篇关于从C#.NET应用程序调用Delphi DLL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆