在C#中调用Delphi COM对象会引发AccessViolationException

在C#中调用Delphi COM对象会引发AccessViolationException,c#,delphi,com,C#,Delphi,Com,我试图向Delphi COM对象发送一个字符串,并期望该对象给出一个答案,但由于某种原因,它抛出了一个AccessViolationException。 这是它抛出的异常,翻译成英语的异常描述是:试图读取或写入受保护内存。这通常表示其他内存已损坏。程序输出(带堆栈顶部跟踪): QManservice已启动。 按任意键停止。 请求接收订单。 收到字符串:S$GET ORDERS OnInverwerkte UITzonerding:System.AccessViolationException:与

我试图向Delphi COM对象发送一个字符串,并期望该对象给出一个答案,但由于某种原因,它抛出了一个AccessViolationException。 这是它抛出的异常,翻译成英语的异常描述是:试图读取或写入受保护内存。这通常表示其他内存已损坏。程序输出(带堆栈顶部跟踪):

QManservice已启动。
按任意键停止。
请求接收订单。
收到字符串:S$GET ORDERS

OnInverwerkte UITzonerding:System.AccessViolationException:与schrijven van Beviligd geheugen的het lezen进行交易。这是一个很好的例子

bij Microsoft.Win32.Win32 Native.SysStringByteLen(IntPtr bstr)
bij系统.StubHelpers.BSTRMarshaler.ConvertToManaged(IntPtr-bstr)
bij QMan_SafanDarley.IWLM_uuz.Send(字符串消息、字符串和应答)
bij WorkLoadManagerServiceDefinitions.QManService.SendStringtoCON(字符串codToSend)位于D:\Michael\C#Projects\QManServiceConsoleApp\OrderEditor\u WCF\QManService.cs:regel 210
D:\Michael\CR Projects\QManServiceConsoleApp\OrderEditor\U WCF\QManService.cs:regel 67中的bij WorkLoadManagerServiceDefinitions.QManService.RequestGetOrders()

这是调用COM的代码

private string SendStringToCOM(string cmdToSend)
    {
        try
        {
            Console.WriteLine($"String received: {cmdToSend}");
            if (WLM == null)
            {
                WLM = new WLM_();
            }
            string answer = string.Empty;
            WLM.Send(cmdToSend, out answer);
            Console.WriteLine("Answer received");
            return answer;
        } catch(Exception e)
        {
            Console.WriteLine(e.Message);
            Console.ReadKey();
            return string.Empty;
        }
    }
这是Delphi中接收调用的代码,它将调用发送给另一个单元,该单元根据接收到的命令执行数据库操作

function TWLM_.Send(const Msg: WideString; out Answer: WideString) : Integer;
begin
    Result := fmProduction.AnalyzeData(Msg, 0);
end;
我应该补充一点,这可以在我的电脑和同事的电脑上使用,但不能在第三台电脑上使用。
关于如何解决这个问题,有什么建议吗?

@J..关于未分配答案参数的评论,在我的情况下,就是这个问题。我让一位同事更改了dll,为参数指定了一个值,这似乎修复了错误,现在它可以正常工作了

function TWLM_.Send(const Msg: WideString; out Answer: WideString) : Integer;
begin
    Result := fmProduction.AnalyzeData(Msg, 0);
end;
这里没有为
答案
参数分配任何内容。这是作为
out
参数传递的,这意味着需要该方法为其分配某些内容。这与函数返回值的行为完全相同

如果不为此变量赋值,则在方法为其分配堆栈空间时,堆栈上将存在任何(未赋值)值。这将不是指向
WideString
的有效指针,但消费代码将尝试封送它,就像封送一样。有时会立即崩溃,有时不会,有时可能会损坏其他数据。无论哪种情况,这都是一个错误

在本机Delphi代码中,可以使用
out
var
参数-在这两种情况下,对于引用类型,调用代码的指针都可以通过接受参数的方法进行读写。如果方法选择不修改该值,则无需修改。然而,对于托管互操作,期望的是
out
参数将始终由该方法分配。C#强制执行,但德尔福没有


在这种情况下,您在C#端传入的空字符串根本不会传入到该方法中-您可能希望它仍然是一个空字符串,不会被Delphi代码修改,但是,通过将参数设置为
out
参数,调用代码期望在该参数中接收一个返回值,并立即用返回的内容覆盖传递的变量(在本例中,是指向无意义的指针)。扩展而言,C端变量在传递给此方法之前可能具有的任何值在Delphi/COM端都不可访问。

Delphi的
WideString
是COM
BSTR的包装。PInvoking to
Send()
时,您的C#code封送
string
s是否为
BSTR
s?你没有在C#侧显示该声明。另外,您的Delphi函数是否使用与C兼容的调用约定?看起来不是。它必须仅使用
cdecl
stdcall
,并在C#端相应地声明。@RemyLebeau代码不封送字符串,但由于它在某些系统上工作,我认为这是不必要的。调用约定也是一样,目前还没有定义,但似乎没有它也能工作。@MichaelD“有时似乎能工作”本身并不是正确代码的证据(通常情况下恰恰相反)。做出假设同样是危险的。你是怎么导入的?@J。。。我通过visualstudio的addreference->COM添加了引用。dll是由我的公司制作的。应答参数将与下一版本的dll一起分配。
out
参数会在输入函数时自动将托管类型(如
WideString
)初始化为有效的默认值。因此,在本例中,
WideString
将被初始化为nil/空字符串。看。@RemyLebeau是的,但这是德尔菲的事,不是吗?我想,初始化编译器管理的
out
参数和返回值是由调用方执行的。在从Delphi代码调用此方法的情况下,是的,编译器将在调用端插入初始化,但方法本身不这样做。。。至少这是我的理解。因为这是由C#wrapper调用的,所以将由该包装器决定如何处理传入的变量。还是我完全误解了什么?@RemyLebeau事实上,我刚刚编译了一个测试,似乎是这样的,即:
Project1.dpr.18:Send(ws);00417B9B8D45FC lea eax,[ebp-$04]00417B9E E85DECFFF call@WStrClr 00417BA3 E8A4FFFFFF call Send
,因此编译器在调用
Send
之前插入
@WStrClr
。但是,如果
Send
被导出到一个COM DLL中,我认为应该由调用方决定如何处理该参数。