Jump to content
StephenM

DLL Load Issue

Recommended Posts

I'm running into a DLL issue that has me perplexed.

 

The DLL is written and compiled in Delphi XE.  It ingests a data structure that identifies a report, which is then retrieved from a repository and displayed.  This all occurs within the DLL.  The only value passed back to the app is an integer indicating success or failure.  The DLL is called from a complex Delphi 5 app and works to this day.

 

Given that Delphi 5 is a bit old (heh), I embarked on a project to compile the app in Delphi 12.  This is done and this is where the problem starts.

 

The function for the DLL is statically defined.  When I run the complex app, I receive an access violation in the DLL when the DLL loads.  Keep in mind I'm not calling the DLL function.  It's failing upon load.

 

To see what's going on, I comment the static definition and go the LoadLibrary/GetProcAddress route.  The LoadLibrary call also fails with access violations.

 

Now here is where it gets interesting.

 

I created a simple Delphi 12 test app that calls the DLL, and the DLL works perfectly.  For fun, I tried both static definitions and dynamic loading.  Both methods work.

 

At this point I wondered if there was some compiler setting that was different between the complex app and the test app.  I performed a side-by-side comparison of the compiler and linker options and made sure they were identical.  I deleted the dcus of the complex app and performed a full build.  The problem still remains.  The DLL does not load with the complex app.

 

I don't have the source for the DLL, so compiling it in the latest version is not an option.

 

Clearly there is something about the complex app that is causing an issue.  I'm at a loss on how to proceed to debug the problem given it occurs upon DLL load.

 

Suggestions are warmly welcome.

Share this post


Link to post

With the little experience I have with loading DLL's to and from newer and older Delphi versions (for a certain project) I think I can say that the exception handling between older Delphi's (5,6,7) and newer Delphi's (roughly those that started using WideString for string), is that they're binary incompatible. Raising in an 'old' DLL and handing in a 'newer' DLL, will typically re-raise an access violation instead. The best advice I could give is how I solved it: keep all exceptions 'within DLL boundaries' if at all possible.

Share this post


Link to post
1 hour ago, StephenM said:

At this point I wondered if there was some compiler setting that was different between the complex app and the test app.  

Maybe related to the change in default Floating-Point exception flags in Delphi 12?

 

https://docwiki.embarcadero.com/RADStudio/Athens/en/What's_New#Disabling_Floating-Point_Exceptions_on_All_Platforms

Edited by Remy Lebeau

Share this post


Link to post
1 minute ago, stijnsanders said:

Raising in an 'old' DLL and handing in a 'newer' DLL, will typically re-raise an access violation instead. The best advice I could give is how I solved it: keep all exceptions 'within DLL boundaries' if at all possible.

Throwing exceptions across the DLL boundary in a vanilla DLL is NEVER a good idea, regardless of compiler version.  Just don't do it.  That can only work in a Package instead of a DLL, but then you can't use a Package across different Delphi versions.

Share this post


Link to post
2 hours ago, Remy Lebeau said:

Throwing exceptions across the DLL boundary in a vanilla DLL is NEVER a good idea, regardless of compiler version.  Just don't do it.  That can only work in a Package instead of a DLL, but then you can't use a Package across different Delphi versions.

Yes! try/except on everything and optionally store the exception's Message (and ClassName) in a string that you make available with a dedicated function xxxGetLastError: PChar;

Share this post


Link to post

Since you wrote that a small D12 app can access the DLL, maybe you can start a new D12 project to get rid of the old

project files (dpr, dproj, res, dsk, ..) and project settings. Then add your forms and units from the old application on it.

Share this post


Link to post

Thanks Remy.  I'll take a look at that article.

 

We are thinking along the same lines, Die.  Once the D12 test app worked, I tried the approach of a clean project.  Sadly it didn't work.  The same access violations occur.

Share this post


Link to post

Maybe one of these LLM answers can give you some clue..

 

1. DLL Entry Points and Initialization
- **Check the DLL's initialization code:**

When the DLL is loaded, it executes its initialization code (like `DllMain` in Windows). Make sure that the DLL does not have any dependencies that might be failing to initialize. If the DLL attempts to access global variables, specific configurations, or other resources that are not set up correctly due to the complex app's environment, it might lead to an access violation.

 

- **Library Conflicts:**

If the DLL relies on other libraries (DLLs or packages), check whether those are being loaded correctly in the context of the complex application.

 

2. Runtime Library Differences
- **Delphi Runtime Compatibility:**

The new Delphi 12 runtime libraries might not be fully compatible with how the complex app was built in Delphi 5. Make sure you’re using runtime libraries in your complex app that are compatible with both the environment it was built in and the DLL.

 

- **Compiler Directives:**

Check if your complex application has specific compiler directives that might affect memory management or other runtime settings.

 

3. Project Settings
- **Memory Management Settings:**

There have been changes in Delphi regarding memory management across versions. It’s possible that your complex Delphi 5 application is still using a legacy memory manager. If your DLL was compiled in Delphi XE, it may expect a different memory management model. Make sure that both applications (the complex one and the DLL) use the same memory manager settings.

 

- **Undefined Symbols:**

If your complex app has issues with undefined symbols or might not be properly linking certain required libraries, this could be causing the access violation when trying to load your DLL.

 

4. Versioning Issues
- **Runtime Package Issues:**

Ensure that the application and the DLL do not have mismatched version dependencies on any runtime packages. Sometimes migrating code from older Delphi versions to newer ones introduces package version discrepancies that can lead to ABI issues.

 

- **Conditional Compilation Flags:**

Make sure you check for any conditional compilation flags that might affect how the DLL behaves when loaded from the complex app versus the simpler test app.

 

5. Diagnostic/debugging strategies
- **Debugging the DLL Load Process:**

Since you cannot modify the DLL itself, consider using tools such as Dependency Walker or Process Monitor to check what resources the DLL is trying to access when loaded by the complex app. These tools can provide clues if it’s trying to access a file or resource that isn't available or accessible.

 

- **Logging:**

If possible, add logging to the DLL load process (if you can do it through some means, like modifying the loader or using external logging). This would help identify where the access violation occurs specifically.

 

6. Running Environment
- **Environment Configuration:**

Check if the environment from where you are launching the complex application is different from where you are launching your test application. Different environment variables or configurations could also impact loading behavior.

 

7. Compatibility Mode
- **Run in Compatibility Mode:**

Consider running the complex application in compatibility mode for an older version of Windows (like XP or 98) to check if there are any odd behavior issues related to the OS.

Share this post


Link to post
18 minutes ago, Die Holländer said:

Maybe one of these LLM answers can give you some clue..

Useless.

Share this post


Link to post

@StephenM:

 

You write "It ingests a data structure that identifies a report". 

 

Please be aware that there are strict limitations about what data types you can safely pass between the exe <--> dll boundary.
Can you describe what this data structure looks like? 

Share this post


Link to post
7 hours ago, Anders Melander said:

Useless.

Maybe you should consider to work with, not against AI, one day :classic_biggrin:

 

Edited by Rollo62

Share this post


Link to post

@A.M. Hoornweg

The structure looks like this:

  TViewerParams = record
    ArchiveFileName: WideString;
    zoomFactor: integer;
    windowState: TWindowState;
    serverURL: WideString;
    isModal: boolean;
    caption: WideString;
  end;

 

Given that the D12 simple app works and the complex one does not, I'm led to the conclusion there is some unit in the complex app that is causing the issue.  I've embarked upon the following:

1) Replicate the D12 simple app into a new test app.

2) Start adding units from the complex into the test app.  After each unit of work, test to see if the DLL loads and works.

3) Rinse and repeat until the error shows up.  Or not.  Either way I'll get to a solution of some sort.

 

Stay tuned.  912 units to sort through.

 

Share this post


Link to post
16 minutes ago, StephenM said:

@A.M. Hoornweg, thanks.  All three projects, D5, D12 simple, D12 complex, all have the same field alignment.

Please verify if "sizeof (tViewerParams)" gives the same result in all compilers.

 

Also, is the calling convention to the dll function declared identically and clearly in all projects (e.g. "stdcall") ?

Edited by A.M. Hoornweg

Share this post


Link to post

I've discovered the issue and can easily replicate it in a sample project.  And it's bizarre.

 

A quick recap.  The simple D12 project loading the DLL works.  It's composed of a main form and a unit with the DLL function declarations.  No issue calling the DLL methods.

 

I drop a second VCL form into the project.  To this form I add a TDBChart from the 'TeeChart Std' components.  Compile, run, and after a while the access violations in my DLL are reported.

 

If I remove the form with the TDBChart from the project, the simple app works once again.

 

Any ideas?

 

 

Share this post


Link to post

Can you look into the TDBChart Unit code?

I see in my version D12.2 that it is not installed as a default component.. 

Edited by Die Holländer

Share this post


Link to post
10 minutes ago, Die Holländer said:

I see in my version D12.2 that it is not installed as a default component.. 

You have to select a checkbox during the install process (Technology - Teechart standard). Unfortunately Embarcadero has licenced the product without the source code.

 

I did not noticed problems with the Teechart.

Share this post


Link to post
9 hours ago, StephenM said:

I've discovered the issue and can easily replicate it in a sample project.  And it's bizarre.

 

A quick recap.  The simple D12 project loading the DLL works.  It's composed of a main form and a unit with the DLL function declarations.  No issue calling the DLL methods.

 

I drop a second VCL form into the project.  To this form I add a TDBChart from the 'TeeChart Std' components.  Compile, run, and after a while the access violations in my DLL are reported.

 

If I remove the form with the TDBChart from the project, the simple app works once again.

 

Any ideas?

 

 

 

In that case it may have to do with the changed FPU flags behavior in Delphi 12. 

 

Try doing this right before calling your DLL, does it make a difference?


    SetFPUMask([exdenormalized, exunderflow, exprecision]);

 

 

This setting makes the FPU behave like in previous Delphi versions. In other words, a FP division by 0 will generate an exception instead of continuing the code path with a NAN result.

 

 

 

 

 

 

 

 

 

Share this post


Link to post

Its weird, but maybe it is something that is declared into the source of the Chart component.

In my projects i managed over the years to get all the 3th party components without source

out of the applications, apart from some of the native Windows.

 

I would try to separate the form(s) with the Chart component from the application and

continue the conversion of the application to the newer Delphi version.

You can always try to compile the chart form as a separate application and create

a communication with your main program to display the charts, like using the

Windows Message system or creating a DLL. 

Share this post


Link to post
6 hours ago, Die Holländer said:

or creating a DLL. 

There's a joke there.  :classic_biggrin:

 

I agree.  The best way forward, now that I've isolated the cause, is to place the charting code to the side.  Most users of the app export data into Excel for charting purposes.

 

Thanks everyone for all your suggestions and help.  You are all the best.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×