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”.

7

1 Answer 1

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
3
  • So I changed code:<br/><br/> function DllTestas(var InputOutput: WideString): Longint; stdcall;<br/><br/> “C#”:<br/> [DllImport("Testas.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)] public static extern int DllTestas([MarshalAs(UnmanagedType.BStr)] ref string InputOutput);<br/><br/> and now message is next: <br/> 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
    Commented Dec 17, 2014 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. Commented Dec 17, 2014 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
    Commented Dec 18, 2014 at 6:18

Not the answer you're looking for? Browse other questions tagged or ask your own question.