Tworzenie TButton w DLL na panelu aplikacji głównej

0

Witam,

mam interfejs obsługi pluginów, w którym przekazuję do DLL panel, na którym chciałbym z poziomu biblioteki DLL tworzyć np. przyciski.

Aplikacja rodzic

function GetNotificationPanel(out NotificationPanel: TPanel): HRESULT; stdcall;

function TfrmMain.GetNotificationPanel(out NotificationPanel: TPanel): HRESULT;
begin
  result := S_OK;
  NotificationPanel := pnlNotifications;
end;

Biblioteka DLL

var
  FNotificationPanel: TPanel;
begin
  ap.GetNotificationPanel(FNotificationPanel);
end;

Jeżeli chcę utworzyć np. TButton na panelu FNotificationPanel, to przy przypisywniu Parent, wyrzuca

Cannot assign TFont to TFont

Rozumiem, że Aplikacja rodzic ma własną obsługę VCL i biblioteka DLL swoją więc mogą się ze sobą kłócić, ale pytanie brzmi w jaki sposób rozwiązać ten problem?
Tworzenie bibliotek w paczkach .bpl odpada. Założeniem mają być DLL, które można tworzyć w różnych językach programowania do programu.

0

Jak wcześniej pisałem to nigdy nie robiłem wtyczek. Spróbowałbym tu rzutowania. Najpierw na TObject, czyli:

function GetNotificationPanel(out NotificationPanel: TObject): HRESULT; stdcall;
 
function TfrmMain.GetNotificationPanel(out NotificationPanel: TObject): HRESULT;
begin
  result := S_OK;
  NotificationPanel := TObject(pnlNotifications);
end; 

(choć chyba to TObject(pnlNotifications) tu nie jest potrzebne) i:

var
  FNotificationPanel: TPanel;
begin
  ap.GetNotificationPanel(TObject(FNotificationPanel));
end; 

no a jeśli nie zadziała rzutowanie na TObject, to spróbowałbym rzutowania na Pointer, czyli:

function GetNotificationPanel(out NotificationPanel: Pointer): HRESULT; stdcall;
 
function TfrmMain.GetNotificationPanel(out NotificationPanel: Pointer): HRESULT;
begin
  result := S_OK;
  NotificationPanel := pnlNotifications;
end; 
0

A w jaki sposób na podstawie Pointera, przypisać

 FNotificationButton.Parent := ???? // FNotificationPanel

A może założenie mam błędne?
Może powinienem w aplikacji głównej zrobić API, które umożliwi bibliotece tworzenie takiego przycisku i przesłać jego zdarzenie OnClick w parametrze? Wtedy aplikacja główna tworzyłaby przycisk na panelu FNotificationPanel i podpinala do przycisku zdarzenie przychodzące w parametrze z DLL. Pytanie jak wtedy z zarządzaniem pamięcią?

0

Poszukaj sobie w źródłach Delphi jak jest zrobiony obiekt TList. A potem popatrz, jak zrobili obiekt TObjectList. W moim Delphi TList jest w Classes.pas, a TObjectList w Contnrs. TObjectlist robi rzutowania pointerów. W jedną i w drugą stronę.

0

@user322: wystarczy zwykłe rzutowanie, dlatego że - z grubsza ujmując - referencje do obiektów to też wskaźniki; Dlatego też przerobienie referencji na zwykły wskaźnik to rzutowanie na Pointer:

MyPointer := Pointer(SomeObject);

W drugą stronę też wystarczy zwykłe rzutowanie, tyle że na konkretną klasę:

MyObject := TObject(SomePointer);

W Twoim przypadku potrzebujesz zamienić wskaźnik na TWinControl, bo takiego typu zapewne jest Parent:

FNotificationButton.Parent := TWinControl(FNotificationPanel);

Jeśli FNotificationPanel to instancja klasy dziedziczącej po TWinControl to będzie działać.

0

Właśnie sprawdziłem kilka minut temu i błąd jest ten sam. Zabrałem się za przerabianie, aby przycisk tworzyć po stronie aplikacji głównej, a z DLL przekazywać przez interface

TMyProc = procedure(Sender: TObject)

Na razie wywołanie działa, i debugger wchodzi w kod odpowiedzialny za procedurkę w DLL. W niej robię

MessageBox(0, 'Test', 'Test', 0);

Ale po tym komunikacie w DLL, zawiesza się program - po jego zamknięciu, tzn nie można nic kliknąć. Wydaje mi się, że to przez uchwyt = 0...

0

W jaki sposób przypisać do zdarzenia np.

Button1.OnClick := MyProc; 

Kompilator krzyczy

[dcc32 Error] Main.pas(563): E2009 Incompatible types: 'method pointer and regular procedure'

Po wywołaniu z DLL funkcji

function AddNotifyBadgeButton(AButtonName, AHint, ABadgeValue: string; ABitmap: TBitmap; AOnClickProc: TNotifyProc): HRESULT; stdcall;

Zawiesza się program

function TfrmMain.AddNotifyBadgeButton(AButtonName, AHint, ABadgeValue: string; ABitmap: TBitmap; AOnClickProc: TNotifyProc): HRESULT;
var
  LNotifyButton: TAdvBadgeSpeedButton;
  Glyph: TBitmap;
begin
  result := S_OK;

  LNotifyButton := TAdvBadgeSpeedButton.Create(pnlNotifications);
  LNotifyButton.Parent := pnlNotifications;
  LNotifyButton.Name := AButtonName;
  LNotifyButton.Hint := AHint;
  LNotifyButton.Align := alLeft;
  LNotifyButton.Flat := True;
  LNotifyButton.Width := 45;
  LNotifyButton.Badge := ABadgeValue;

  LNotifyButton.AlignWithMargins := True;
  LNotifyButton.Margins.Top := 10;
  //Glyph := TBitmap.Create;
  //frmDM.ImageList1.GetBitmap(1, NotificationButtonGlyph);
  LNotifyButton.Glyph := ABitmap;
  //LNotifyButton.OnClick := AOnClickProc;
end;
0

W jaki sposób przypisać do zdarzenia np.

Button1.OnClick := MyProc;

MyProc musi być zgodny z typem TNotifyEvent (bo takiego typu jest właściwość OnClick), czyli posiadać jeden parametr typu TObject - z reguły nazywany jest po prostu Senderem.

0

Posiada, ale jest on w DLL

type TNotifyProc = procedure(Sender: TObject);

procedure NotifyButtonOnClick(Sender: TObject);
begin
  MessageBox(0, 'ok', 'test', 0);
end;
0

Musi też być metodą klasy, aby bezpośrednie przypisanie było kompatybilne;

Dla zwykłej procedury też się da, ale trzeba nieco magii - tyle że nie pamiętam jak się to robiło, więc poszukaj sam w Google; Kiedyś tu na forum podawałem przykładowy kod, ale nie dam rady tego teraz znaleźć.

0

A możesz podpowiedzieć w jaki sposób w DLL "wsadzić" tą procedurę w klasę i ją przekazać w takiej formie jak wyżej, jeżeli w DLL mam tylko Unit1.pas?

Ok, poszukam

0

A bo ja wiem... :D

Poszukaj tego co podałem, ale nie jestem w stanie potwierdzić, czy to faktycznie zadziała.

0

Ok, poradziłem sobie.

Teraz pytanie, jak przekazać TBitmap z DLL do aplikacji głównej?

1

Przypomniałem sobie - zwykłą procedurkę można przypisać za pomocą typu TMethod:

procedure SomeProc();
begin
  Application.MessageBox('', '');
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  LMethod: TMethod;
begin
  LMethod.Data := nil;
  LMethod.Code := @SomeProc;

  Button1.OnClick := TNotifyEvent(LMethod);
end;

Okienko wiadomości wyświetla się po wciśnięciu przycisku, więc działa; Problem będzie, jeśli SomeProc ma mieć parametry.

0

Super, dzięki, ja znalazłem trochę rozbudowany kod, który przyjmie też parametry. Ale Twój zupełnie wystarcza na moje potrzeby.

Z przekazaniem Bitmapy z DLL do aplikaci głównej też sobie poradziłem poprzez przesłanie Handle.

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