1

Situation is next: I have “.dll” file written in Delphi. It get string parameter and return it back. If I use this “.dll” in “C#” application for “Windows” - it works fine, but I need to use it in “asp.net web application” and in web application it generate next exceptions:

iisexpress.exe has triggered a breakpoint.

Unhandled exception at 0x77A9E753 (ntdll.dll) in iisexpress.exe: 0xC0000374: A heap has been corrupted (parameters: 0x77AD4270).

Other unmanaged “.dll” files in “asp.net web application” works fine. So I make simple mock “.dll”, using ShareMem and 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.

And simple “asp.net web application”:

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 />");
        }
    }
}

Properties - “Native Code” is checked and “Platform target” is “x86”.

So this mock code generating the same result.

Question: Where is mistake and how to resolve it?

Suggestion to rewrite “.dll” to “C#” - please, not offer. It was my first idea, but person who make this “.dll” will find 1000 reason why it's impossible, because it's his “bread” and he is not so inspired form idea to learn new language.

Mock “.dll” was compiled with “Delphi 2005” and “Delphi XE5” - result the same. “asp.net web application” - “VS 2013 Ultimate”. I have source of “.dll”.

Arik
  • 180
  • 2
  • 9
  • 1
    First check http://stackoverflow.com/questions/4163364/calling-a-delphi-dll-from-a-c-sharp-net-application and http://forums.asp.net/t/1591002.aspx?Delphi+5+DLL+to+use+in+asp+net+bin+Tlbimp+exe+ – Amit Dec 17 '14 at 12:04
  • 1
    perhaps the dlls only works well in a STA model. Give it a try: http://weblog.west-wind.com/posts/2012/Sep/18/Creating-STA-COM-compatible-ASPNET-Applications – deostroll Dec 17 '14 at 12:06
  • 1
    [MarshalAs(UnmanagedType.AnsiBStr)] - according to the docs (http://docwiki.embarcadero.com/Libraries/XE6/en/System.PAnsiChar and http://msdn.microsoft.com/en-us/library/s9ts558h%28v=vs.110%29.aspx), there must be `[MarshalAs(UnmanagedType.LPStr)]`. Besides, I suspect, that `ref string` should be replaced with `StringBuilder`. – Dennis Dec 17 '14 at 12:17
  • @Amit Thank you, but all this for “Windows application”, not for “web application”. In “windows application” “.dll” works fine. For real in “web application” when on exception I press “Continue” “.dll” show result on “web page”, but when I put this code in IIS it don't show exceptions, but brake server. – Arik Dec 17 '14 at 12:43
  • @Dennis Thank you, but now I get next message - “Source Not Available. Source information is missing from the debug information for this module. You can view disassemble in the Disassembly window. To always view disassembly for missing source files, change the setting in the Option dialog. – Arik Dec 17 '14 at 13:03
  • 1
    Your code is broken everywhere. It just appears to work in a windows app. You can't fix it without changing the dll. The sooner you understand that the sooner you can make progress. – David Heffernan Dec 17 '14 at 13:08
  • @deostroll Thank you for your answer. I get more simple decision of problem, so when I will have a time, I will take a more close look on STA. – Arik Dec 18 '14 at 06:22

1 Answers1

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

This prototype is doomed to failure. It cannot reasonably be used for interop. The problem is that that there is no clarity over who allocates memory and who is responsible for tidying up.

Your C# interop code is broken and just appears to work in certain scenarios. You cannot hope to proceed this way.

The simplest way to interop strings is to use the COM string, BSTR which was designed for this purpose. On the C# side pass it as string with the MarshalAs(UnmanagedType.BStr) attribute. On the Delphi side use WideString.

Now, using BSTR makes it easy to pass strings from unmanaged callee to managed caller. In the other direction the issue doesn't arise. You can use a null terminated string, allocated by the caller. Pass as string on the C# side and receive as either PAnsiChar or PWideChar depending on how you marshalled the string. Then again you may prefer to use a single type for all strings. In which case use BSTR.

One word of warning. Don't use WideString as a function return type when doing interop: Why can a WideString not be used as a function return value for interop?

Some examples:

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);
        }
    }
}

Output

Foo: Hello
Bar: World
Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • So I changed code:

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

    “C#”:
    [DllImport("Testas.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)] public static extern int DllTestas([MarshalAs(UnmanagedType.BStr)] ref string InputOutput);

    and now message is next:
    Source Not Available. Source information is missing from the debug information for this module. You can view disassemble in the Disassembly window. To always view disassembly for missing source files, change the setting in the Option dialog.
    – Arik Dec 17 '14 at 13:35
  • You aren't going to be able to debug the Delphi code from Visual Studio. If you want to debug the Delphi code, you need the Delphi debugger. – David Heffernan Dec 17 '14 at 13:37
  • I put you sample, and it any way generate - “Source Not Available”. But if I put your sample on IIS it works fine. Interesting thing is, that I have other “.dll” (without source) and it using “borlandmm.dll” and it not generating any exceptions or messages in “VS”. So I thought that problem is in “Delphi” code. Any way, code works in IIS without problem so I marked your message as answer. – Arik Dec 18 '14 at 06:18