BCB5 mix RADStudio2007
-
Hallo,
ich habe ein größeres Programm im MDI Stil unter BCB5. Hier werden alle Module als DLL geladen (LoadLib...). Jetzt wollte ich so langsam mal auf das RAD-Studio wechseln. Dabei habe ich überlegt, das Hauptprogramm MidiMain im RAD-Studio neu zu schreiben und die "alten"- Module dann nachzuladen. Ich bekomme einen Fehler "Fehler beim Lesen von MemSql->Lines.Strings: Formular kann nicht erstellt werden. Zur Zeit sind keine MDI-Formulare aktiv". Irgend eine Idee? Geht das überhaupt: Mdi-Main aus Rad-Studio und Mdi-Child aus BCB5?//--------------------------------------------------------------------------- __fastcall TfrmSqlAssistent::TfrmSqlAssistent(TComponent* Owner) : TForm(Owner) { fstIni->FileName = cs_APPDATASUBPATH + Name +cs_INIAPPENDIX ; fstSqlAssiMain->Active = true; MemSql->Lines->Clear(); // hier passierts AdcMain->Close(); AdcMain->ConnectionString = m_BHSBasics->GlobalConString; AdcMain->Open(); }
Aufgerufen wird die form wie folgt:
( die Step-Meldungen laufen alle durch)extern "C" { stdDecl int StartSqlAssistent(int ParameterCount, void** Parameters); } int StartSqlAssistent(int ParameterCount, void** Parameters) { MessageBox(NULL,"Step 1", "TEST", MB_OK); if( ApplicationInterface ) ApplicationInterface->PassCheckpoint( TCheckpoint("Enter Methode", "sqlassiStartUnt.cpp", "Step 1", "StartSqlAssistent", "OK") ); MessageBox(NULL,"Step 2", "TEST", MB_OK); if (!m_BHSBasics) { m_BHSBasics = new TBHSBasics(NULL,ApplicationInterface); m_BHSBasics->Open(Parameters,ParameterCount); } else m_BHSBasics->Open(Parameters,ParameterCount,false); MessageBox(NULL,"Step 3", "TEST", MB_OK); if( ApplicationInterface ) ApplicationInterface->PassCheckpoint( TCheckpoint("Enter Methode", "sqlassiStartUnt.cpp", "Step 1", "StartSqlAssistent", "OK") ); MessageBox(NULL,"Step 4", "TEST", MB_OK); frmSqlAssistent = new TfrmSqlAssistent(Application); return 1; }
-
Hans-Martin schrieb:
Geht das überhaupt: Mdi-Main aus Rad-Studio und Mdi-Child aus BCB5?
Nein.
(Der Vollständigkeit halber: wenn du dich auf eine WinAPI-basierte Schnittstelle beschränkst, geht es schon. Aber das dürfte für dich nicht von Interesse sein.)
-
Sch....
welches WinApi-Interface meints Du? Wg. Mdi-System auf API-Basis?
Also vertragen sich die MDI Systeme der VCL(BCB5) und der neuen vom Rad Studio (überhaupt)-nicht?
Das ist ziemlich schlecht. Ich habe noch (160000 Zeilen VCL-BCB5) viel Zeug zum umbauen und das Hauptproblem ist, dass "developper express" die Grids von TdxDBGrid auf TcxDBGrid grundlegend geändert hat und ich mir einen sanften Übergang von BCB5 auf RAD durch verschiedene DLL-Typen erhoft habe. Da gibt es Masken mit bis zu 9 Tabsheets und auf jedem ist so ein TdxDBGrid.
-
Hans-Martin schrieb:
Also vertragen sich die MDI Systeme der VCL(BCB5) und der neuen vom Rad Studio (überhaupt)-nicht?
Mit MDI hat das wenig zu tun. Du kannst einfach nicht verschiedene Versionen der VCL miteinander benutzen, d.h. du kannst nicht der DLL einen Zeiger auf ein VCL-Objekt aus der Anwendung übergeben. Das gesamte Interface muß sich auf POD-Typen beschränken.
Mittelfristig solltest du natürlich deine vollständige Anwendung migrieren. Wenn das tatsächlich ein so großes Problem ist, kannst du natürlich trotzdem versuchen, über besagte Win32-Interfaces ein C++Builder 5-Formular als MDI-Fenster in einer C++Builder 2007-Anwendung anzuzeigen. Dazu mußt du allerdings einige Änderungen vornehmen:
- Zunächst mußt du die DLL-Schnittstellen komplett auf C-Interfaces umstellen, d.h., du darfst weder verwalteten Datentypen (z.B. Strings) übergeben noch Exceptions über eine Modulgrenze werfen. Für das Exception-Firewalling bieten sich die in Windows bereits existierenden OLE-Mechanismen an:
safecall.hpp:
#ifndef _SAFECALL_HPP #define _SAFECALL_HPP #include <unknwn.h> #include <System.hpp> #include <SysUtils.hpp> #include <exception> void raiseSafecallException (HRESULT hresult); inline void SafecallCheck (HRESULT hresult) { if (hresult != 0) raiseSafecallException (hresult); } HRESULT setSafecallExceptionInfo (WideString desc, const GUID& iid = GUID_NULL, unsigned helpContext = 0, WideString helpFile = WideString ()); HRESULT setSafecallExceptionInfo (const std::exception& e); HRESULT setSafecallExceptionInfo (Sysutils::Exception& e); HRESULT setSafecallExceptionInfo (void); #define SAFECALL_CATCH \ catch (const std::exception& e) \ { return setSafecallExceptionInfo (e); } \ catch (Sysutils::Exception& e) \ { return setSafecallExceptionInfo (e); } \ catch (...) \ { return setSafecallExceptionInfo (); } #endif // _SAFECALL_HPP
safecall.cpp:
#include <vcl.h> #pragma hdrstop #include "safecall.hpp" #include <ComObj.hpp> void raiseSafecallException (HRESULT hresult) { if (hresult == 0) return; DelphiInterface <IErrorInfo> ei; if (GetErrorInfo (0, &ei) != S_OK) OleError (hresult); else { unsigned long helpContext = 0; WideString desc, helpFile, source; GUID iid; ei->GetDescription (&desc); ei->GetGUID (&iid); ei->GetHelpContext (&helpContext); ei->GetHelpFile (&helpFile); ei->GetSource (&source); throw EOleException (desc, hresult, source, helpFile, helpContext); } } HRESULT setSafecallExceptionInfo (WideString desc, const GUID& iid = GUID_NULL, unsigned helpContext = 0, WideString helpFile = WideString ()) { HRESULT result = (SEVERITY_ERROR << 31) | (FACILITY_ITF << 16) | 1; DelphiInterface <ICreateErrorInfo> cei; DelphiInterface <IErrorInfo> ei; if (CreateErrorInfo (&cei) == S_OK) { cei->SetDescription (desc.c_bstr ()); cei->SetHelpContext (helpContext); cei->SetHelpFile (helpFile.c_bstr ()); cei->SetSource (WideString (Application->ExeName).c_bstr ()); cei->SetGUID (iid); if (cei->QueryInterface <IErrorInfo> (&ei) == S_OK) { result |= 2; SetErrorInfo (0, ei); } } return result; } HRESULT setSafecallExceptionInfo (const std::exception& e) { return setSafecallExceptionInfo (e.what ()); } HRESULT setSafecallExceptionInfo (Sysutils::Exception& e) { EOleException* oleExc = dynamic_cast <EOleException*> (&e); if (oleExc) return setSafecallExceptionInfo (oleExc->Message, GUID_NULL, oleExc->HelpContext, oleExc->HelpFile); else return setSafecallExceptionInfo (e.Message, GUID_NULL, e.HelpContext); } HRESULT setSafecallExceptionInfo (void) { return setSafecallExceptionInfo (L"Unknown exception occurred"); }
Benutzung so:
// in DLL extern "C" __declspec (dllexport) HRESULT __stdcall DllInterfaceFunc (int foo) { try { // ... return S_OK; } SAFECALL_CATCH } // in Anwendung void callDllInterfaceFunc (void) { SafecallCheck (DllInterfaceFunc (42)); }
- Damit ein MDI-Child-Form der DLL sich einem anderen Fenster als Application->ClientWindow unterordnet, mußt du die DLL zunächst ähnlich wie eine gewöhnliche VCL-Anwendung initialisieren. Obgleich wir es nicht benutzen werden, benötigt die DLL ein Hauptformular - erstelle einfach ein leeres.
// MdiDll.cpp #include <vcl.h> #include <windows.h> #pragma hdrstop //--------------------------------------------------------------------------- USEFORM ("FakeMainForm.cpp", FakeMainForm); //--------------------------------------------------------------------------- #pragma argsused int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved) { if (reason == DLL_PROCESS_ATTACH) { Application->Initialize (); Application->ShowMainForm = false; Application->CreateForm (__classid (TFakeMainForm), &FakeMainForm); } return 1; }
- Deine Anwendung, die die MDI-Clients hostet, muß folgende Funktion exportieren, damit die DLLs an das gewünschte MDI-Client-Handle kommen können:
extern "C" __declspec (dllexport) HRESULT __stdcall GetMainFormClientHandle (HWND& result) { try { result = Application->MainForm->ClientHandle; return S_OK; } SAFECALL_CATCH }
- Nun mußt du für sämtliche MDI-Formulare die Funktion TCustomForm::CreateWindowHandle() überschreiben und entsprechend anpassen. Da du vermutlich einige hast, bietet es sich an, ein gemeinsames Basis-Formular einzuführen, von dem du alle MDI-Childs ableitest:
unit ExtMdiChild; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs; type TExtMdiChildForm = class(TForm) private protected function GetExternClientHandle: HWND; virtual; procedure CreateWindowHandle(const Params: TCreateParams); override; public property ExternClientHandle: HWND read GetExternClientHandle; end; implementation uses Consts; {$R *.dfm} type TGetMainFormClientHandle = function: HWND; safecall; { TExtMdiChildForm } function TExtMdiChildForm.GetExternClientHandle: HWND; var GetClientHandle: TGetMainFormClientHandle; begin GetClientHandle := TGetMainFormClientHandle ( GetProcAddress (GetModuleHandle (0), 'GetMainFormClientHandle')); if Assigned (GetClientHandle) then Result := GetClientHandle else Result := 0; end; procedure TExtMdiChildForm.CreateWindowHandle(const Params: TCreateParams); var CreateStruct: TMDICreateStruct; begin if (FormStyle = fsMDIChild) and not (csDesigning in ComponentState) then begin if ExternClientHandle = 0 then raise EInvalidOperation.Create (SNoMDIForm); with CreateStruct do begin szClass := Params.WinClassName; szTitle := Params.Caption; hOwner := THandle (HInstance); X := Params.X; Y := Params.Y; cX := Params.Width; cY := Params.Height; style := Params.Style; lParam := THandle(Params.Param); end; WindowHandle := SendMessage (ExternClientHandle, WM_MDICREATE, 0, LPARAM (@CreateStruct)); Include (FFormState, fsCreatedMDIChild); end else inherited; end; end.
- Schließlich muß deine DLL noch eine Möglichkeit bieten, ein MDI-Formular zu erstellen:
extern "C" __declspec (dllexport) HRESULT __stdcall CreateAndShowMdiForm (HWND& result) { try { TForm* newForm = new TSomeMDIChild (Application); newForm->Show (); result = newForm->Handle; return S_OK; } SAFECALL_CATCH }
Natürlich ist das nicht perfekt. Es gibt einige kleinere Probleme, die sich nicht durch das Überschreiben einer virtuellen Funktion beheben lassen (dazu suche in Forms.pas nach Application.MainForm.ClientHandle). Aber es sollte für den Notfall ausreichend sein.
- Zunächst mußt du die DLL-Schnittstellen komplett auf C-Interfaces umstellen, d.h., du darfst weder verwalteten Datentypen (z.B. Strings) übergeben noch Exceptions über eine Modulgrenze werfen. Für das Exception-Firewalling bieten sich die in Windows bereits existierenden OLE-Mechanismen an:
-
erst mal vielen Dank.
Ich werde es mir in der nächsten Woche mal genauer anschauen.
Es gibt 35 DLLs, z.T. mit mehreren Formularen.
Dazu kommt noch dass ich eine eigene Plattform für Exceptions u.v.a.m. habe. Das muss ich mir noch mal ansehen, - ist ja höchstens 7-8 Jahre her.
Au Weia, ich habe eine üble Ahnung