Claim that they did not get caught except for the block during initialization of the library

Update

Further research showed that claims did not start using the Delphi host due to invalid configuration files. Once this was resolved, the Delphi host died as abruptly as the C # host.

Please note that all this is connected with 64-bit builds of XE2. We have already noticed that statements kill the 64-bit Delphi debugger, although they do not use the 32-bit platform.

Replacing AssertErrorProc and just executing some protocols there eliminates the situation for both the Delphi debugger and our C # and Delphi hosts.

Both hosts also survive by throwing exceptions in the place of statements. Exceptions are thrown with the exception of blocks that are in place.

The problem cannot be reproduced using Delphi XE3 (thanks to @David for helping with this), so the current status is that this is due to (error) handling of exceptions / statements in Delphi XE2, especially on a 64-bit platform.


I have a Delphi DLL that is designed to be called from a C # self-service web service. For debugging purposes, this DLL can also be called from the Delphi executable.

The DLL can be used successfully with both Delphi hosts and C #.

Today I came across a situation that caused a statement in the code executed during the initialization of the DLL, and found that this statement was unable to leave the DLL when it was placed in the Delphi process, but was not caught and forces the host die when it is a C # process.

Delphi dll

The Delphi DLL has its own DllProc procedure, which is called manually from dpr, because Delphi RTL "captures" the entry point to allow element initialization. See http://docwiki.embarcadero.com/VCL/XE/en/System.DLLProc for details.

Delphi dpr dll code:

 begin DllProc := MyDllMain; MyDllMain(DLL_PROCESS_ATTACH); end. 

The main user dll routine simply ensures that some structures are initialized when the DLL loads and exits when the last "bootloader" leaves.

 procedure MyDllMain(Reason: Integer); begin // ... // DLL_PROCESS_ATTACH: begin if _RefCount < 1 then InitializeDLL; Inc(_RefCount); end; // ... end; 

The InitializeDLL procedure is protected by a try except block that is specifically designed to prevent exceptions from being excluded from the 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; 

Delphic host

The Delphi host manually calls LoadLibrary for the Delphi DLL, extracts pointers to the functions it needs, and calls the DLL using them.

 procedure InternalSetup; begin FLibrary := LoadLibrary(CLibraryPath); GetResource := GetProcAddress(FLibrary, 'GetResource'); PostResource := GetProcAddress(FLibrary, 'PostResource'); PutResource := GetProcAddress(FLibrary, 'PutResource'); DeleteResource := GetProcAddress(FLibrary, 'DeleteResource'); end; 

Call: Result: = GetResource (INVALID_URI, {aQueryParams =} '', {out} ResponseBody);

C # host

C # host contains the following code to call the 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); 

Question

As stated above, an exception block is thrown when the host for the DLL is a Delphi executable. However, when a call is made to the Delphi DLL from the C # host, assertion triggers, an unblocked block is not reached (the message is not logged, instead the uninitialized log logs an unhandled exception from the assertion), and the C # process dies when the "vshost.exe dialog stops working "

What causes this and how can I prevent it?

+6
source share
1 answer

Statements are a special flavor of exception, which requires additional scaffolding and support from the compiler.

If you look at the routines (in the SysUtils module), there are many assumptions, for example, for example:

 { 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 } 

This comment is just one of many discussions of assumptions related to the ASSERT () mechanism.

Whether this is an aspect of the implementation of statements in the Delphi compiler or not, it seems to me that these assumptions are not valid when launched in a C # host process. If exactly these prerequisites are behind the problem, then creating an exception in the β€œnormal” way can avoid the problem without forcing you to change anything, except how you yourself create the exception.

Try replacing ASSERT (FALSE, "Whatever") with a simple call to directly throw an EAssertionFailed exception (that is, not include the compiler scaffolding and the assumption that the ASSERT () call is being called .

You can still subject the code to conditional compilation to achieve the same effect as when using the compiler option Enable statements (compiler option "C"):

 {$ifopt C+} // only if compiling with assertions enabled raise EAssertionFailed.Create('Whatever'); {$endif} 
+2
source

Source: https://habr.com/ru/post/957513/


All Articles