在 C# 中调用 Delphi COM 对象抛出 AccessViolationException
Calling Delphi COM object in C# throws AccessViolationException
我正在尝试将一个字符串发送到 Delphi COM 对象并期待该对象的回答,但由于某种原因它抛出了 AccessViolationException。
这是它抛出的异常,翻译成英文的异常描述是:Attempted to read or write protected memory。这通常表明其他内存已损坏。程序输出(带有栈顶跟踪):
QManservice started.
Press any key to stop.
Request to get orders received.
String received: S$GET ORDERS,
Onnverwerkte uitzondering: System.AccessViolationException: Poging tot het lezen of schrijven van beveiligd geheugen. Dit duidt er vaak op dat ander geheugen is beschadigd.
bij Microsoft.Win32.Win32Native.SysStringByteLen(IntPtr bstr)
bij System.StubHelpers.BSTRMarshaler.ConvertToManaged(IntPtr bstr)
bij QMan_SafanDarley.IWLM_.Send(String Msg, String& Answer)
bij WorkLoadManagerServiceDefinitions.QManService.SendStringtoCON(String codToSend) in D:\Michael\C# Projects\QManServiceConsoleApp\OrderEditor_WCF\QManService.cs:regel 210
bij WorkLoadManagerServiceDefinitions.QManService.RequestGetOrders() in D:\Michael\CR Projects\QManServiceConsoleApp\OrderEditor_WCF\QManService.cs:regel 67
...
这是调用 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;
此处您没有为 Answer
参数分配任何内容。这是作为 out
参数传递的,这意味着该方法需要为其分配一些内容。这与函数 return 值的行为完全相同。
如果您不为该变量分配任何值,那么在方法为其分配堆栈 space 时,堆栈中恰好存在任何(未分配的)值。这将不是指向 WideString
的有效指针,但使用代码会尝试将其封送,就好像它是一样。有时这会立即崩溃,有时不会,有时它可能只是破坏其他数据。无论哪种情况,都是错误。
在本机 Delphi 代码中,您可能习惯使用 out
和 var
参数 behaving quite similarly - 在这两种情况下,对于引用类型,调用代码的指针都可用通过接受参数的方法进行读写。如果该方法选择不修改它不需要的值。但是,对于托管互操作,期望 out
参数将始终由该方法分配。 C# 强制执行此操作,但 Delphi 不执行此操作。
在这种情况下,您在 C# 端传入的空字符串根本不会传入该方法 - 您可能希望它应该保持为空字符串,未被 Delphi 代码修改,但是通过使参数成为 out
参数,调用代码期望在该参数中接收 return 值,并立即用 returned 的任何内容覆盖传递的变量(在这种情况下,指针胡说八道)。 C# 端的变量在传递给此方法之前可能具有的任何值,通过扩展,将无法在 Delphi/COM 端访问。
我正在尝试将一个字符串发送到 Delphi COM 对象并期待该对象的回答,但由于某种原因它抛出了 AccessViolationException。 这是它抛出的异常,翻译成英文的异常描述是:Attempted to read or write protected memory。这通常表明其他内存已损坏。程序输出(带有栈顶跟踪):
QManservice started.
Press any key to stop.
Request to get orders received.
String received: S$GET ORDERS,Onnverwerkte uitzondering: System.AccessViolationException: Poging tot het lezen of schrijven van beveiligd geheugen. Dit duidt er vaak op dat ander geheugen is beschadigd.
bij Microsoft.Win32.Win32Native.SysStringByteLen(IntPtr bstr)
bij System.StubHelpers.BSTRMarshaler.ConvertToManaged(IntPtr bstr)
bij QMan_SafanDarley.IWLM_.Send(String Msg, String& Answer)
bij WorkLoadManagerServiceDefinitions.QManService.SendStringtoCON(String codToSend) in D:\Michael\C# Projects\QManServiceConsoleApp\OrderEditor_WCF\QManService.cs:regel 210
bij WorkLoadManagerServiceDefinitions.QManService.RequestGetOrders() in D:\Michael\CR Projects\QManServiceConsoleApp\OrderEditor_WCF\QManService.cs:regel 67 ...
这是调用 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;
此处您没有为 Answer
参数分配任何内容。这是作为 out
参数传递的,这意味着该方法需要为其分配一些内容。这与函数 return 值的行为完全相同。
如果您不为该变量分配任何值,那么在方法为其分配堆栈 space 时,堆栈中恰好存在任何(未分配的)值。这将不是指向 WideString
的有效指针,但使用代码会尝试将其封送,就好像它是一样。有时这会立即崩溃,有时不会,有时它可能只是破坏其他数据。无论哪种情况,都是错误。
在本机 Delphi 代码中,您可能习惯使用 out
和 var
参数 behaving quite similarly - 在这两种情况下,对于引用类型,调用代码的指针都可用通过接受参数的方法进行读写。如果该方法选择不修改它不需要的值。但是,对于托管互操作,期望 out
参数将始终由该方法分配。 C# 强制执行此操作,但 Delphi 不执行此操作。
在这种情况下,您在 C# 端传入的空字符串根本不会传入该方法 - 您可能希望它应该保持为空字符串,未被 Delphi 代码修改,但是通过使参数成为 out
参数,调用代码期望在该参数中接收 return 值,并立即用 returned 的任何内容覆盖传递的变量(在这种情况下,指针胡说八道)。 C# 端的变量在传递给此方法之前可能具有的任何值,通过扩展,将无法在 Delphi/COM 端访问。