情况接下来: 我有用Delphi编写的“.dll”文件。它获取字符串参数并将其返回。如果我在“Windows”的“C#”应用程序中使用这个“.dll” - 它工作正常,但我需要在“asp.net web应用程序”中使用它,而在Web应用程序中它会生成下一个例外:
iisexpress.exe has triggered a breakpoint.
Unhandled exception at 0x77A9E753 (ntdll.dll) in iisexpress.exe: 0xC0000374: A heap has been corrupted (parameters: 0x77AD4270).
“asp.net web应用程序”中的其他非托管“.dll”文件工作正常。所以我使用ShareMem和borlandmm.dll进行简单的模拟“.dll”:
library Testas;
uses
ShareMem, SysUtils, Classes;
{$R *.res}
function DllTestas(var InputOutput: PAnsiChar): Longint; stdcall;
begin
Result := StrToIntDef(InputOutput, 0);
InputOutput := 'aaaa';
end;
exports
DllTestas;
begin
end.
简单的“asp.net web应用程序”:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Runtime.InteropServices;
namespace WebApplication1
{
public partial class Default : System.Web.UI.Page
{
[DllImport("Testas.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern int DllTestas([MarshalAsAttribute(UnmanagedType.AnsiBStr)] ref string InputOutput);
protected void Page_Load(object sender, EventArgs e)
{
string InOut = "123";
int result = DllTestas(ref InOut);
Response.Write("Testas.dll:" + "<br />" + result.ToString() + " " + InOut + "<br />" + "<br />");
}
}
}
属性 - 选中“本机代码”,“平台目标”为“x86”。
所以这个模拟代码产生相同的结果。
问题:错误在哪里以及如何解决?
建议将“.dll”重写为“C#” - 请不要提供。这是我的第一个想法,但是制作这个“.dll”的人会找到1000个不可能的原因,因为它是他的“面包”,并且他并没有如此灵感地学习新语言。
模拟“.dll”是用“Delphi 2005”和“Delphi XE5”编译的 - 结果相同。 “asp.net web应用程序” - “VS 2013 Ultimate”。我有“.dll”的来源。
答案 0 :(得分:2)
function DllTestas(var InputOutput: PAnsiChar): Longint; stdcall;
这个原型注定要失败。它不能合理地用于互操作。问题在于,没有明确谁分配内存以及谁负责整理。
您的C#互操作代码已损坏,似乎只在某些情况下有效。你不能希望这样做。
互操作字符串的最简单方法是使用为此目的而设计的COM字符串BSTR
。在C#端,将string
属性传递给MarshalAs(UnmanagedType.BStr)
。在Delphi端使用WideString
。
现在,使用BSTR
可以轻松地将字符串从非托管被调用方传递给托管调用方。在另一个方向,问题不会出现。您可以使用由调用者分配的空终止字符串。在C#侧作为string
传递,并以PAnsiChar
或PWideChar
接收,具体取决于您如何编组字符串。然后,您可能更喜欢对所有字符串使用单一类型。在这种情况下使用BSTR
。
一句警告。在进行互操作时,请勿使用WideString
作为函数返回类型:Why can a WideString not be used as a function return value for interop?
一些例子:
<强>的Delphi 强>
library Project1;
procedure Foo(InputVal: PWideChar; out OutputVal: WideString); stdcall;
begin
OutputVal := 'Foo: ' + InputVal;
end;
procedure Bar(InputVal: WideString; out OutputVal: WideString); stdcall;
begin
OutputVal := 'Bar: ' + InputVal;
end;
exports
Foo, Bar;
begin
end.
<强> C#强>
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
const string dllname = @"Project1.dll";
[DllImport(dllname, CharSet = CharSet.Unicode)]
static extern void Foo(
string InputVal,
[MarshalAs(UnmanagedType.BStr)]
out string OutputVal
);
[DllImport(dllname)]
static extern void Bar(
[MarshalAs(UnmanagedType.BStr)]
string InputVal,
[MarshalAs(UnmanagedType.BStr)]
out string OutputVal
);
static void Main(string[] args)
{
string OutputVal;
Foo("Hello", out OutputVal);
Console.WriteLine(OutputVal);
Bar("World", out OutputVal);
Console.WriteLine(OutputVal);
}
}
}
<强>输出强>
Foo: Hello Bar: World