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.



  • 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


Anmelden zum Antworten