Overview
A while ago I wrote an article (The Open Tools API using C++ Builder) on building an Open Tools API (OTA) plug-in using C++ Builder. In the article I mentioned that there was a bug that prevented a DLL from directly referencing the DesignIDE.bpl
global variables BorlandIDEServices
and SplashScreenServices
.
I raises this as a bug to Embarcadero (https://quality.embarcadero.com/browse/RSP-16481) that prevented a splash screen from being used in a DLL and David Millington has responded with a workaround which I’ll describe here and which works for eariler IDEs as well.
The fix
The fix require the addition of a line of code in the project CPP file and amendments to other files so I’ll describe the changes to each file in the following subsections:
CPPOTATemplateXE102.cpp
There is a single change to the main project file where we add an additional line to link in the DesignIDE.bpi
package file. This is the workaround provided by Embarcadero while they seek to fix the Tokyo compiler but this fix works with earlier compilers as well.
#include <vcl .h> #include <windows .h> #pragma hdrstop #pragma argsused //: @note This pragma line fixes the missing external references to the DesignIDE.BPL package. #pragma link "DesignIDE.bpi" int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved) { return 1; }
CPPOTATemplateMacros.h
Now we’ve bound the DesignIDE.bpi
file to the DLL we no longer need the macro that binds the local IDE services reference to the BorlandIDEServices
passed to the wizard initialization method.
#ifndef CPPOTATemplateMacrosH #define CPPOTATemplateMacrosH #include <toolsapi .hpp> //: @note Not required any more //: #ifdef DLL //: #define BorlandIDEServices LocalIDEServices //: extern _di_IBorlandIDEServices LocalIDEServices; //: #endif #define QUERY_INTERFACE(T, iid, obj) \ if ((iid) == __uuidof(T)) { \ *(obj) = static_cast(this); \ static_cast(*(obj))->AddRef(); \ return S_OK; \ } #endif
CPPOTATemplatePkgDLLInit.cpp
In the main DLL code we can remove the variable declaration and assignment code which assigned the BorlandIDEServices
reference pass to the wizard initialization function as they are not needed any more.
#pragma hdrstop #include <cppotatemplatepkgdllinit .h> #include <cppotatemplatemainwizard .h> #pragma package(smart_init) #ifndef DLL // For Packages... // We need to declare for a package a Register procedure. // The NAMESPACE MUST BE the same name as unit Register is declared in and be lower case except // for first letter. namespace Cppotatemplatepkgdllinit { void __fastcall PACKAGE Register() { RegisterPackageWizard(new TCPPOTATemplateWizard("TCPPOTATemplateWizard")); } } #else // For DLLs... // We need to declare a local variable to accept the BorlandIDEServices reference from the // Wizard creation method below //: @note Not Required //: _di_IBorlandIDEServices LocalIDEServices; // We also need to delcare the wizard entry point that is called by the IDE on loading a DLL extern "C" bool __stdcall __declspec(dllexport) INITWIZARD0001( const _di_IBorlandIDEServices Service, TWizardRegisterProc RegisterWizard, TWizardTerminateProc&) { //: @note Not Required //: LocalIDEServices = Service; // get reference to the BorlandIDEServices RegisterWizard(new TCPPOTATemplateWizard("TCPPOTATemplateWizard")); return true; } #endif
CPPOTATemplateSplashScreen.h
Next we need to remove or comment out the #ifndef
that surrounds the declaration of the function that installs the splash screen.
#ifndef CPPOTATemplateSplashScreenH #define CPPOTATemplateSplashScreenH #endif //: @note Not required //: #ifndef DLL void __fastcall AddSplashScreen(); //: #endif
CPPOTATemplateSplashScreen.cpp
Next we need to remove or comment out the #ifndef
that surrounds the function that installs the splash screen.
#pragma hdrstop #include "CPPOTATemplateSplashScreen.h" #include "windows.h"; #include "CPPOTATemplateConstants.h" #include "SysInit.hpp" #include <toolsapi .hpp> #include "SysUtils.hpp" #include "Forms.hpp" #include "CPPOTATemplateFunctions.h" #pragma package(smart_init) //: @note IFNDEF not required anymore //: #ifndef DLL void __fastcall AddSplashScreen() { int iMajor; int iMinor; int iBugFix; int iBuild; HBITMAP bmSplashScreen; BuildNumber(iMajor, iMinor, iBugFix, iBuild); bmSplashScreen = LoadBitmap(HInstance, L"CPPOTATemplateSplashScreenBitMap24x24"); _di_IOTASplashScreenServices SSServices; if (SplashScreenServices->Supports(SSServices)) { String strRev = strRevision[iBugFix]; SSServices->AddPluginBitmap( Format(strSplashScreenName, ARRAYOFCONST((iMajor, iMinor, strRev, Application->Title))), bmSplashScreen, False, Format(strSplashScreenBuild, ARRAYOFCONST((iMajor, iMinor, iBugFix, iBuild))) ); Sleep(5000); //: @debug Here to pause splash screen to check icon } } //: #endif
CPPOTATemplateMainWizard.cpp
The final change to make is to comment out or remove the #ifndef
that surrounds the call to install the splash screen which is contained within the main wizard class’s constructor.
__fastcall TCPPOTATemplateWizard::TCPPOTATemplateWizard(String strObjectName) : TDGHNotifierObject(strObjectName) { //: @note Not required now //: #ifndef DLL AddSplashScreen(); //: #endif FAboutBoxPlugin = AddAboutBoxPlugin(); FIDENotifier = AddIDENotifier(); FAppOptions = new TCPPOTATemplateOptions(); FIDEOptions = AddOptionsFrameToIDE(FAppOptions); FTimerCounter = 0; FAutoSaveTimer = new TTimer(NULL); FAutoSaveTimer->Interval = 1000; FAutoSaveTimer->OnTimer = AutoSaveTimerEvent; FAutoSaveTimer->Enabled = true; }
Hopefully the above changes are straight forward and allow you to either update my existing code (although you can download the changes from the C++ OTA Template page) or update your own code.