“ASP.NET Web应用程序”和非托管DLL

时间:2014-12-17 11:59:44

标签: c# asp.net .net delphi dll

情况接下来:     我有用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”的来源。

1 个答案:

答案 0 :(得分:2)

function DllTestas(var InputOutput: PAnsiChar): Longint; stdcall;

这个原型注定要失败。它不能合理地用于互操作。问题在于,没有明确谁分配内存以及谁负责整理。

您的C#互操作代码已损坏,似乎只在某些情况下有效。你不能希望这样做。

互操作字符串的最简单方法是使用为此目的而设计的COM字符串BSTR。在C#端,将string属性传递给MarshalAs(UnmanagedType.BStr)。在Delphi端使用WideString

现在,使用BSTR可以轻松地将字符串从非托管被调用方传递给托管调用方。在另一个方向,问题不会出现。您可以使用由调用者分配的空终止字符串。在C#侧作为string传递,并以PAnsiCharPWideChar接收,具体取决于您如何编组字符串。然后,您可能更喜欢对所有字符串使用单一类型。在这种情况下使用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