Skocz do zawartości


Zdjęcie

Pisanie wtyczki - pierwsze kroki i problem nie do przebicia


  • Zaloguj się, aby dodać odpowiedź
23 odpowiedzi w tym temacie

#1 gnomiuszka

gnomiuszka

    Adept

  • Użytkownik
  • 277 postów

Napisano 2013.02.17, 02:19

Zajrzałem do SDK na stronie głównej aqq i postanowiłem zacząć tworzyć jakieś własne wtyczki do aqq. Dawniej sporo programowałem w delphi, obecnie piszę w C#. Wiem jednak, że C# może się nie nadać do tworzenia wtyczek, ponieważ użytkownik może być wtedy zmuszony do instalacji całego net frameworka tylko po to, by wtyczka mogła realizować jakąś małą prostą funkcję. Tak oto powróciłem do delphi i środowiska Borland developer 2006 turbo deophi. 

 

Pobrałem następnie wtyczkę wyświetlającą komunikat Hello word. Ładuję wszystko do środowiska programistycznego, następnie chcę skompilować i dostaję o coś takiego

 

 
Zaczynam podejrzewać, że używane środowisko / komponenty są chyba za stare i niektóre moduły nie są rozpoznawane. 
Normalnie jak piszę jakąś aplikację okienkową to wszystko jest oki. Dla pewności, czy wszystko dobrze się załadowało wybieram menu project > add i wstawiam plik pluginAPI.pas pobrany ze strony SDK AQQ, a w swojej wtyczce w sekcji uses dopisuję pluginAPI, ale środowisko podkreśla to na czerwono tak, jak by wogóle nie był wykrywany. 


#2 WiTuŚ

WiTuŚ

    AQQ Maniak

  • Przyjaciel
  • 2028 postów

Napisano 2013.02.17, 10:58

Musisz pobrać także nagłówek czyli PluginAPI i wrzucić plik do folderu z wtyczką Hello World. Problem zniknie.



#3 gnomiuszka

gnomiuszka

    Adept

  • Użytkownik
  • 277 postów

Napisano 2013.02.18, 18:04

Tak, faktycznie plik pluginAPI.pas nie znajdował się w katalogu projektu wtyczki. Dodałem go i wydawało się, ze problem zniknął, ale gdy prubowałem wywołać jakieś zdarzenie odwołujące się do notyfikacji aqq otrzymywałem błąd. Gdy jednak zadeklarowałem funkcje bez operacji i je wyprowadziłem, biblioteka normalnie kompilowała się i była prawidłowo rozpoznawana przez aqq ;)



#4 gnomiuszka

gnomiuszka

    Adept

  • Użytkownik
  • 277 postów

Napisano 2013.02.20, 21:46

hmm. Sprawa jest dość zastanawiająca. Robię wszystko według dokumentacji SDK i mam problem z atrybutem OnModulesLoaded. Kompilator nie wykrywa tej zmiennej i nie wiem, czy muszę ją zadeklarować, czy muszę jeszcze coś doimportować?

Oto zrzut ekranowy z kłopotliwym kodem

 

 

 
Gdy wstawię znak // w liniach zawierających komendę OnModulesLoaded, wszystko się normalnie kompiluje i wygląda tak
 

 

Moim celem jest napisanie wtyczki, która pozwoli na wyszukiwanie w tekście słów kluczowych lub całych zdań, tak jak jest to w wordzie czy przeglądarkach internetowych pod skrótem ctrl+f. Taka funkcjonalność przyda się szczególnie w archiwum, gdzie można decydować, czy przeszukiwanie ma dotyczyć konkretnej, wybranej długiej rozmowy, czy wszystkich rozmó z wybraną osobą. Załóżmy, że trzy miesiące temu nasz znajomy wysłał nam linka do czegoś ciekawego i podał nam tytuł artykułu. Wchodzimy do archiwum, zaznaczamy kontakt i po wciśnięciu ctrl+F wpisujemy tytuł tego materiału. W rezultacie otrzymujemy listę rozmów, które zawierają wpisany prez nas tytuł artykułu. Teraz wystarczy przejrzeć mocno zawężoną listę, by w mik znaleźć to, czego się szukało :) Tylko, ze to moja pierwsza wtyczka, jaką próbuję napisać. Stąd wynikają problemy opisane wyżej i przedstawione w kodzie. Gdyby ktoś chciał się zgłosić do mnie w tej sprawie, to proszę napisać na jabber gnomiuszka@aqq.eu. Z góry dzięki :)



#5 Beherit

Beherit

    In Sorte Diaboli

  • Wtyczkopisarz
  • 5031 postów

Napisano 2013.02.20, 22:12

OnModulesLoaded jest funkcją, musisz takową umieścić w kodzie. Równie dobrze zamiast tego możesz dać "DupaDupa" i taką funkcję dać w kodzie i zadziała - wywoła się po dostaniu wskazanej notyfikacji.

P.S. Jak chcesz wrzucać na forum kod to nie dawaj zrzutów - można to dać w tagu code.

Użytkownik Beherit edytował ten post 2013.02.20, 22:12


#6 gnomiuszka

gnomiuszka

    Adept

  • Użytkownik
  • 277 postów

Napisano 2013.02.20, 22:44

Dzięki Beherit. Zrobiłęm, jak poradziłeś. Teraz sprawa stała się jaśniejsza. Poprawiłem i działa. Przy starcie aqq mam dodatkowy komunikat. Teraz mogę rozbudować aqq by odtwarzał dodatkowo dźwięk uruchomienia, jak przy świątecznym splashu :D. A więc te notyfikacje jako parametr przyjmują referencje do konkretnej funkcji, którą trzeba zdefiniować. Jeszcze raz dzięki :D



#7 gnomiuszka

gnomiuszka

    Adept

  • Użytkownik
  • 277 postów

Napisano 2013.02.22, 01:01

a więc tak

Wtyczka zaczyna działać i po odpaleniu odtwarza mi dźwięk powitalny przy starcie aqq :)

Mam jednak pewien kłopot ze ścieżkami względnymi. Odnoszę wrażenie, że przy wywoływaniu dll-ki z poziomu aqq nie wiadomo, w jakiej lokalizacji znajduje się wtyczka. z tego powodu odwołanie się do względnej ścieżki nie działa na przykład zamiast pisać plik:='audio.wav' muszą pisać plik:=C:/katalog/audio/muzyczka/audio.wav';

Jest to szczególnie kłopotliwe przy próbie zapisu/odczytu ustawień wtyczki z pliku strukturalnego 'ustawienia.plgaqq'. Otrzymuję wówczas komunikat "File access denied". Przy tradycyjnych projektach aplikacji VCL nie ma żadnych zgrzytów tego typu. 

Oto źródłowy kod mojej wtyczki

 

//Główny plik

library MojaWtyczka;


uses
  SysUtils,
  Windows,
  MMSystem,
  PluginAPI,
  Dialogs,
  Unit2 in 'Unit2.pas',
  Unit3 in 'Unit3.pas' {Form3};

var
  PluginInfo: TPluginInfo;
  PluginLink: TPluginLInk;
  sciezka:PChar;


{$R *.res}
    function AQQPluginInfo(AQQVersion: Dword): PpluginInfo; stdcall;
    begin
    PluginInfo.Author:='gnomiuszka';
    PluginInfo.ShortName:='Poszukiwacz';
    PluginInfo.Version:=PLUGIN_MAKE_VERSION(0,0,0,1);
    PluginInfo.Flag:=0;
    PluginInfo.Description:='Szukaj słów kluczowych w wybranej rozmowie lub we wszystkich rozmowach z wybranym kontaktem. Wtyczka działa na zasadzie funkcji znajdowania (ctrl+F)';
    PluginInfo.ReplaceDefaultModule:=0;
    PluginInfo.cbSize:=SizeOf(TPluginInfo);
    Result:=@PluginInfo;
    end;
    Function OnModulesLoaded(wParam, LParam: integer):integer; stdcall;

    begin
   playsound('E:/onLoad.wav', 0, SND_FILENAME);
   //działa tylko na bezwzględnej scieżce
   //jak odwołać się do lokalizacji?
   //chodzi o coś akiego
   //sciezkapliku:=AQQ_USED_PROFILE_LOCATION+'/plugins/mojawtyczka/onLoad.wav'
    result:=0;
    end;
    function Load(link: PPluginLink): Integer; stdcall;
    begin
      PluginLink:=Link^;
     PluginLink.HookEvent(AQQ_SYSTEM_MODULESLOADED, OnModulesLoaded);
      Result:=0;
    end;
    function UnLoad: Integer; stdcall;
    begin
      Result:=0;
      PluginLink.UnhookEvent(Thandle(@OnModulesLoaded));
      playsound(nil, 0, SND_PURGE);//zatrzymanie odtwarzania
    end;
    Function Settings: Integer; stdcall;
    begin
      result:=0;
      if not Assigned(Form2) then
      begin
        Form3 := TForm3.Create(nil);

      end;
      Form3.ShowModal;
FreeAndNil(Form3);

      
    end;


exports
Load,
Unload,
AQQPluginInfo,
Settings;
begin
end.

 

a to kod odpowiedzialny za okno ustawień i zapisywanie ustawień

unit Unit3;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, MMSystem;

type
  TForm3 = class(TForm)
    GroupBox1: TGroupBox;
    w_rozmowie: TCheckBox;
    w_archiwum: TCheckBox;
    GroupBox2: TGroupBox;
    wybr_rozmowa: TRadioButton;
    wszyst_rozm: TRadioButton;
    GroupBox3: TGroupBox;
    przycisk: TCheckBox;
    skrot: TCheckBox;
    klawisz: TEdit;
    GroupBox4: TGroupBox;
    Edit2: TEdit;
    Button1: TButton;
    OpenDialog1: TOpenDialog;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    procedure w_archiwumClick(Sender: TObject);
    procedure skrotClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form3: TForm3;
  sciezkapliku: string='E:/OnLoad';
  type PluginSet= packed record
    w_rozmowie: bool;
    w_archiwum: bool;
    calearchiwum: bool;
    wybrana_rozmowa: bool;
    przycisk:bool;
    skrot:bool;
    klawisz: string[10];
  end;
  var
  ustaw:PluginSet;
  plik: file of PluginSet;
  const nazwapliku='ustawienia.plgaqq';


implementation

{$R *.dfm}

procedure TForm3.Button1Click(Sender: TObject);
begin
if OpenDialog1.Execute then
begin
   sciezkapliku:=OpenDialog1.FileName;
   Edit2.Text:=sciezkapliku;
   //ustawienie pliku audio odgrywanego przy starcie

end;

end;

procedure TForm3.Button2Click(Sender: TObject);
begin
playsound(PChar(sciezkapliku), 0, SND_ASYNC);
//test pliku
end;

procedure TForm3.Button3Click(Sender: TObject);
begin
//zapis ustawień do pliku
assignfile(plik, nazwapliku);
//w normalnych projektach działa, ale tu otrzymuję komunikat File access denied
ReWrite(plik);
seek(plik, 0);
ustaw.w_rozmowie:=w_rozmowie.Checked;
ustaw.w_archiwum:=w_archiwum.checked;
ustaw.calearchiwum:=wszyst_rozm.Checked;
ustaw.wybrana_rozmowa:=wybr_rozmowa.Checked;
ustaw.przycisk:=przycisk.Checked;
ustaw.skrot:=skrot.Checked;
ustaw.klawisz:=klawisz.Text;
write(plik, ustaw);
closefile(plik);
end;

procedure TForm3.w_archiwumClick(Sender: TObject);
begin
groupBox2.Enabled:=w_archiwum.Checked;
end;

procedure TForm3.skrotClick(Sender: TObject);
begin
klawisz.Enabled:=skrot.Checked;
end;

end.

 

 



#8 Draen

Draen

    Bywalec

  • Użytkownik
  • 128 postów

Napisano 2013.02.22, 09:16

Proszę bardzo: http://www.aqq.eu/sdk/data/f105.html



#9 Beherit

Beherit

    In Sorte Diaboli

  • Wtyczkopisarz
  • 5031 postów

Napisano 2013.02.22, 09:25

Względna ścieżka odnosi się do procesu, czyli plik jest szukany przy "AQQ.exe.". Tak jak wyżej, do tych celów użyj SDK.

#10 Oconnel

Oconnel

    AQQ Developer

  • Właściciel
  • 4622 postów

Napisano 2013.02.22, 10:31

Dodatkowo ustawienia wtyczki lepiej zapisuj w formacie INI, a nie w pliku rekordowym. Będziesz miał problem potem z dodaniem kolejnych opcji do tak stworzonego pliku.


MyPortalYouTubeTwitter54slty.png

#11 gnomiuszka

gnomiuszka

    Adept

  • Użytkownik
  • 277 postów

Napisano 2013.02.22, 14:04

Dzięki za pomoc :)

Skoro względna ścieżka odnosi się do katalogu, w którym jest proces aqq.exe, a jest to katalog systemowy, którego nie można modyfikować rozumiem, że stworzenie pliku w takiej sytuacji jest niemożliwe.

 

Jeśli zaś chodzi o odwołanie się do katalogu znajdującego się w katalogu wtyczek to czy mam to zapisać tak?

 

ustawienia:=AQQ_FUNCTION_GETPLUGINUSERDIR+'\poszukiwacz\ustawienia.ini';  

Prubowałem odwoływać się do tego atrybutu, ale w komunikacie zwraca mi Aqq/System/UserDir, zamiast np C:/users/gnomiuszka/wapster/aqq folder/...

 

O plikach ini na pewno pomyślę. Faktycznie, podczas pisania okna ustawień, gdy przychodziło mi na myśl dodanie nowej pozycji w ustawieniach, musiałem za każdym razem modyfikować strukturę.

 



#12 Beherit

Beherit

    In Sorte Diaboli

  • Wtyczkopisarz
  • 5031 postów

Napisano 2013.02.22, 14:42

AQQ_FUNCTION_GETPLUGINUSERDIR zwraca folder, gdzie znajdują się instalowane przez użytkownika wtyczki czyli folder prywatny wtyczek. Jeżeli zwraca Ci coś innego to musisz coś robić nie tak - i zresztą widać to w kodzie powyżej bo AQQ_FUNCTION_GETPLUGINUSERDIR wywołuje się poprzez PluginLink.CallService(AQQ_FUNCTION_GETPLUGINUSERDIR,0,0). W C++ wygląda to tak:
UnicodeString GetPluginUserDir()
{
  return StringReplace((wchar_t*)PluginLink.CallService(AQQ_FUNCTION_GETPLUGINUSERDIR,0,0), "\\", "\\\\", TReplaceFlags() << rfReplaceAll);
}
//---------------------------------------------------------------------------
U mnie jest jeszcze StringReplace bo w C++ ścieżki mają podwójne ukośniki.

#13 Oconnel

Oconnel

    AQQ Developer

  • Właściciel
  • 4622 postów

Napisano 2013.02.22, 14:59

// Pobieramy ścieżkę do katalogu gdzie zainstalowana jest wtyczka
Path := PWideChar(PluginLink.CallService(AQQ_FUNCTION_GETPLUGINUSERDIR, hInstance, 0));
// Dodajemy do ścieżki katalog z nazwą naszej wtyczki
Path := Path + '\Nazwa_Wtyczki';
// Upewniamy się, że katalog istnieje.
ForceDirectories(Path);
// Plik ustawień możemy zatem umieścić w tym katalogu.
plik_ustawien := Path + '\Settings.ini';


MyPortalYouTubeTwitter54slty.png

#14 Beherit

Beherit

    In Sorte Diaboli

  • Wtyczkopisarz
  • 5031 postów

Napisano 2013.02.22, 18:48

W funkcji AQQ_FUNCTION_GETPLUGINUSERDIR nie trzeba podawać uchwytu do pliku DLL :P

#15 gnomiuszka

gnomiuszka

    Adept

  • Użytkownik
  • 277 postów

Napisano 2013.02.23, 22:37

hmm Marnie coś idzie. Udało się co prawda wczytać ustawienia wtyczki z pliku ini, ale mam problem z pobraniem ścieżki do katalogu wtyczek z poziomu obiektu Form3, gdy wywołuję zdarzenie button3click. Próbowałem zadeklarować zmienne PluginLink oraz scieżka jako zmienne publiczne, które będą widoczne zarówno z poziomu głównego pliku, jak i z poziomu pliku odpowiedzialnego za formę. Tu jednak pojawia się problem widoczności, a zadeklarowanie wspomnianych zmiennych jako publiczne kończy się błędem. Prubuję zatem stworzyć nowe instancje zmiennej ścieżka oraz PluginLink i wszystko niby się pięknie kompiluje, uruchamia, ale gdy chcę pobrać lokalizację katalogu wtyczek i dostać się do pliku ini, dostaję komunikat Access Violation at MojaWtyczka.dll. Zastanawiam się teraz, czy nie stworzyć klasy statycznej, w której będą przechowywane obiekty PluginLink oraz Sciezka, wtedy mógłbym odwoływać się do tych zmiennych z dowolnego okna wtyczki. 

 

Oto jak wygląda zdarzenie przycisku zapisującego ustawienia

procedure TForm3.Button3Click(Sender: TObject);
begin
//zapis ustawień do pliku
   ini:=TiniFIle.Create(sciezka+'\ustawienia.ini');
   try
        ini.WriteBool('szukanie', 'w_rozmowie', w_rozmowie.Checked);
        ini.WriteBool('szukanie', 'w_archiwum', w_archiwum.Checked);
        ini.WriteBool('szukanie', 'wszyst_rozm',wszyst_rozm.Checked);
        ini.WriteBool('szukanie', 'wybr_rozmowa', wybr_rozmowa.Checked);
   finally
    ini.Free;
    showMessage('zapisano');
   end;
//w normalnych projektach działa, ale tu otrzymuję komunikat access Violation
end;

 

 



#16 Beherit

Beherit

    In Sorte Diaboli

  • Wtyczkopisarz
  • 5031 postów

Napisano 2013.02.23, 23:03

Nie da się na formach korzystać z SDK. Jak w takim razie na formie pobrać ścieżkę do katalogu prywatnego wtyczek? Wystarczy, że w rdzeniu wtyczki (główny plik) napiszesz funkcję, która zwraca ścieżkę. Potem już wystarczy tę funkcję zaimportować na formie - niestety nie wiem jak to wygląda w Delhi, w C++ wygląda to tak (analogicznie do tej funkcji co podałem wcześniej w temacie):
__declspec(dllimport)UnicodeString GetPluginUserDir();


#17 Oconnel

Oconnel

    AQQ Developer

  • Właściciel
  • 4622 postów

Napisano 2013.02.24, 10:04

Dodaj do projektu DataModule. W nim trzymaj wszystkie globalne deklaracje zmiennych. Stwórz ten moduł zaraz przy okazji funkcji OnLoad. Potem do każdej nowej formy dodaj w klauzuli "uses" (dolnej lub górnej) moduł który dodałeś wcześniej. I tyle, masz dostęp do wszystkiego.


MyPortalYouTubeTwitter54slty.png

#18 gnomiuszka

gnomiuszka

    Adept

  • Użytkownik
  • 277 postów

Napisano 2013.02.26, 01:24

dzięki OC. Globalne zmienne mam teraz w oddzielnym pliku DataModule.pas, którego podpinam do wszystkich innych modułów i działa :) ustawienia zapisują się i ładują po restarcie aqq. Takie odwoływanie się między modułami ma jeszcze jedną fajną zaletę, że nie muszę dublować zmiennych. np, mogę napisać napisać warunek

if Form3.przycisk.checked then
//wyświetlenie przycisku wyszukiwania
end;
if Form3.w_archiwum.checked then
//działa funkcje przeszukiwania w archiwum
end;

Jeden ważny etap już jest. Teraz pozostaje funkcjonalność :)



#19 gnomiuszka

gnomiuszka

    Adept

  • Użytkownik
  • 277 postów

Napisano 2013.03.01, 17:19

Odnośnie funkcjonalności mam pytanka odnośnie funkcji i notyfikacji. Nie wiem dokładnie, jakie funkcje można podpinać pod notyfikacje i w jakich zdarzenach te notyfikacje się wywołuje. chodzi mi na przykład o to, że jak mam focus na oknie rozmowy, to ma reagować na skrut klawiszowy. czyli coś takiego

procedure onKeyPress (sender: TObiect)
begin
if form3.skrot.text=sender.keyCode then
begin
szukaj:=Form1.Create;
szukaj.showmodal;
end;
end;

 

następnie w zdarzeniu reagującym na wprowadzanie tekstu do pola Edit zaznaczałbym znaleziony tekst w oknie rozmowy. czyli

procedure editTextChange(sender: Tobiect)
begin
przeszukaj(edit.text);
end;


 

i tu moje pytanko, jak pobierać tekst z aktywnej zakładki w oknie rozmowy i go zaznaczać?

i czy reakcje na skróty klawiszowe są przypisane do okna z focusem czy są to globalne skróty klawiszowe aqq? 



#20 WiTuŚ

WiTuŚ

    AQQ Maniak

  • Przyjaciel
  • 2028 postów

Napisano 2013.03.01, 19:27

Uuuuu nie wiem czy w ogóle da się to zrobić. Jeżeli chodzi o SDK to osobiście nigdy takiej funkcji nie widziałem, bo raczej takiej nie ma. Chyba, że jakoś na około da się takie coś zrobić (takie coś jak odnalezienie szukanego tekstu w oknie rozmowy i zaznaczenie go).

 

Jeżeli chodzi o okno rozmowy i zakładkę to jest coś takiego jak uchwyt danego okna - musisz go pobrać i wtedy ewentualnie masz nad nim kontrolę,


Użytkownik WiTuŚ edytował ten post 2013.03.01, 19:37





Użytkownicy przeglądający ten temat: 0

0 użytkowników, 0 gości, 0 anonimowych