Autor Téma: ActiveX komponenta, chyba 'Member not found'  (Přečteno 924 krát)

Offline chramostejl

  • Nováček
  • *
  • Příspěvků: 3
  • Karma: 0
    • Verze Delphi: Delphi 11 CE
ActiveX komponenta, chyba 'Member not found'
« kdy: 25-09-2024, 13:20:55 »
Ahoj vespolek,

začnu zeširoka: mám tu na stole analogový I/O převodník (hardware), který na svých portech umí číst/zapisovat hodnoty napětí. Komunikaci s počítačem zprostředkovává ActiveX dll, která je celkem slušně zdokumentovaná, poskytuje deset funkcí prostřednictvím dvou rozhraní (interfaces). Mám tu pár příkladů VBS skriptů, které dělají co mají, takže vím, že dll funguje správně.
Chtěl bych si ale napsat prográmek k práci s převodníkem v Delphi a tady jsem jako v podstatě začátečník narazil. V _TLB vypadají zmíněná rozhraní takhle:

Kód: Delphi [Vybrat]
  1. IDrvAx_Native = interface(IDispatch)
  2.     ['{D1BF8C60-B0EC-4F66-B2AA-72A2B57283C5}']
  3.     function LoadPARFile(const PARFilePath: WideString; out ErrorMessage: WideString;
  4.                          out Chyba: WordBool): HResult; stdcall;
  5.     function Run: HResult; stdcall;
  6.     function Stop: HResult; stdcall;
  7.     function MarkInput(InputIndex: Integer; out CommunicationState: TCommunicationState): HResult; stdcall;
  8.     function ReadInputs(out CommunicationState: TCommunicationState): HResult; stdcall;
  9.     function MarkOutput(OutputIndex: Integer; const Value: WideString;
  10.                         out CommunicationState: TCommunicationState): HResult; stdcall;
  11.     function WriteOutputs(out CommunicationState: TCommunicationState): HResult; stdcall;
  12.     function CleanUp: HResult; stdcall;
  13.   end;

a

Kód: Delphi [Vybrat]
  1. IDrvAx_Event = dispinterface
  2.     ['{7BA14667-F8EC-41E5-8215-5F2488701A73}']
  3.     function OnInputRead(CommunicationState: TCommunicationState; InputIndex: Integer;
  4.                           ErrorCode: Integer; const Value: WideString): HResult; dispid 1;
  5.     function OnOutputWritten(CommunicationState: TCommunicationState; InputIndex: Integer;
  6.                              ErrorCode: Integer): HResult; dispid 2;
  7.   end;

Funguje to tak, že LoadPARFile natáhne konfigurační soubor, který v zásadě definuje číslování portů. Funkce MarkInput/Output označí vybrané porty pro čtení/zápis a funkce ReadInputs/WriteOutputs spustí čtění/zápis s tím, že OnInputRead potvrdí čtení, vrátí hodnotu na vstupu a případnou chybu, OnOutputWritten potvrdí zápis a vrátí chybu zápisu.

Udělal jsem si jednoduché klikátko, které načte parametrický soubor a pak se pokusí číst/zapsat vstup/výstup:

Kód: Delphi [Vybrat]
  1. var
  2.   Form1: TForm1;
  3.   DL: IDrvAx_Native;
  4.   ErrorMessage: WideString;
  5.   Chyba: WordBool;
  6.   CommunicationState: TCommunicationState;
  7.   Serv: IDispatch;
  8.   Vystup: IDrvAx_Event;
  9.   Value: WideString;
  10.   ErrorCode: Integer;
  11.  
  12.   const
  13.   com: TGUID = '{45D4DCAF-DCE7-4484-9321-8E12C7B7E8DB}';
  14.  
  15.  
  16. implementation
  17.  
  18. {$R *.dfm}
  19.  
  20. procedure TForm1.Button1Click(Sender: TObject);
  21.  
  22. begin
  23.  
  24. Serv := CreateComObject(com) as IDispatch;
  25. Vystup := IDrvAx_Event(Serv);
  26. DL :=  IDrvAx_Native(Serv);
  27.  
  28. DL.LoadPARFile('C:\Users\user\Documents\IO\IO.par', ErrorMessage, Chyba);
  29.  if (Chyba = false) then
  30.  begin
  31.   Label1.Font.Color := clRed;
  32.   Label1.Caption := ErrorMessage;
  33. end;
  34.     if (Chyba) and (ErrorMessage = '') then
  35.       begin
  36.         Label1.Font.Color := clBlue;
  37.         Label1.Caption := 'V poradku nahrano!';
  38.         Button2.Enabled := true;
  39.       end;
  40.  
  41.  
  42. end;
  43. procedure TForm1.Button2Click(Sender: TObject);
  44. begin
  45.  
  46.   DL.MarkInput(20, CommunicationState);
  47.   DL.ReadInputs(CommunicationState);
  48.  
  49.   DL.MarkOutput(5, '2.4', CommunicationState);
  50.   DL.WriteOutputs(CommunicationState);
  51.  
  52.   if (Vystup.OnInputRead(CommunicationState, 20, ErrorCode, Value) = S_OK) then
  53.   begin
  54.  
  55.   Label1.Caption := Value;
  56.     end;
  57. end;

Funguje to až po volání Vystup.OnInputRead, které vrací 'Member not found'. Vím, že zápis proběhne (naměřím 2.4 V), předpokládám, že proběhne i čtení, ale neumím interagovat s rozhraním DrvAx_Event, které má přečtenou hodnotu vracet.

Prošel jsem si na téma IDispatch a dispinterface co se dalo, výsledek je tenhle kód, který nefunguje, ergo dělám něco blbě, ale motám se v kruhu.

Kdybyste mě někdo byl ochoten vykopnout z něj tím správným směrem, byl bych rád.

Pár podobných problémů jsem našel, typicky ale končí (v lepším případě) sdělením "už mi to funguje", ale bez bližších informací...

Díky.

P.S.: V příloze je celá TLB, pokud by se někomu chtělo s tím ztratit čas.
 
Honza

Offline Jan Fiala

  • Hrdina
  • ****
  • Příspěvků: 445
  • Karma: 6
    • Verze Delphi: 10.4.1
    • PSPad editor
Re:ActiveX komponenta, chyba 'Member not found'
« Odpověď #1 kdy: 26-09-2024, 09:12:35 »
Pro zacatek zkus vytvorit objekt takto:
Kód: Delphi [Vybrat]
  1. DL :=  Tdldrv_ax_.Create(nil); // nebo si tam dej nejakeho vlastnika

U ActiveX se volalo jeste nejake obecne CoInitialize na zacatku a CoUninitialize na konci.

Offline chramostejl

  • Nováček
  • *
  • Příspěvků: 3
  • Karma: 0
    • Verze Delphi: Delphi 11 CE
Re:ActiveX komponenta, chyba 'Member not found'
« Odpověď #2 kdy: 26-09-2024, 15:04:27 »
Díky.

Kód: Delphi [Vybrat]
  1. DL :=  Tdldrv_ax_.Create(nil);

nebo i

Kód: Delphi [Vybrat]
  1. DL :=  Tdldrv_ax_.Create(self);

jsem zkoušel nejdřív. Funkce pod rozhraním _Native fungují.

Jenže tímhle způsobem nevím co s OnInputRead nebo OnOutputWritten (z _TLB):

Kód: Delphi [Vybrat]
  1. Tdldrv_ax_OnInputRead = procedure(ASender: TObject; outCommunicationState: TCommunicationState;
  2.                                                       InputIndex: Integer; ErrorCode: Integer;
  3.                                                       const Value: WideString) of object;
  4. ...
  5.  
  6. Tdldrv_ax_ = class(TOleServer)
  7.   private
  8.     FOnInputRead: Tdldrv_ax_OnInputRead;
  9.  
  10. ...
  11.  
  12.   published
  13.     property OnInputRead: Tdldrv_ax_OnInputRead read FOnInputRead write FOnInputRead;
  14.  
  15.   end;

V céčku mi funguje:
Kód: C++ [Vybrat]
  1. dldrv_ax.dldrv_axClass DL;
  2.  
  3. ...
  4. DL = new dldrv_ax.dldrv_axClass();
  5.  
  6. ...
  7. DL.OnInputRead += new dldrv_ax.IDrvAx_Event_OnInputReadEventHandler(DL_OnInputRead);
  8. ...
  9.  
  10. void DL_OnInputRead(dldrv_ax.TCommunicationState CommunicationState, int InputIndex, int ErrorCode, string Value)
  11. {
  12.  
  13. ...
  14.  
  15. }
  16.  

ale v Delphi si s tím nevím rady...

Díky.
Honza

Offline Radek Červinka

  • Administrátoři
  • Padawan
  • *****
  • Příspěvků: 3496
  • Karma: 112
    • Verze Delphi: D2007, DXE + 2 poslední
    • O Delphi v češtině
Re:ActiveX komponenta, chyba 'Member not found'
« Odpověď #3 kdy: 26-09-2024, 15:10:00 »
Kód: Delphi [Vybrat]
  1. Tdldrv_ax_OnInputRead = procedure(ASender: TObject; outCommunicationState: TCommunicationState;
  2.                                                       InputIndex: Integer; ErrorCode: Integer;
  3.                                                       const Value: WideString) of object;
  4.  
znamena, ze mas mit metodu tridy / to je to of object/ (treba formulare), ktera ma dane parametry. Takze to bude vypadat
Kód: Delphi [Vybrat]
  1. procedure TForm1.MyOnInputRead(ASender: TObject; outCommunicationState: TCommunicationState;
  2.                                                       InputIndex: Integer; ErrorCode: Integer;
  3.                                                       const Value: WideString);
  4. begin
  5.  
  6. end;
  7.  
  8. Takze pak udelas DL.OnInputRead := MyOnInputRead;
  9.  

Ale to kdyby jsi to mel instalovane jako komponenty, tak to mas jako normalni event (jako u tlacitka OnClick).
Embarcadero MVP - Czech republic

Offline Radek Červinka

  • Administrátoři
  • Padawan
  • *****
  • Příspěvků: 3496
  • Karma: 112
    • Verze Delphi: D2007, DXE + 2 poslední
    • O Delphi v češtině
Re:ActiveX komponenta, chyba 'Member not found'
« Odpověď #4 kdy: 26-09-2024, 15:12:06 »
Proc sis to nedal nainstalovat? Jako pres menu Component / Install Component. Pak by jsi to mohl normalne soupnout na formular a tipnu ze mezi udalostmi by jsi mel to OnInputRead.
Embarcadero MVP - Czech republic

Offline chramostejl

  • Nováček
  • *
  • Příspěvků: 3
  • Karma: 0
    • Verze Delphi: Delphi 11 CE
Re:ActiveX komponenta, chyba 'Member not found'
« Odpověď #5 kdy: 26-09-2024, 15:48:47 »
!! Díky za kopanec, zdá se, že Import Component != Install Component.

Zkusím se prokousat dál a případně se ozvu.

Díky!!
Honza