Here's my problem.
I have an application that loads a DLL. I have the source and control both the main application and the DLL. I would like the DLL to use the same help viewer than the one used in the main application (as registered by System.HelpIntfs.RegisterViewer).
Now, I believe that if both the DLL and the main application would "link with runtime packages", that would be pretty much done automatically (IIRC). For the sake of this question, lets assume this option is off the table (politics...)
I first looked into the possibility of manually assigning the HelpManager: IHelpManager
variable in the System.HelpIntfs unit. But the variable doesn't seem accessible (it's in the implementation section and no procedure seems to allow to set it).
Then I looked at TApplication. Assigning manually the FHelpSystem : IHelpSystem
would fix my problem, but that variable is only accessible through a read only property.
I thought about passing the main application's ICustomHelpViewer to the DLL and call RegisterViewer with it, but then I started being worried it would get "corrupt", and that it would probably get "shutdown" when the DLL unload.
Then I thought about creating another ICustomHelpViewer that I would pass to my DLL that would just map all the pertinent calls to the application's ICustomHelpViewer. I figured I should forward all call except ShutDown
, SoftShutDown
and NotifyId
. Am I right?
Is there any better options?
(Halfway through writing my question, I figured this option. Thought I'd still post the question and see if better options are suggested.)
So, the option I went for is actually setting the TApplication.FHelpSystem. Being an old-timer, it didn't occur to me right away that private variables aren't really that private anymore.
In my main application, I call System.HelpIntfs.GetHelpSystem to get a reference to the main application's help system that I pass to my DLL.
In my DLL, I set TApplication's FHelpSystem through RTTI. I use the following function:
procedure SetRttiField(AInstance : TObject; const AFieldName : string; AValue : TValue);
var
vRTTI : TRttiContext;
vRTTIType : TRttiType;
vRttiField : TRttiField;
begin
vRTTI := TRttiContext.create;
vRttiType := vRTTI.GetType(AInstance.ClassInfo);
if not Assigned(vRttiType) then
EXIT;
vRttiField := vRTTIType.GetField(AFieldName);
if not Assigned(vRttiField) then
EXIT;
vRttiField.SetValue(AInstance, AValue);
end;
And call it like this:
SetRttiField(Application, 'FHelpSystem', TValue.From(aHelpSystem))