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:
IDrvAx_Native = interface(IDispatch)
['{D1BF8C60-B0EC-4F66-B2AA-72A2B57283C5}']
function LoadPARFile(const PARFilePath: WideString; out ErrorMessage: WideString;
out Chyba: WordBool): HResult; stdcall;
function Run: HResult; stdcall;
function Stop: HResult; stdcall;
function MarkInput(InputIndex: Integer; out CommunicationState: TCommunicationState): HResult; stdcall;
function ReadInputs(out CommunicationState: TCommunicationState): HResult; stdcall;
function MarkOutput(OutputIndex: Integer; const Value: WideString;
out CommunicationState: TCommunicationState): HResult; stdcall;
function WriteOutputs(out CommunicationState: TCommunicationState): HResult; stdcall;
function CleanUp: HResult; stdcall;
end;
a
IDrvAx_Event = dispinterface
['{7BA14667-F8EC-41E5-8215-5F2488701A73}']
function OnInputRead(CommunicationState: TCommunicationState; InputIndex: Integer;
ErrorCode: Integer; const Value: WideString): HResult; dispid 1;
function OnOutputWritten(CommunicationState: TCommunicationState; InputIndex: Integer;
ErrorCode: Integer): HResult; dispid 2;
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:
var
Form1: TForm1;
DL: IDrvAx_Native;
ErrorMessage: WideString;
Chyba: WordBool;
CommunicationState: TCommunicationState;
Serv: IDispatch;
Vystup: IDrvAx_Event;
Value: WideString;
ErrorCode: Integer;
const
com: TGUID = '{45D4DCAF-DCE7-4484-9321-8E12C7B7E8DB}';
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
Serv := CreateComObject(com) as IDispatch;
Vystup := IDrvAx_Event(Serv);
DL := IDrvAx_Native(Serv);
DL.LoadPARFile('C:\Users\user\Documents\IO\IO.par', ErrorMessage, Chyba);
if (Chyba = false) then
begin
Label1.Font.Color := clRed;
Label1.Caption := ErrorMessage;
end;
if (Chyba) and (ErrorMessage = '') then
begin
Label1.Font.Color := clBlue;
Label1.Caption := 'V poradku nahrano!';
Button2.Enabled := true;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
DL.MarkInput(20, CommunicationState);
DL.ReadInputs(CommunicationState);
DL.MarkOutput(5, '2.4', CommunicationState);
DL.WriteOutputs(CommunicationState);
if (Vystup.OnInputRead(CommunicationState, 20, ErrorCode, Value) = S_OK) then
begin
Label1.Caption := Value;
end;
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.