Wykrycie zamknięcia okna z DLL wbudowanego w aplikację główną

0

Mam AdvOfficePager - rozbudowana wersja TPageControl z kolekcji TMS, w którym w aplikacji głównej tworzę na żądanie DLL zakładkę, następnie przesyłam jej uchwyt z powrotem do DLL i tam tworzę formę, a następnie przypisuję jako parenta uchwyt zakładki. Wygląda to następująco:

DLL:

var
  TabH: HWND;
  OrderList: TfrmMobileOrderList;
begin
    // ap to interfejs aplikacji głównej
    ap.AddTabPage(tabh, 'Oczekujące na wizytę'); // Tworzy zakładkę w głównym programie o nazwie 'Oczekujące na wizytę' i zwraca jej uchwyt w zmiennej tabh
    OrderList := TfrmMobileOrderList.CreateP(TabH); // Tworzy okno
    OrderList.show;
end;

Okno frmMobileOrderList w DLL

private
  FParent: THandle;
end;
...

constructor TfrmMobileOrderList.CreateP(Parent: THandle);
begin
  FParent := Parent;

  inherited Create(nil);
end;

procedure TfrmMobileOrderList.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.WndParent := FParent;
  Params.Style := WS_CHILD or WS_CLIPSIBLINGS or WS_BORDER;
  Align := alClient;
end;

W aplikacji głównej mam listę obiektów w której trzymam listę zakładek, oraz rozróżniam, które należą do aplikacji głównej, a które do bibliotek DLL:

type
  TPages = class(TAdvOfficePage) // Dziedziczy po komponencie zakładki
  private
    Name: string;
    Form: TForm; // Tylko dla apliakacji głównej, aby przy zamykaniu zakładki, zwolnić również okno
    DLL: boolean; // Czy pochodzi z DLL
  end;

// Tutaj trzymam obiekty zakładek
FPageList: TObjectList<TPages>;

// A tak wygląda funkcja, wywoływana po kliknięciu w X na zakładce TAdvOfficePager
procedure TFormManager.CloseForm(APage: TAdvOfficePage);
var
  i: Integer;
begin
  for i := 0 to FPageList.Count -1 do
    if (FPageList.Items[i].Caption = APage.Caption) then
    begin
      // Okna zwalniamy tylko dla zakładek z aplikacji głównej
      // Dla DLL trzeba inaczej (JAK??)
      if not FPageList.Items[i].DLL then
      begin
        FPageList.Items[i].Form.Free;
        FPageList.Items[i].Form := nil;
      end;
      FPageList.Delete(i);
      break;
    end;

  if FPageList.Count = 0 then
    FNotebook.ActivePage := 'Wallper';
end;

Funkcja dodająca zakładki w programie głównym:

function TfrmMain.AddTabPage(out ATabH: HWND; ACaption: PAnsiChar): HRESULT;
begin
  ATabH := FormManager.ShowFormDLL(ATabH, ACaption);
end;

function TFormManager.ShowFormDLL(ATabH: HWND; ACaption: PAnsiChar): HWND;
var
  I: Integer;
  LPageExists: boolean;
begin
  Result := 0;

  LPageExists := False;
  for I := 0 to FPageList.Count -1 do
  begin
    if FPageList.Items[i].Caption = ACaption then
    begin
      // Ustaw aktywna zakladke
      FPageControl.ActivePage := FPageControl.PageByCaption(ACaption);
      LPageExists := True;
      Result := FPageControl.ActivePage.Handle;
      break;
    end;
  end;

  if not LPageExists then
  begin
    FPageList.Add(TPages.Create(nil)); // CreateParented(ATabH)); nie pomaga
    FPageList.Last.ParentWindow := ATabH; // nie pomaga
    FPageList.Last.Caption := ACaption;
    FPageList.Last.DLL := True;

    FPageControl.AddAdvPage(FPageList.Last);
    Result := FPageList.Last.Handle;
    FPageControl.ActivePage := FPageList.Last;
    FNotebook.ActivePage := 'Forms';
  end;
end;

Pytanie: Jak po kliknięciu X na zakładce AdvOfficePage w funkcji CloseForm zwolnić również formę z DLL?

    ap.AddTabPage(tabh, 'Oczekujące na wizytę'); // Tworzy zakładkę w głównym programie o nazwie 'Oczekujące na wizytę' i zwraca jej uchwyt w zmiennej tabh
    OrderList := TfrmMobileOrderList.CreateP(TabH); // Tworzy okno
    OrderList.show;

Jak widać w powyższym kodzie, uchwyt do okna tworzony jest później niż zakładka, bo gdybym miał uchwyt okna od razu, to mógłbym wysłać komunikat WM_CLOSE i chyba byłoby po problemie. Z tego co się orientuję, to muszę tworzyć okno po utworzeniu zakładki, bo muszę najpierw mieć uchwyt zakładki, żeby przypisać go do tworzonego okna.

0

A może by tak dokować okno (okno systemu Windows) formy w oknie (oknie systemu Windows) zakładki. Wtedy można by w żądaniu utworzenia zakładki od razu przekazać Handle już utworzonego okna, które zostało bu zdokowane w zakładce. Chyba że ten Parent jest Ci niezbędnie potrzebny. Robię tak w aplikacji (nie Delphi) dokując w niej okna z dll-ki (Delphi).

0

Nie do końca rozumiem co masz na myśli pisząc dokować te okna, bo wydaje mi się, że właśnie to robię... Dokuję okno DLL w zakładce okna Aplikacji rodzica, przypisując oknu z DLL parenta uchwytu zakładki aplikacji rodzica. Zmieniłem kod tak:

FOrderList := TfrmMobileOrderList.Create(Application );
ap.AddTabPage(tabhh, FOrderList.Handle, 'Oczekujące na wizytę');
Windows.SetParent(FOrderList.Handle, TabhH);
FOrderList.show;

Najpierw tworzę okno i do funkcji tworzenia zakładki w aplikacji głównej, przekazuję uchwyt okna. Następnie, przy zamknięciu zakładki wysyłam komunikat WM_CLOSE do uchwytu okna, co powoduje jego zwolnienie. Nie wiem czy to jest dobre rozwiązanie i czy tak się robi?

Pytanie drugie (znane z poprzedniego wątku):
W aplikacji głównej tworzę przycisk na żądanie DLL i przekazuję z DLL procedurę parametrem

DLL:

type TNotifyProc = procedure(Sender: TObject);

function AddNotifyBadgeButton(AButtonName, AHint, ABadgeValue: PAnsiChar; ABitmapHandle: THandle; AOnClickProc: TNotifyProc): HRESULT; stdcall;

Aplikacja główna:

function TfrmMain.AddNotifyBadgeButton(AButtonName, AHint, ABadgeValue: PAnsiChar; ABitmapHandle: THandle; AOnClickProc: TNotifyProc): HRESULT;
var
  LNotifyButton: TAdvBadgeGlowButton;
  LMethod: TMethod;
  Bitmap: TBitmap;
begin
  result := S_OK;

  LNotifyButton := pnlNotifications.FindComponent(AButtonName) as TAdvBadgeGlowButton;
  if Assigned(LNotifyButton) then
  begin
    LNotifyButton.Hint := AHint;
    LNotifyButton.Badge := ABadgeValue;
    exit;
  end;


  LMethod.Data := nil;
  LMethod.Code := @AOnClickProc;
  LNotifyButton := TAdvBadgeGlowButton.Create(pnlNotifications);
  LNotifyButton.Parent := pnlNotifications;
  LNotifyButton.Name := AButtonName;
  LNotifyButton.BorderStyle := bsNone;
  LNotifyButton.ShowHint := AHint <> '';
  LNotifyButton.Hint := PChar(AHint);
  LNotifyButton.Align := alLeft;
  LNotifyButton.Width := 45;
  LNotifyButton.ShowCaption := False;
  LNotifyButton.Badge := PChar(ABadgeValue);
  LNotifyButton.Appearance.Assign(AdvOfficePagerOfficeStyler1.GlowButtonAppearance);

  LNotifyButton.AlignWithMargins := True;
  LNotifyButton.Margins.Bottom := 3;
  LNotifyButton.Margins.Left := 3;
  LNotifyButton.Margins.Right := 3;
  if PChar(ABadgeValue) <> '' then
    LNotifyButton.Margins.Top := 9
  else
    LNotifyButton.Margins.Top := 3;
  Bitmap := TBitmap.Create;
  Bitmap.Handle := ABitmapHandle;
  LNotifyButton.Picture.Assign(Bitmap);
  LNotifyButton.Layout := TButtonLayout.blGlyphTop;
  LNotifyButton.OnClick := TNotifyEvent(LMethod); //AnonProc2NotifyEvent(pnlNotifications, AOnClickProc);
end;

Chodzi o:

 LNotifyButton.OnClick := TNotifyEvent(LMethod);

W tym przypadku, nie mogę przekazać dodatkowych parametrów procedury z DLL. Mam jeszczę taki kod:

AnonProc2NotifyEvent(pnlNotifications, AOnClickProc);

który definiuje się tak:

// Klasa wymagana do przypisania OnClick dla procedur przychodzących z DLL (TNotifyProc)
type
  TNotifyEventWrapper = class(TComponent)
  private
    FProc: TProc<TObject>;
  public
    constructor Create(Owner: TComponent; Proc: TProc<TObject>);
  published
    procedure Event(Sender: TObject);
  end;

constructor TNotifyEventWrapper.Create(Owner: TComponent; Proc: TProc<TObject>);
begin
  inherited Create(Owner);
  FProc := Proc;
end;

procedure TNotifyEventWrapper.Event(Sender: TObject);
begin
  FProc(Sender);
end;

function AnonProc2NotifyEvent(Owner: TComponent; Proc: TProc<TObject>): TNotifyEvent;
begin
  Result := TNotifyEventWrapper.Create(Owner, Proc).Event;
end;

W jaki sposób można tutaj dokleić dodatkowe parametry przekazywane z DLL?

0

Myślałem o użyciu metody TControl.ManualDock. Być może to Ci pomoże. W resztę Twojej ostatniej wypowiedzi nie wnikałem.

To, co robisz z dokowaniem, to jest dokładnie to samo co ja robię dokując delphiane okno w nie delphianej aplikacji. Ale nie wiem, czy tak musisz. Ty po obu stronach masz Delphi. No i okno, które ma być dokowane można utworzyć przed utworzeniem zakładki. Na początku pisałeś, że po i nie rozumiałem, co masz na myśli.

0

Ten temat rozwiązałem, została jedynie wątpliwość czy tak się powinno robić. Teraz czekam na pomoc do pytania numer 2.

0

Wydaje mi się, że powinieneś stworzyć swój własny typ np ze stringiem:

type
    TMyNotifyEvent = procedure(Sender: TObject; pStr: string) of object;

oraz dodać helpera do tej klasy TAdvBadgeGlowButton w której będzie property z TMyNotifyEvent
ewentualnie napisać swoją klasę dziedziczącą z TAdvBadgeGlowButton i tam dodać odpowiednie property

1 użytkowników online, w tym zalogowanych: 0, gości: 1