Ситуация следующая: у меня есть файл ".dll", написанный в Delphi. Он получает строковый параметр и возвращает его обратно. Если я использую этот ".dll" в приложении "С#" для "Windows" - он отлично работает, но мне нужно использовать его в "веб-приложении asp.net", а в веб-приложении он генерирует следующие исключения:
iisexpress.exe has triggered a breakpoint.
Unhandled exception at 0x77A9E753 (ntdll.dll) in iisexpress.exe: 0xC0000374: A heap has been corrupted (parameters: 0x77AD4270).
Другие неуправляемые файлы ".dll" в "веб-приложении asp.net" отлично работают. Поэтому я делаю простой mock ".dll", используя ShareMem и borlandmm.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":
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" на "С#" - пожалуйста, не предлагайте. Это была моя первая идея, но человек, создавший эту ".dll", найдет 1000 причин, почему это невозможно, потому что это его "хлеб", и он не настолько вдохновлен идеей идеи изучать новый язык.
Mock ".dll" был скомпилирован с "Delphi 2005" и "Delphi XE5" - результат тот же. "веб-приложение asp.net" - "VS 2013 Ultimate". У меня есть источник.dll.
function DllTestas(var InputOutput: PAnsiChar): Longint; stdcall;
Этот прототип обречен на провал. Он не может быть разумно использован для взаимодействия. Проблема в том, что нет ясности относительно того, кто распределяет память и кто несет ответственность за уборку.
Ваш код взаимодействия с С# сломан и просто работает в определенных сценариях. Вы не можете надеяться на продолжение этого пути.
Самый простой способ взаимодействия строк - использовать COM-строку BSTR
которая была разработана для этой цели. На стороне С# передать его как string
с MarshalAs(UnmanagedType.BStr)
. На стороне Delphi используйте WideString
.
Теперь, используя BSTR
легко передать строки от неуправляемого вызываемого абонента управляемому абоненту. В другом направлении вопрос не возникает. Вы можете использовать строку с нулевым завершением, выделенную вызывающим. Передайте string
на стороне С# и получите как PAnsiChar
и PWideChar
зависимости от того, как вы упорядочили строку. Затем снова вы можете использовать один тип для всех строк. В этом случае используйте BSTR
.
Одно слово предупреждения. Не используйте WideString
в качестве возвращаемого типа функции при выполнении взаимодействия: почему WideString не может использоваться как возвращаемое значение функции для взаимодействия?
Некоторые примеры:
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.
С#
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