进一步调查表明,由于一些不正确的配置文件,断言没有使用Delphi主机触发。一旦解决了这个问题,Delphi主机就像C#主机一样死机。
请注意,所有这些都与XE2 64位版本相关联。我们已经注意到断言会破坏Delphi 64位调试器,而他们不使用32位平台。
替换AssertErrorProc
并在那里进行一些日志记录可以解决这种情况,对于Delphi调试器来说都是我们的C#和Delphi主机。
两个主机都可以在断言处抛出异常。 的异常被正常的except块捕获。
使用Delphi XE3无法重现问题(感谢@David帮忙解决这个问题),因此当前状态是这与Delphi XE2中的(错误)异常/断言处理有关,特别是在64位上平台。
我有一个Delphi DLL,它可以从自托管的C#Web服务中调用。出于调试目的,也可以从Delphi可执行文件中调用此DLL。
可以从Delphi和C#主机成功使用DLL。
今天我遇到了在初始化DLL时执行的代码中触发断言的情况,并发现在Delphi进程托管时成功阻止断言离开DLL,但是没有捕获并导致主机当这是一个C#进程时就会死掉。
Delphi DLL有自己的DllProc过程,可以从dpr手动调用,因为Delphi RTL“劫持”了入口点以允许单元初始化。有关详细信息,请参阅http://docwiki.embarcadero.com/VCL/XE/en/System.DLLProc。
Delphi DLL dpr代码:
begin
DllProc := MyDllMain;
MyDllMain(DLL_PROCESS_ATTACH);
end.
自定义dll主程序只是确保在首次加载DLL时初始化某些结构,并在最后一个“加载器”离开时完成。
procedure MyDllMain(Reason: Integer);
begin
// ...
// DLL_PROCESS_ATTACH:
begin
if _RefCount < 1 then
InitializeDLL;
Inc(_RefCount);
end;
// ...
end;
InitializeDLL过程受try except块保护,专门用于防止异常转义DLL。
procedure InitializeDLL;
begin
try
// Some code triggering an Assert(False, 'Whatever');
except
on E: Exception do
TLogger.LogException('InitializeDLL');
// Don't raise through. Exceptions should not leave DLL.
end;
end;
Delphi主机手动调用Delphi DLL的LoadLibrary,检索指向它所需函数的指针并使用它们调用DLL。
procedure InternalSetup;
begin
FLibrary := LoadLibrary(CLibraryPath);
GetResource := GetProcAddress(FLibrary, 'GetResource');
PostResource := GetProcAddress(FLibrary, 'PostResource');
PutResource := GetProcAddress(FLibrary, 'PutResource');
DeleteResource := GetProcAddress(FLibrary, 'DeleteResource');
end;
呼叫: 结果:= GetResource(INVALID_URI,{aQueryParams =}'',{out} ResponseBody);
C#主机包含以下代码来调用DLL
[DllImport("EAConfigurationEngine.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
// Delphi:
// function GetResource(aURI: string; aQueryParams: string; out aResponseBody: WideString): THTTPStatusCode; stdcall; export;
private static extern int GetResource(
string uri,
string queryParams,
[MarshalAs(UnmanagedType.BStr)] out string responseBody);
如上所述,当DLL的主机是Delphi可执行文件时,会触发except块。但是,当从C#主机调用Delphi DLL时,断言触发,
导致这种情况发生的原因是什么?如何预防?
答案 0 :(得分:2)
断言是一种特殊的异常,需要一些额外的脚手架和编译器的支持。
如果您查看所涉及的例程(在SysUtils单元中),则会描述许多假设,例如:
{ This code is based on the following assumptions: }
{ - Our direct caller (AssertErrorHandler) has an EBP frame }
{ - ErrorStack points to where the return address would be if the }
{ user program had called System.@RaiseExcept directly }
此评论只是众多讨论 ASSERT()机制所涉及的假设之一。
无论是否涉及Delphi编译器中的断言实现的这一方面,我认为这些假设在C#主机进程中运行时无效。如果问题背后是这些假设,那么以“正常”方式提出异常可以避免问题,而不会强迫您更改除了引发异常本身的方式之外的其他任何内容。
尝试用简单的调用替换 ASSERT(FALSE,'Whatever')直接引发 EAssertionFailed 异常(即不涉及编译器脚手架和假设 ASSERT()调用)。
您仍然可以使代码受条件编译影响,以达到与使用启用断言编译器选项(编译器选项“C”)相同的效果:
{$ifopt C+} // only if compiling with assertions enabled
raise EAssertionFailed.Create('Whatever');
{$endif}