Rozdział 13. COM i ActiveX

Adam Boduch

W tym rozdziale zajmiemy się dość specyficznym aspektem programowania, stworzonym przez firmę Microsoft i nadal z nią kojarzonym ? projektowaniem kontrolek COM i ActiveX. Dowiesz się, do czego służą kontrolki ActiveX, jak je tworzyć i jak ich używać.

1 Czym jest COM?
     1.1 Kontrolka w rozumieniu COM
     1.2 Odrobinę historii
2 Tworzenie obiektów COM
3 Metody i właściwości
     3.3 Dodawanie metod
          3.3.1 Parametry nowej metody
          3.3.2 Wartość zwrotna funkcji
     3.4 Dodawanie właściwości
4 Kod źródłowy kontrolki
5 Budowa i rejestracja kontrolki
6 Wykorzystanie obiektu COM
7 Interfejsy
     7.5 GUID
8 ActiveX
9 Import kontrolek ActiveX
     9.6 Wykorzystanie kontrolki TVText
10 Tworzenie kontrolek ActiveX
11 Przykład: wyświetlanie napisów do filmu
     11.7 Tworzenie interfejsu COM
     11.8 Tworzenie kontrolki ActiveX
          11.8.3 Strona wizualna
          11.8.4 Kod źródłowy kontrolki
     11.9 Budowa i rejestracja
12 ActiveX w Internecie
     12.10 Względy bezpieczeństwa
     12.11 Przykładowa kontrolka
          12.11.5 Tworzenie kontrolki ActiveX
          12.11.6 Publikowanie kontrolki
13 Podsumowanie

Czym jest COM?

Rozwinięciem angielskiego skrótu COM jest nazwa Component Object Model (obiektowy model komponentów). Jest to specyfikacja firmy Microsoft, która w założeniu dotyczy tworzenia obiektów wielokrotnego użytku, niezależnie od języka programowania.

Aby zrozumieć ActiveX, musisz poznać COM ? postaram się zwięźle Ci to wytłumaczyć. Otóż firma Microsoft stworzyła model obiektów, które wykorzystywane mogą być w każdym środowisku programistycznym Win32. Wynikiem powstania obiektu COM jest kontrolka ? plik z rozszerzeniem .ocx. Kontrolka taka może być wykorzystana zarówno w Delphi, jak i Visual C++, C++ Builder czy Visual Basic.

Na razie obiekty COM są jedynie obiektami działającymi w różnych środowiskach Windows ? niemożliwe jest wykorzystanie ich poza tym systemem.

Kontrolka w rozumieniu COM

W tym rozdziale będę używał słowa ?kontrolka? w znaczeniu obiektu COM. Do tej pory to słowo kojarzyło Ci się zapewne z komponentem Delphi. Można powiedzieć, że obiekty COM są takim uniwersalnym komponentem, podobnym do biblioteki DLL. Raz utworzona kontrolka może być wykorzystywana wiele razy, przez wielu programistów oraz w różnych środowiskach programowania. Jeżeli ktoś już napisał kontrolkę spełniającą daną funkcję, to po co wyważać otwarte drzwi i tworzyć jeszcze raz to samo? Przykładem może być przeglądarka WWW. Napisanie programu analizującego kod HTML jest niezwykle czasochłonnym i żmudnym zadaniem. Niekiedy jednak w naszym programie konieczne staje się wyświetlenie jakiegoś dokumentu w formie strony WWW. Dzięki technologii COM i ActiveX (o ActiveX powiemy nieco później) możemy zaimportować udostępnione przez twórców przeglądarki obiekty COM i wykorzystać je w programie jako dodatkowy komponent.

Odrobinę historii

COM jest technologią stosunkowo nową, bo powstałą kilka lat temu. Wprowadzenie jej miało na celu stworzenie jednolitego standardu komunikacji, tak aby np. (by jeszcze raz posłużyć się powyższym przykładem) programiści mogli korzystać z możliwości przeglądania stron WWW w swoich aplikacjach. Firma Microsoft wyszła naprzeciw temu zadaniu i utworzyła moduł obiektów (COM), który umożliwia udostępnianie innym aplikacjom swoich metod.
W przypadku Delphi zaimportowana kontrolka COM staje się jakby komponentem, umieszczonym na palecie komponentów. Wówczas w dość prosty sposób można skorzystać z zalet takiego obiektu, wywołując zawarte w nim metody.

Tworzenie obiektów COM

  1. Z menu File wybierz New/New/Other. Pojawi się Repozytorium. Zaznacz zakładkę ActiveX (rysunek 13.1).

13.1.jpg
Rysunek 13.1. Zakładka ActiveX Repozytoriumv

  1. Zaznacz w tym oknie ikonę ActiveX Library i naciśnij OK. W tym momencie zostanie utworzony pusty projekt.
  2. Z menu File wybierz polecenie Save. Wskaż miejsce, gdzie Delphi ma zapisać plik.

Utworzyliśmy właśnie pusty projekt, ale na razie do niczego nam to nie służy. Utworzenie właściwego obiektu COM też jest proste ? polega na wybraniu ikony COM Object.

Ponownie wybierz polecenie File/New/Other, a w zakładce ActiveX tym razem wybierz pozycję COM Object. Delphi wyświetli okno kreatora obiektów COM, widoczne na rysunku 13.2.

13.2.jpg
Rysunek 13.2. Kreator obiektów COM

W polu Class Name (nazwa kontrolki) wpisz XorCode. Pole Description służy do wstawienia krótkiego opisu obiektu. Możesz wpisać np. Kodowanie metodą XOR. Klikając przycisk OK, zamknij okno.

Obiekt COM został utworzony. Z menu File wybierz Save All i wpisz nazwę modułu.

Na pierwszym planie znajduje się okno edytora biblioteki typu (rysunek 13.3). Za pomocą tego edytora sterujemy obiektem COM. Wszystkie zmiany dokonane w tym edytorze znajdą odzwierciedlenie w module, lecz tym zajmiemy się nieco później.

13.3.jpg
Rysunek 13.3. Okno edytora biblioteki typu

Metody i właściwości

Tak, jak zwykły komponent posiada różne metody i właściwości, za pomocą których możemy ?sterować? ich zachowaniem, tak metody są obecne również w kontrolce COM.

Dodawanie metod

Dodawanie metod oraz właściwości jest czynnością w miarę prostą. Zadanie opiera się na umiejętnym wykorzystaniu edytora (rysunek 13.4). W gałęzi po lewej znajduje się pozycja IXorCode ? kliknij ją prawym klawiszem myszy i z menu wybierz New/Method.

13.4.jpg
Rysunek 13.4. Dodawanie nowej metody

Po wybraniu pozycji Method będziesz musiał podać nazwę naszej metody ? wpisz słowo Code, co spowoduje pojawienie się nowej gałęzi. Operowanie na nowej metodzie odbywa się poprzez zakładki, które pojawią się po prawej stronie (rysunek 13.5).

13.5.jpg
Rysunek 13.5. Zakładki służące do zmiany opcji metody

Parametry nowej metody

Ustalenia parametrów naszej funkcji oraz typu wartości przez nią zwracanej można dokonać w zakładce Parameters. Nasza kontrolka będzie służyła do kodowania tekstu metodą XOR (metodę tę opiszę nieco później) ? a zatem będzie nam potrzebna tylko jedna metoda, która nie posiada parametrów. Warto jednak wiedzieć, jak się tworzy parametry.

Parametry zaznaczonej metody prezentowane są w zakładce Parameters w ramce Parameters (rysunek 13.6).

13.6.jpg
Rysunek 13.6. Ustawianie parametru dla metody

W pierwszej kolumnie należy wpisać nazwę parametru, a w kolejnej jego typ; natomiast w trzeciej możemy określić, czy parametr ma być wejściowy czy też wyjściowy. Klikając przycisk Add, możemy dodać kolejny parametr.

Wartość zwrotna funkcji

W naszym przykładzie nie interesują nas żadne parametry funkcji, gdyż domyślnie nie są nam potrzebne. Jedyna ważna kwestia to wartość zwrotna funkcji, którą możemy ustawić za pomocą listy rozwijalnej Return Type. Rozwiń ową listę i wybierz lpstr, co oznacza wartość typu PChar.

Zapisz projekt, aby zmiany dokonane w edytorze zostały odzwierciedlone w kodzie.

System Windows był pisany w języku C, stąd na liście rozwijalnej nie można dostrzec żadnych typów charakterystycznych dla Object Pascala. W języku C nie ma takiego typu jak PChar ? można korzystać jedynie z lpstr.

Dodawanie właściwości

W naszej przykładowej kontrolce skorzystamy przy pisaniu procedury kodującej z dwóch zmiennych (właściwości) ? kodowanego łańcucha oraz hasła. Musimy mieć więc dwie właściwości.

Kliknij prawym przyciskiem myszy pozycję IXorCode i (podobnie jak w przypadku tworzenia właściwości) wybierz właściwość New/Properties. Stwórz w ten sposób dwie właściwości ? lpString oraz lpPassword. Nie przejmuj się, że zostały stworzone tym samym po dwie gałęzie dla każdej właściwości. Domyślnie zakłada się bowiem, że jedna z nich potrzebna jest do zapisywania danych do właściwości, a druga do ich odczytu. My jednak potrzebujemy właściwości ?tylko do zapisu?, więc jedną możemy bez obaw usunąć. Po zaznaczeniu konkretnej pozycji na zakładce Attributes znajdzie się pole Invoke Kind, a w nim wartość Property Put albo Property Get. Możesz usunąć klucze, w których jest Property Get, klikając prawym przyciskiem myszy element, który chcesz skasować, a następnie wybierając polecenie Delete.

Kolejnym krokiem jest ustawienie odpowiednich wartości.
#Zaznacz pozycję lpString.
#Kliknij zakładkę Parameters.
#Odszukaj pozycję Return Type i z listy wybierz void.
#Następnie ustaw parametr ? nadaj mu typ LPSTR.
To samo zrób z kluczem lpPassword.
Dzięki temu w kontrolce mamy dwie właściwości, które służyć będą tylko do zapisu. Napisałem ?właściwości?, ale w rzeczywistości w kodzie programu widnieją dwie procedury:

procedure TXorCode.Set_lpString(Value: PChar);
begin

end;

procedure TXorCode.Set_lpPassword(Value: PChar);
begin

end;

Cóż, taka jest zasada tworzenia kontrolek COM. Użytkownik wykorzystujący ową kontrolkę, chcąc nadać właściwości nową wartość, w rzeczywistości wywołuje np. procedurę Set_lpPassword, przekazując wartość w parametrze Value.

Kod źródłowy kontrolki

Zmian dokonujemy za pośrednictwem edytora, który w tle generuje kod źródłowy dla kontrolki. Oprócz standardowego kodu źródłowego kontrolki Delphi tworzy także plik z tzw. interfejsami. Plik taki można łatwo rozpoznać po końcówce _TLB, dodanej do nazwy pliku. Zajrzyj do katalogu, w którym zapisałeś projekt ? na pewno znajduje się tam plik XorCode_TLB.pas lub inny (jeżeli nadałeś inną nazwę). W listingu 13.1 zaprezentowałem kod tego pliku.

Listing 13.1. Kod źródłowy biblioteki

unit XorCode_TLB;

// ************************************************************************ //
// WARNING                                                                    
// -------                                                                    
// The types declared in this file were generated from data read from a       
// Type Library. If this type library is explicitly or indirectly (via        
// another type library referring to this type library) re-imported, or the   
// 'Refresh' command of the Type Library Editor activated while editing the   
// Type Library, the contents of this file will be regenerated and all        
// manual modifications will be lost.                                         
// ************************************************************************ //

// PASTLWTR : 1.2
// File generated on 03-02-04 15:22:45 from Type Library described below.

// ************************************************************************  //
// Type Lib: C:\Moje dokumenty\Pliki textowe\delphi_kompendium_programisty\listingi\13\XorCode\XorCode.tlb (1)
// LIBID: {356B1880-384C-11D7-A2DC-00E04CE92EC6}
// LCID: 0
// Helpfile: 
// HelpString: XorCode Library
// DepndLst: 
//   (1) v2.0 stdole, (C:\WINDOWS\SYSTEM\STDOLE2.TLB)
// ************************************************************************ //
{$TYPEDADDRESS OFF} // Unit must be compiled without type-checked pointers. 
{$WARN SYMBOL_PLATFORM OFF}
{$WRITEABLECONST ON}
{$VARPROPSETTER ON}
interface

uses Windows, ActiveX, Classes, Graphics, StdVCL, Variants;
  

// *********************************************************************//
// GUIDS declared in the TypeLibrary. Following prefixes are used:        
//   Type Libraries     : LIBID_xxxx                                      
//   CoClasses          : CLASS_xxxx                                      
//   DISPInterfaces     : DIID_xxxx                                       
//   Non-DISP interfaces: IID_xxxx                                        
// *********************************************************************//
const
  // TypeLibrary Major and minor versions
  XorCodeMajorVersion = 1;
  XorCodeMinorVersion = 0;

  LIBID_XorCode: TGUID = '{356B1880-384C-11D7-A2DC-00E04CE92EC6}';

  IID_IXorCode: TGUID = '{356B1881-384C-11D7-A2DC-00E04CE92EC6}';
  CLASS_XorCode: TGUID = '{356B1883-384C-11D7-A2DC-00E04CE92EC6}';
type

// *********************************************************************//
// Forward declaration of types defined in TypeLibrary                    
// *********************************************************************//
  IXorCode = interface;

// *********************************************************************//
// Declaration of CoClasses defined in Type Library                       
// (NOTE: Here we map each CoClass to its Default Interface)              
// *********************************************************************//
  XorCode = IXorCode;


// *********************************************************************//
// Interface: IXorCode
// Flags:     (256) OleAutomation
// GUID:      {356B1881-384C-11D7-A2DC-00E04CE92EC6}
// *********************************************************************//
  IXorCode = interface(IUnknown)
    ['{356B1881-384C-11D7-A2DC-00E04CE92EC6}']
    function Code: PChar; stdcall;
    procedure Set_lpString(Value: PChar); stdcall;
    procedure Set_lpPassword(Value: PChar); stdcall;
  end;

// *********************************************************************//
// The Class CoXorCode provides a Create and CreateRemote method to          
// create instances of the default interface IXorCode exposed by              
// the CoClass XorCode. The functions are intended to be used by             
// clients wishing to automate the CoClass objects exposed by the         
// server of this typelibrary.                                            
// *********************************************************************//
  CoXorCode = class
    class function Create: IXorCode;
    class function CreateRemote(const MachineName: string): IXorCode;
  end;

implementation

uses ComObj;

class function CoXorCode.Create: IXorCode;
begin
  Result := CreateComObject(CLASS_XorCode) as IXorCode;
end;

class function CoXorCode.CreateRemote(const MachineName: string): IXorCode;
begin
  Result := CreateRemoteComObject(MachineName, CLASS_XorCode) as IXorCode;
end;

end.

Jeśli przyjrzałeś się tym instrukcjom, kod zapewne wydał Ci się bardzo ?zagmatwany? i niezrozumiały. Przykładowo taki fragment:

IXorCode = interface(IUnknown)
    ['{356B1881-384C-11D7-A2DC-00E04CE92EC6}']
    function Code: PChar; stdcall;
    procedure Set_lpString(Value: PChar); stdcall;
    procedure Set_lpPassword(Value: PChar); stdcall;
  end;

jest deklaracją interfejsu IXorCode. O interfejsach powiem nieco później. Tymczasem przyjrzyj się listingowi 13.2., w którym znajduje się kod źródłowy kontrolki.

Nazwy interfejsów COM zaczynają się od liter I, podobnie jak nazwy klas VCL rozpoczynają się literą T.

Listing 13.3. Kod źródłowy kontrolki

unit MainFrm;

{$WARN SYMBOL_PLATFORM OFF}

interface

uses
  Windows, ActiveX, Classes, ComObj, XorCode_TLB, StdVcl;

type
  TXorCode = class(TTypedComObject, IXorCode)
  private
    FlpString : PChar;
    FlpPassword : PChar;
  protected
    function Code: PChar; stdcall;
    procedure Set_lpString(Value: PChar); stdcall;
    procedure Set_lpPassword(Value: PChar); stdcall;
    {Declare IXorCode methods here}
  end;

implementation

uses ComServ;

function TXorCode.Code: PChar;
var
  I : Integer;
  PassCount : Integer;
begin
  PassCount := 0; 
  Result := FlpString; // przypisz wartość początkową

  for I := 1 to Length(FlpString) do // wykonuj dla każdej litery osobno
  begin 
{ 
  Każdy znak zamieniaj na wartość liczbową, a następnie 
  "XOR-uj" z każdą literą hasła ? powstaje wówczas unikalna kombinacja. 
} 
   Result[i] := Chr(Ord(FlpString[i]) xor Ord(FlpPassword[PassCount]));
   Inc(PassCount);  // zwiększ licznik ? kolejna litera hasła 
   { Jeżeli licznik przekroczy długość hasła ? wyzeruj go } 
   if PassCount > Length(FlpPassword) then PassCount := 0;
  end; 

end;

procedure TXorCode.Set_lpString(Value: PChar);
begin
{ przydzielenie do zmiennej wartości podanej w parametrze Value }
  FlpString := Value;
end;

procedure TXorCode.Set_lpPassword(Value: PChar);
begin
{ przydzielenie do zmiennej wartości podanej w parametrze Value }
  FlpPassword := Value;
end;


initialization
  TTypedComObjectFactory.Create(ComServer, TXorCode, Class_XorCode,
    ciMultiInstance, tmApartment);
end.

Budowa i rejestracja kontrolki

Jeżeli cały potrzebny kod jest już gotowy, możemy przystąpić do kolejnego kroku, jakim jest skompilowanie kontrolki do postaci pliku DLL. Z menu Project wybierz pozycję Build XorCode. Cały kod zostanie skompilowany do postaci pliku *.dll ? jeżeli nie ma żadnych błędów, można przystąpić do rejestracji kontrolki.

Przed użyciem obiektu COM należy go zarejestrować w systemie. Jest to prosta czynność, polegająca na wybraniu z menu Project polecenia Register ActiveX Server. W wyniku tej operacji zobaczysz informację potwierdzającą (rysunek 13.7).

13.7.jpg
Rysunek 13.7. Okno informujące o pomyślnej rejestracji kontrolki

Rejestracja kontrolki w systemie może odbyć się także za pośrednictwem funkcji RegisterComServer. W parametrze procedury należy podać jedynie ścieżkę do pliku DLL.

Wykorzystanie obiektu COM

Aby nasz program prawidłowo wykorzystał kontrolkę COM, nie jest wcale konieczne umieszczenie pliku *.dll w katalogu z programem. Wystarczyło uprzednie zarejestrowanie kontrolki, aby informacja o ścieżce została dodana do rejestru. Do prawidłowego użytkowania obiektu COM potrzebny nam będzie jednak moduł XorCode_TLB.pas, który znajduje się w tym samym katalogu, co skompilowany obiekt COM. Skopiuj więc ten plik do katalogu, gdzie znajduje się projekt wykorzystujący kontrolkę.

Dodaj do listy uses programu moduły:

uses XorCode_TLB, ComObj;

Pełny kod źródłowy programu ? aplikacji demonstrującej użycie operacji XOR ? znajduje się w listingu 13.4.

Listing 13.4. Kod źródłowy programu wykorzystującego kontrolkę COM

unit MainFrm;

interface

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

type
  TMainForm = class(TForm)
    btnCode: TButton;
    edtValue: TEdit;
    procedure btnCodeClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

uses XorCode_TLB, ComObj;

procedure TMainForm.btnCodeClick(Sender: TObject);
var
  XorCode : IXorCode;
  lpPassword : String;
begin
{ pobierz hasło }
  lpPassword := InputBox('Podaj hasło', 'Wpisz hasło', '');

  XorCode := CreateCOMObject(CLASS_XORCODE) as IXorCode;
  if Assigned(XorCode) then
  begin
  { ustaw wartości }
    XorCode.Set_lpString(PChar(edtValue.Text));
    XorCode.Set_lpPassword(PChar(lpPassword));
    edtValue.Text := XorCode.Code;  // zakoduj
  end;

end;

end.

Zanim skorzystamy z dobrodziejstw obiektu COM, należy go utworzyć. Umożliwia to funkcja CreateCOMObject. W tym momencie możemy już wywoływać funkcję ze skompilowanej biblioteki DLL, przekazując jej potrzebne parametry (tekst do zakodowania oraz hasło). Hasło musi podać sam użytkownik programu w okienku wywoływanym za pomocą funkcji InputBox. Uruchomiony program jest przedstawiony na rysunku 13.8. Na samym początku po naciśnięciu przycisku zostaniemy poproszeni o wpisanie hasła ? ja wpisałem 123. Następnie według tego hasła tekst znajdujący się w kontrolce zostanie zakodowany. Jeśli będziesz chciał zdekodować tekst, a nie podasz hasła (123), nie uzyskasz dostępu do tekstu.

13.8.jpg
Rysunek 13.8. Zakodowany tekst

Funkcja InputBox jest zadeklarowana w module Dialogs. Dzięki niej w prosty sposób można wyświetlić okno zawierające pole edycyjne (TEdit), etykietę (TLabel) oraz dwa przyciski: OK i Anuluj. Pierwszym parametrem tej funkcji jest tekst, który ma zostać wyświetlony na pasku tytułowym okna; drugi parametr to tekst mający znaleźć się w etykiecie, a ostatni ? domyślny tekst wpisany w polu TEdit. Wartość zwracana przez ową funkcję to tekst, który użytkownik wpisał w polu edycyjnym.

Interfejsy

Z punktu widzenia Object Pascala interfejs obiektu COM jest zwykłą klasą. Można by powiedzieć, że interfejsy umożliwiają manipulację obiektem ? dają wgląd w funkcje obiektu COM i umożliwiają kontaktowanie się z kontrolką. W powyższym przykładzie interfejs wygląda tak:

  IXorCode = interface(IUnknown)
    ['{356B1881-384C-11D7-A2DC-00E04CE92EC6}']
    function Code: PChar; stdcall;
    procedure Set_lpString(Value: PChar); stdcall;
    procedure Set_lpPassword(Value: PChar); stdcall;
  end;

Charakterystyczną jego cechą jest nazwa, rozpoczynająca się od litery I (a nie od T, jak w przypadku klas Object Pascala) oraz słowo kluczowe Interface. Podobnie jak TObject stanowi podstawową klasę całego VCL, tak wszystkie interfejsy wywodzą się z klasy IUknown.

GUID

Ciekawym elementem jest dość długi numer wpisany w nawiasie kwadratowym. Jest to tzw. GUID (Globally Unique ID). Jest to 128-bitowa liczba, określająca dany interfejs. Numer ten ma postać losową i składa się z czynników, które mogą być w danym momencie unikalne ? np. aktualna data, numery określające komputer itp.

Z punktu widzenia programisty ten numer nie jest tak istotny ? nie należy się nim zbytnio przejmować.

ActiveX

ActiveX to technologia oparta na COM. Pozwala na tworzenie kontrolek .ocx lub .dll. Tak naprawdę ActiveX to obiekt COM, tyle że posiadający własny interfejs dostępny na poziomie projektowania. Wygląda to w ten sposób, że tworzony jest zwykły formularz VCL, będący w rzeczywistości kontrolką ActiveX. Można korzystać ze wszystkich komponentów i, ogólnie rzecz biorąc, projektowanie jest łatwiejsze niż w przypadku zwykłych obiektów COM.

Dodatkowo ActiveX pozwala na wygenerowanie kodu pozwalającego na umieszczenie aplikacji na stronie WWW.

Import kontrolek ActiveX

Korzystając z Delphi, możesz nawet nie wiedzieć, że w rzeczywistości korzystasz z kontrolki ActiveX. Po zaimportowaniu do Delphi taka kontrolka jest przedstawiana jak zwykły komponent i znajduje się na palecie komponentów. Przykład? Komponent TWebBrowser (paleta Internet). Komponent ten służy do wyświetlania stron WWW, ale w rzeczywistości jest to kontrolka ActiveX przeglądarki Internet Explorer. A zatem mając zainstalowaną przeglądarkę, posiadasz również kontrolkę ActiveX, którą z kolei możesz użyć w Delphi.

Przewaga kontrolek ActiveX nad obiektami COM polega między innymi na tym, że podczas projektowania kontrolek można użyć komponentów VCL oraz umieszczać je na palecie komponentów.

Na dołączonej do książki płycie CD-ROM znajdziesz kontrolkę ActiveX o nazwie VTextProj.ocx. Spróbujmy zaimportować ją do palety komponentów.

  1. Z menu Component wybierz Import ActiveX Control (rysunek 13.9).

13.9.jpg
Rysunek 13.9. Importowanie kontrolki ActiveX

  1. Naciśnij przycisk Add; wskaż w oknie kontrolkę ActiveX, którą chcesz importować.
  2. Na liście pojawi się nowa pozycja. Naciśnij przycisk Install.
  3. Zostaniesz poproszony o wskazanie pakietu, w którym zostanie umieszczony ?komponent? (rysunek 13.10). Wybierz OK, akceptując wprowadzone dane.

13.10.jpg
Rysunek 13.10. Wskazanie pakietu

  1. Następnie zostaniesz zapytany, czy chcesz skompilować pakiet (rysunek 13.11). Zatwierdź przyciskiem OK.

13.11.jpg
Rysunek 13.11. Kontynuacja instalacji

Po tym zabiegu kontrolka powinna zostać zainstalowana w palecie komponentów. Zapisz zmiany i utwórz nowy projekt; komponent znajduje się już na palecie ActiveX.

Wykorzystanie kontrolki TVText

Nie powiedziałem jeszcze, do czego służy kontrolka, którą właśnie importowałeś. Otóż umożliwia ona wyświetlanie napisów do filmów. Jeżeli lubisz filmy, to zapewne wiesz, że wiele odtwarzaczy umożliwia wyświetlanie napisów w przypadku, gdy film jest w innej wersji językowej (rysunek 13.12).

Tworzeniem takiej kontrolki krok po kroku zajmiemy się w dalszej części tego rozdziału, a tymczasem możesz przyjrzeć się listingowi 13.5. w którym znajduje się program korzystający z tej kontrolki

13.12.jpg
Rysunek 13.12. Kontrolka w trakcie działania!

Listing 13.5. Kod źródłowy programu wykorzystującego kontrolkę TVText

{
  Copyright (c) 2002 by Adam Boduch <[email protected]>
}

unit MainFrm;

interface

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

type
  TMainForm = class(TForm)
    VText: TVText;
    btnLoad: TButton;
    OpenDialog: TOpenDialog;
    btnStop: TButton;
    procedure btnLoadClick(Sender: TObject);
    procedure btnStopClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

procedure TMainForm.btnLoadClick(Sender: TObject);
begin
  if OpenDialog.Execute then
  begin
    VText.Stop; // zatrzymanie, jeżeli teraz coś jest odtwarzane...
    btnStop.Enabled := True;
    VText.Start(PChar(OpenDialog.FileName)); // wywołanie procedury
  end;
end;

procedure TMainForm.btnStopClick(Sender: TObject);
begin
  VText.Stop;
  btnStop.Enabled := False;
end;

procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  VText.Stop; // zatrzymanie, jeżeli użytkownik chce zamknąć program
end;

end.

Wykorzystanie takiego ?komponentu? w programie jest niezwykle proste. Działanie tego obiektu jest przed nami ukryte ? o to nie musimy się martwić. Jedyne, co nas interesuje, to dwie metody: Start i Stop. W pierwszej należy podać ścieżkę do pliku tekstowego z napisami, co spowoduje rozpoczęcie działania. Druga metoda ? Stop ? zatrzyma działanie obiektu.

Tworzenie kontrolek ActiveX

Tworzenie obiektów ActiveX jest równie proste jak tworzenie obiektów COM. Jak już mówiłem, podczas projektowania kontrolek ActiveX możemy używać wizualnej biblioteki komponentów (VCL).

Proces ten jest podobny do tworzenia obiektów COM. Z menu File wybierz polecenie New, a następnie Other. Kliknij zakładkę ActiveX, wybierz ikonę ActiveX Form. Na rysunku 13.13 przedstawione jest okno, które zostanie wyświetlone w wyniku tej operacji.

13.13.jpg
Rysunek 13.13. Tworzenie nowej kontrolki ActiveX

W polu New ActiveX Name wpisz ActiveXTest. Pole Implementation Unit niech zawiera wartość ActiveXTestFrm.pas, a Project Name: ActiveXTestProj.dpr. Naciśij OK, a Delphi utworzy kontrolkę ActiveX i wyświetli formularz.

Z menu File wybierz polecenie Save All. Podczas zapisywania domyślną nazwą pliku, jaką zaproponuje Delphi, będzie ta, którą wpisałeś w oknie tworzenia kontrolki ActiveX.

Katalog, który wybrałeś, wzbogacił się o parę nowych plików. Oprócz standardowych plików znajduje się tam również plik ActiveXTestProj_TLB.pas. Plik ten zawiera interfejsy COM.

Przykład: wyświetlanie napisów do filmu

Napiszmy przykładową kontrolkę ActiveX, która pozwoli Ci zrozumieć istotę działania tych obiektów. Nasza przykładowa kontrolka będzie umożliwiać wyświetlanie napisów do filmów.

Na samym początku procesu tworzenia kontrolki musimy, tak jak w przypadku kontrolek COM, stworzyć dwie metody ? Start i Stop.

Tworzenie interfejsu COM

Stwórz nową kontrolkę ActiveX. Postępuj tak, jak w przypadku ćwiczenia poprzedniego ? w polu New ActiveX Name kreatora wpisz VText. W polu Implementation Unit wpisz VTextFrm.pas, a w polu Project Name ? VTextProj.dpr.

Zapisz cały projekt. W katalogu z kontrolką znajduje się plik VTextProj_TLB.pas. Domyślnie ten plik nie jest otwarty w projekcie. Otwórz go zatem ? w edytorze kodu utworzona zostanie nowa zakładka, a na pierwszy plan wysunie się edytor biblioteki.

Kliknij prawym przyciskiem myszy pozycję IVText i z menu podręcznego wybierz New/Method. Metodę tę nazwij Start. Kliknij zakładkę Parameters. Będziesz musiał dodać nowy parametr dla funkcji. Doprowadź parametr do takiej postaci, jaką przedstawiono na rysunku 13.14.

13.14.jpg
Rysunek 13.14. Ustawianie parametrów metody

Stwórz teraz metodę Stop i powtórz cały proces. Nasza procedura nie będzie zawierała żadnych parametrów ? nie musisz zatem robić nic więcej. Zapisz cały projekt.

Tworzenie kontrolki ActiveX

Jak już mówiłem, nasza kontrolka wyświetlać będzie napisy do filmów. Wiele odtwarzaczy multimedialnych oferuje możliwość załadowania napisów do filmu, który akurat oglądamy w innej wersji językowej. Nasza kontrolka będzie uwzględniała plik z napisami, którego poszczególne wiersze zapisane są w ten sposób:

<tt....
0046:Proszę.
0053:Pan musi być naprawdę ważny...
...</tt>

Przed poszczególnymi kwestiami aktorów umieszczony jest czas od rozpoczęcia filmu, w jakim powinien zostać wyświetlony napis. Kontrolka w momencie wywołania metody Start zaczyna działać w pętli while. Zresztą zaraz przekonasz się, jak to będzie wyglądać?

Strona wizualna

#Przede wszystkim otwórz plik VTextFrm (jeżeli jeszcze nie jest otwarty w projekcie).
#Naciskając klawisz F12, przełącz się do formularza. Zmniejsz ten formularz, dostosowując go do własnych wymagań.
#Umieść na formularzu komponent TPanel, a jego właściwości Align nadaj wartość alClient.
#Właściwość BevelInner zmień na bvLowered, a sam komponent nazwij pnlMessage.
#Na komponencie pnlMessage umieść obiekt TLabel i zmień wartość właściwości Align na alBottom; komponent nazwij lblCurrent.
Oto cały proces projektowania kontrolki od strony wizualnej! Kolejnym krokiem jest tworzenie samego kodu kontrolki.

Kod źródłowy kontrolki

Przełącz się do kodu pliku VTextFrm. Odszukaj sekcję Private i dodaj następujące wiersze kodu:

    
    FLines : TStringList; // zmienna przechowuje napisy
    FTime, FText : TArray; // tablica ? czas oraz napis
    FBroken : WordBool; // określa, czy proces jest uruchomiony
    procedure PrepareText; // przygotuj (przeanalizuj) plik tekstowy

Skorzystałem tutaj z nowego typu danych ? tablicy dynamicznej TArray. Dodaj deklaracje tego typu w sekcji Type:

TArray = array of String;

Procedura PrepareText, którą zadeklarowaliśmy w sekcji private, służy do przygotowania pliku tekstowego. Zaraz po wywołaniu metody Startdo pola FLines zostaje odczytany plik z napisami. Procedura PrepareText ma oddzielić z każdego wiersza czas, w którym napis ma być wyświetlony, oraz samą treść napisu.

procedure TVText.PrepareText;
var
  i : Integer;
begin
{ określ wielkość tablicy na podstawie ilości wierszy }
  SetLength(FTime, FLines.Count);
  SetLength(FText, FLines.Count);

  { pętla po wszystkich wierszach... }
  for I := 0 to FLines.Count ?1 do
  begin
  { do tego elementu tablicy przypisz czas, w którym powinien zostać wyświetlony napis }
    FTime[i] := (Copy(FLines[i], 1, 8));
  { tutaj przypisz samą treść }
    FText[i] := (Copy(FLines[i], 10, Length(FLines[i]) ? 8));
  end;
end;

Dzięki temu mamy gotowe do użytku tablice FTime oraz FText. Teraz jedyny problem stanowi wyświetlenie odpowiedniego elementu tablicy w odpowiednim czasie. Odpowiada za to metoda Start.

procedure TVText.Start(FileName: PChar);
var
  Counter : Integer; // licznik ? ile już napisów zostało wyświetlonych
  FPause : Integer; // czas wyświetlania napisu
  CurrentTime : TTime; // czas odtwarzania filmu
  wHour, wMin, wSec : Integer;
begin
  FLines := TStringList.Create;
  FLines.LoadFromFile(FileName); // załaduj plik tekstowy

  PrepareText; // przygotuj dwie tablice
  FBroken := False;

  Counter := ?1;
  FPause := 0;
  wHour := 0;
  wMin := 0;
  wSec := 0;

  { pętla wyświetlana co 1000 milisekund, dopóki zmienna FBroken nie ma wartości False }
  while (not FBroken) or (not Application.Terminated) do
  begin
    Application.ProcessMessages;
    Sleep(1000);  // odczekaj 1 sek.

    if FBroken then Break; // jeżeli zmienna = TRUE, przerwij działanie

    Inc(wSec); // zwiększ liczbę sekund
    if wSec >= 60 then // jeżeli liczba sekund jest większa od 60...
    begin
      Inc(wMin); // zwiększ liczbę minut
      wSec := 0; // wyzeruj zmienną
    end;

    if wMin > 60 then // jeżeli liczba minut jest większa do 60
    begin
      Inc(wHour); // zwiększ liczbę godzin
      wMin := 0; // wyzeruj minuty
    end;

    // na podstawie danych utwórz zmienną TTime
    CurrentTime := EncodeTime(wHour, wMin, wSec, 0);
    lblCurrent.Caption := TimeToStr(CurrentTime);
    


    if AnsiMatchStr(TimeToStr(CurrentTime), FTime) then
    begin
      Inc(Counter);
      pnlMessage.Caption := FText[Counter];
      FPause := 0;
    end else
    begin
      if Length(pnlMessage.Caption) > 0 then
      begin
        Inc(FPause);
        
        if FPause = 5 then
        begin
          FPause := 0;
          pnlMessage.Caption := '';
        end;
      end;
    end;
  end;
end;

Po załadowaniu napisów i wywołaniu procedury PrepareText mamy gotowe tablice. Pętla, która jest wykonywana w odstępie 1 sekundy, za każdym razem zwiększa liczbę sekund, a następnie minut (jeżeli liczba sekund osiągnie 60) itd. Następnie ? za pomocą funkcji EncodeTime i dzięki zmiennym wHour, wMin, wSec ? możemy skonstruować typ TTime. Funkcja AnsiMatchStr sprawdza, czy dany wartość CurrentTime znajduje się w tablicy FTime.

Funkcja AnsiMatchStr znajduje się w module StrUtils.pas. Aby wszystko mogło działać, musisz ten moduł dodać do listy uses.

Nastąpi wówczas wyświetlenie tekstu z tablicy FText. Jednym problem jest rozróżnienie, który element tablicy powinien być w tym momencie wyświetlony. Aby go rozwiązać, należy wprowadzić zmienną Counter, która zwiększy się o jeden za każdym razem, gdy napis zostanie wyświetlony.

W dokumentacji Delphi znajduje się błąd ? funkcja AnsiMatchStr wcale nie zwraca liczby w postaci Integer, jak zostało to napisane. W rzeczywistości zwraca True, jeżeli element został znaleziony, lub ? w przeciwnym wypadku ? False.

Pozostało jeszcze napisanie metody Stop. Procedura ta będzie służyć do wstrzymywania całego procesu. Jej kod jest prosty:

procedure TVText.Stop;
begin
  FBroken := True;
end;

Zmiana wartości zmiennej FBroken na True powoduje zatrzymanie działania pętli While. Pełny kod źródłowy modułu przestawiony jest w listingu 13.6.

Listing 13.6. Kod źródłowy kontrolki

{
  Copyright (c) 2002 by Adam Boduch <[email protected]>
}

unit VTextFrm;

{$WARN SYMBOL_PLATFORM OFF}

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ActiveX, AxCtrls, VTextProj_TLB, StdVcl, StdCtrls, ExtCtrls, StrUtils;

type
  TArray = array of String;
  
  TVText = class(TActiveForm, IVText)
    pnlMessage: TPanel;
    lblCurrent: TLabel;
  private
    { Private declarations }
    FEvents: IVTextEvents;

    FLines : TStringList; // zmienna przechowuje napisy
    FTime, FText : TArray; // tablica ? czas oraz napis
    FBroken : WordBool; // określa, czy proces jest uruchomiony
    procedure PrepareText; // przygotuj (przeanalizuj) plik tekstowy

    procedure ActivateEvent(Sender: TObject);
    procedure ClickEvent(Sender: TObject);
    procedure CreateEvent(Sender: TObject);
    procedure DblClickEvent(Sender: TObject);
    procedure DeactivateEvent(Sender: TObject);
    procedure DestroyEvent(Sender: TObject);
    procedure KeyPressEvent(Sender: TObject; var Key: Char);
    procedure PaintEvent(Sender: TObject);
  protected
    { Protected declarations }
    procedure DefinePropertyPages(DefinePropertyPage: TDefinePropertyPage); override;
    procedure EventSinkChanged(const EventSink: IUnknown); override;
    function Get_Active: WordBool; safecall;
    function Get_AlignDisabled: WordBool; safecall;
    function Get_AutoScroll: WordBool; safecall;
    function Get_AutoSize: WordBool; safecall;
    function Get_AxBorderStyle: TxActiveFormBorderStyle; safecall;
    function Get_Caption: WideString; safecall;
    function Get_Color: OLE_COLOR; safecall;
    function Get_DoubleBuffered: WordBool; safecall;
    function Get_DropTarget: WordBool; safecall;
    function Get_Enabled: WordBool; safecall;
    function Get_Font: IFontDisp; safecall;
    function Get_HelpFile: WideString; safecall;
    function Get_KeyPreview: WordBool; safecall;
    function Get_PixelsPerInch: Integer; safecall;
    function Get_PrintScale: TxPrintScale; safecall;
    function Get_Scaled: WordBool; safecall;
    function Get_ScreenSnap: WordBool; safecall;
    function Get_SnapBuffer: Integer; safecall;
    function Get_Visible: WordBool; safecall;
    function Get_VisibleDockClientCount: Integer; safecall;
    procedure _Set_Font(var Value: IFontDisp); safecall;
    procedure Set_AutoScroll(Value: WordBool); safecall;
    procedure Set_AutoSize(Value: WordBool); safecall;
    procedure Set_AxBorderStyle(Value: TxActiveFormBorderStyle); safecall;
    procedure Set_Caption(const Value: WideString); safecall;
    procedure Set_Color(Value: OLE_COLOR); safecall;
    procedure Set_DoubleBuffered(Value: WordBool); safecall;
    procedure Set_DropTarget(Value: WordBool); safecall;
    procedure Set_Enabled(Value: WordBool); safecall;
    procedure Set_Font(const Value: IFontDisp); safecall;
    procedure Set_HelpFile(const Value: WideString); safecall;
    procedure Set_KeyPreview(Value: WordBool); safecall;
    procedure Set_PixelsPerInch(Value: Integer); safecall;
    procedure Set_PrintScale(Value: TxPrintScale); safecall;
    procedure Set_Scaled(Value: WordBool); safecall;
    procedure Set_ScreenSnap(Value: WordBool); safecall;
    procedure Set_SnapBuffer(Value: Integer); safecall;
    procedure Set_Visible(Value: WordBool); safecall;
    procedure Start(FileName: PChar); safecall;
    procedure Stop; safecall;
  public
    { Public declarations }
    procedure Initialize; override;
  end;

implementation

uses ComObj, ComServ;

{$R *.DFM}

{ TVText }

procedure TVText.DefinePropertyPages(DefinePropertyPage: TDefinePropertyPage);
begin
  { Define property pages here.  Property pages are defined by calling
    DefinePropertyPage with the class id of the page.  For example,
      DefinePropertyPage(Class_VTextPage); }
end;

procedure TVText.EventSinkChanged(const EventSink: IUnknown);
begin
  FEvents := EventSink as IVTextEvents;
  inherited EventSinkChanged(EventSink);
end;

procedure TVText.Initialize;
begin
  inherited Initialize;
  OnActivate := ActivateEvent;
  OnClick := ClickEvent;
  OnCreate := CreateEvent;
  OnDblClick := DblClickEvent;
  OnDeactivate := DeactivateEvent;
  OnDestroy := DestroyEvent;
  OnKeyPress := KeyPressEvent;
  OnPaint := PaintEvent;
end;

function TVText.Get_Active: WordBool;
begin
  Result := Active;
end;

function TVText.Get_AlignDisabled: WordBool;
begin
  Result := AlignDisabled;
end;

function TVText.Get_AutoScroll: WordBool;
begin
  Result := AutoScroll;
end;

function TVText.Get_AutoSize: WordBool;
begin
  Result := AutoSize;
end;

function TVText.Get_AxBorderStyle: TxActiveFormBorderStyle;
begin
  Result := Ord(AxBorderStyle);
end;

function TVText.Get_Caption: WideString;
begin
  Result := WideString(Caption);
end;

function TVText.Get_Color: OLE_COLOR;
begin
  Result := OLE_COLOR(Color);
end;

function TVText.Get_DoubleBuffered: WordBool;
begin
  Result := DoubleBuffered;
end;

function TVText.Get_DropTarget: WordBool;
begin
  Result := DropTarget;
end;

function TVText.Get_Enabled: WordBool;
begin
  Result := Enabled;
end;

function TVText.Get_Font: IFontDisp;
begin
  GetOleFont(Font, Result);
end;

function TVText.Get_HelpFile: WideString;
begin
  Result := WideString(HelpFile);
end;

function TVText.Get_KeyPreview: WordBool;
begin
  Result := KeyPreview;
end;

function TVText.Get_PixelsPerInch: Integer;
begin
  Result := PixelsPerInch;
end;

function TVText.Get_PrintScale: TxPrintScale;
begin
  Result := Ord(PrintScale);
end;

function TVText.Get_Scaled: WordBool;
begin
  Result := Scaled;
end;

function TVText.Get_ScreenSnap: WordBool;
begin
  Result := ScreenSnap;
end;

function TVText.Get_SnapBuffer: Integer;
begin
  Result := SnapBuffer;
end;

function TVText.Get_Visible: WordBool;
begin
  Result := Visible;
end;

function TVText.Get_VisibleDockClientCount: Integer;
begin
  Result := VisibleDockClientCount;
end;

procedure TVText._Set_Font(var Value: IFontDisp);
begin
  SetOleFont(Font, Value);
end;

procedure TVText.ActivateEvent(Sender: TObject);
begin
  if FEvents <> nil then FEvents.OnActivate;
end;

procedure TVText.ClickEvent(Sender: TObject);
begin
  if FEvents <> nil then FEvents.OnClick;
end;

procedure TVText.CreateEvent(Sender: TObject);
begin
  if FEvents <> nil then FEvents.OnCreate;
end;

procedure TVText.DblClickEvent(Sender: TObject);
begin
  if FEvents <> nil then FEvents.OnDblClick;
end;

procedure TVText.DeactivateEvent(Sender: TObject);
begin
  if FEvents <> nil then FEvents.OnDeactivate;
end;

procedure TVText.DestroyEvent(Sender: TObject);
begin
  if FEvents <> nil then FEvents.OnDestroy;
end;

procedure TVText.KeyPressEvent(Sender: TObject; var Key: Char);
var
  TempKey: Smallint;
begin
  TempKey := Smallint(Key);
  if FEvents <> nil then FEvents.OnKeyPress(TempKey);
  Key := Char(TempKey);
end;

procedure TVText.PaintEvent(Sender: TObject);
begin
  if FEvents <> nil then FEvents.OnPaint;
end;

procedure TVText.Set_AutoScroll(Value: WordBool);
begin
  AutoScroll := Value;
end;

procedure TVText.Set_AutoSize(Value: WordBool);
begin
  AutoSize := Value;
end;

procedure TVText.Set_AxBorderStyle(Value: TxActiveFormBorderStyle);
begin
  AxBorderStyle := TActiveFormBorderStyle(Value);
end;

procedure TVText.Set_Caption(const Value: WideString);
begin
  Caption := TCaption(Value);
end;

procedure TVText.Set_Color(Value: OLE_COLOR);
begin
  Color := TColor(Value);
end;

procedure TVText.Set_DoubleBuffered(Value: WordBool);
begin
  DoubleBuffered := Value;
end;

procedure TVText.Set_DropTarget(Value: WordBool);
begin
  DropTarget := Value;
end;

procedure TVText.Set_Enabled(Value: WordBool);
begin
  Enabled := Value;
end;

procedure TVText.Set_Font(const Value: IFontDisp);
begin
  SetOleFont(Font, Value);
end;

procedure TVText.Set_HelpFile(const Value: WideString);
begin
  HelpFile := String(Value);
end;

procedure TVText.Set_KeyPreview(Value: WordBool);
begin
  KeyPreview := Value;
end;

procedure TVText.Set_PixelsPerInch(Value: Integer);
begin
  PixelsPerInch := Value;
end;

procedure TVText.Set_PrintScale(Value: TxPrintScale);
begin
  PrintScale := TPrintScale(Value);
end;

procedure TVText.Set_Scaled(Value: WordBool);
begin
  Scaled := Value;
end;

procedure TVText.Set_ScreenSnap(Value: WordBool);
begin
  ScreenSnap := Value;
end;

procedure TVText.Set_SnapBuffer(Value: Integer);
begin
  SnapBuffer := Value;
end;

procedure TVText.Set_Visible(Value: WordBool);
begin
  Visible := Value;
end;

procedure TVText.Start(FileName: PChar);
var
  Counter : Integer; // licznik ? ile już napisów zostało wyświetlonych
  FPause : Integer; // czas wyświetlania napisu
  CurrentTime : TTime; // czas odtwarzania filmu
  wHour, wMin, wSec : Integer;
begin
  FLines := TStringList.Create;
  FLines.LoadFromFile(FileName); // załaduj plik tekstowy

  PrepareText; // przygotuj dwie tablice
  FBroken := False;

  Counter := ?1;
  FPause := 0;
  wHour := 0;
  wMin := 0;
  wSec := 0;

  { pętla wyświetlana co 1000 milisekund dopóki zmienna FBroken nie ma wartości False }
  while (not FBroken) or (not Application.Terminated) do
  begin
    Application.ProcessMessages;
    Sleep(1000);  // odczekaj 1 sek.

    if FBroken then Break; // jeżeli zmienna = TRUE, przerwij działanie

    Inc(wSec); // zwiększ liczbę sekund
    if wSec >= 60 then // jeżeli liczba sekund jest większa od 60...
    begin
      Inc(wMin); // zwiększ liczbę minut
      wSec := 0; // wyzeruj zmienną
    end;

    if wMin > 60 then // jeżeli liczba minut jest większa od 60
    begin
      Inc(wHour); // zwiększ liczbę godzin
      wMin := 0; // wyzeruj minuty
    end;

    // na podstawie danych utwórz zmienną TTime
    CurrentTime := EncodeTime(wHour, wMin, wSec, 0);
    lblCurrent.Caption := TimeToStr(CurrentTime);



    if AnsiMatchStr(TimeToStr(CurrentTime), FTime) then
    begin
      Inc(Counter);
      pnlMessage.Caption := FText[Counter];
      FPause := 0;
    end else
    begin
      if Length(pnlMessage.Caption) > 0 then
      begin
        Inc(FPause);
        
        if FPause = 5 then
        begin
          FPause := 0;
          pnlMessage.Caption := '';
        end;
      end;
    end;
  end;
end;

procedure TVText.Stop;
begin
  FBroken := True;
end;

procedure TVText.PrepareText;
var
  i : Integer;
begin
{ określ wielkość tablicy na podstawie ilości wierszy }
  SetLength(FTime, FLines.Count);
  SetLength(FText, FLines.Count);

  { pętla po wszystkich wierszach... }
  for I := 0 to FLines.Count ?1 do
  begin
  { do tego elementu tablicy przypisz czas, w którym powinien zostać wyświetlony napis }
    FTime[i] := (Copy(FLines[i], 1, 8));
  { tutaj przypisz samą treść }
    FText[i] := (Copy(FLines[i], 10, Length(FLines[i]) ? 8));
  end;
end;

initialization
  TActiveFormFactory.Create(
    ComServer,
    TActiveFormControl,
    TVText,
    Class_VText,
    1,
    '',
    OLEMISC_SIMPLEFRAME or OLEMISC_ACTSLIKELABEL,
    tmApartment);
end.

W odróżnieniu od obiektów COM kontrolki ActiveX posiadają dodatkowe metody, takie jak zwykłe formularze (generowane automatycznie przez Delphi). Dzięki temu po umieszczeniu kontrolki ActiveX na formularzu nasz nowy komponent będzie posiadać standardowe metody oraz zdarzenia zwykłego formularza ? stąd wynika obszerność kodu źródłowego.

Budowa i rejestracja

Nasza kontrolka jest już gotowa. Po wybraniu polecenia Build z menu Project kod źródłowy zostanie skompilowany i przekształcony w plik .ocx. Jeżeli projekt zawiera jakieś błędy, informajce o tym znajdziesz w oknie wiadomości (ang. Message View).

Po wybraniu polecenia Register ActiveX Server z menu Run kontrolka zostanie zarejestrowana w systemie. Instalację kontrolek ActiveX omówiłem już w podpunkcie ?Import kontrolek ActiveX?.

ActiveX w Internecie

Istnieje możliwość publikowania tworzonych przez siebie kontrolek w Internecie na stronie WWW. Niepotrzebna jest nawet do tego większa znajomość języka HTML. Delphi wygeneruje nawet potrzebną stronę WWW, którą wystarczy tylko umieścić gdzieś na serwerze.

Względy bezpieczeństwa

Używanie kontrolek ActiveX w Internecie nie jest zbyt popularne. Wiele użytkowników ze względu na niebezpieczeństwo wynikające z korzystania ActiveX ma wyłączoną opcję ich ładowania (zdaje się, że jest to domyślne ustawienie). Z tego względu przy próbie załadowania kontrolki zobaczą tylko taki komunikat, jak na rysunku 13.15.

13.15.jpg
Rysunek 13.15. Komunikat informujący o niemożności obsłużenia ActiveX

Przykładowa kontrolka

W tym punkcie zaprezentuję możliwości publikowania własnych kontrolek. Z tego też względu obiekt, który teraz stworzymy, będzie bardzo prosty.

Tworzenie kontrolki ActiveX

Utwórz nową kontrolkę i nazwij ją ActiveWWW. W polu Implementation Unit wpisz ActiveWWWFrm.pas, a w polu Project Name ? ActiveWWWProj.dpr.

Nasza kontrolka będzie w pętli wyświetlać napis z wykorzystaniem efektu maszyny do pisania, czyli literka po literce. Na formularzu umieść komponent TGroupBox, a na nim TLabel. Rozciągnij etykietę na całą szerokość TGroupBox i zmień właściwość AutoSize na False.

Umieść dodatkowo dwa przyciski. Jeden będzie służył do rozpoczęcia animacji, a drugi do jej zatrzymania.

var FBroken : Boolean;

procedure TActiveWWW.btnGoClick(Sender: TObject);
const
  ExMsg = 'To jest przykładowa kontrolka :?)';
var
  i : Integer;
begin
  FBroken := False;
{ pętla while wykonywana przez cały czas trwania programu }
  while (not Application.Terminated) or (not FBroken) do
  begin
    lblMessage.Caption := '';
  { pętla for powoduje wyświetlenie na etykiecie kolejnych liter }
    for i := 1 to Length(ExMsg) do
    begin
      Application.ProcessMessages;
      if FBroken then Break;
      Sleep(100);
      lblMessage.Caption := lblMessage.Caption + ExMsg[i];
    end;
  end;
end;

procedure TActiveWWW.btnStopClick(Sender: TObject);
begin
  FBroken := True;
end;

Wykorzystaliśmy efekt maszyny do pisania, polegający na wyświetlaniu przykładowego napisu litera po literze.

Publikowanie kontrolki

Do opublikowania kontrolki w sieci posłużymy się dwoma poleceniami z menu Project: Web Deployment Options oraz Web Deploy. Na samym początku wybierz pierwsze polecenie, aby określić opcje publikacji (rysunek 13.16).

13.16.jpg
Rysunek 13.16. Okno Web Deployment Options

W oknie tym musimy podać parę informacji, które są potrzebne do zbudowania kontrolki. Załóżmy, że będzie ona uruchamiana na lokalnym serwerze Apache.

Pierwsze pole Target dir okna Web Deployment Options musi zawierać ścieżkę do katalogu, w którym kontrolka zostanie umieszczona po zbudowaniu. W kolejnym polu (Target URL) wpisz adres URL, który będzie prowadził do odpowiedniej strony ? ja wpisałem http://localhost. Ostatnie pole (HTML Dir) określa ścieżkę, w której wygenerowany zostanie odpowiedni plik HTML. Ja wpisałem tę samą wartość, co w pozycji Target Dir.

To właściwie wszystko. Zamknij okno przyciskiem OK. Wybierz z menu Project pozycję Web Deploy. Kontrolka powinna zostać skompilowana i zapisana w wybranym przez Ciebie katalogu.

W oknie Web Deployment Options możesz zaznaczyć opcję Use CAB file compression. Dzięki temu skompilowana kontrolka ActiveX zostanie skompresowana w pliku *.cab.

Po tym zabiegu plik ActiveWWWProj.htm zawiera treść przedstawioną w listingu 13.7.

Listing 13.7. Kod HTML strony wygenerowanej przez Delphi

<HTML>
<H1> Delphi 7 ActiveX Test Page </H1><p>
You should see your Delphi 7 forms or controls embedded in the form below.
<HR><center><P>
<OBJECT
    classid="clsid:6013039B-7D1B-4DAE-98D2-232733529810"
    codebase="http://localhost/ActiveWWWProj.ocx#version=1,0,0,0"
    width=350
    height=250
    align=center
    hspace=0
    vspace=0
>
</OBJECT>
</HTML>

Aby kontrolka ActiveX była lepiej wyświetlana, nadałem szerokości i wysokości obiektu takie wartości:

<tt> width=400
height=150</tt>

Rezultat działania programu możesz zaobserwować na rysunku 13.17.

13.17.jpg
Rysunek 13.17. Kontrolka ActiveX w działaniu

Pełen kod źródłowy powyższej kontrolki możesz znaleźć na płycie CD-ROM w katalogu ../listingi/13/WebActiveX/ActiveWWWProj.dpr

Podsumowanie

W tym rozdziale pokazałem, w jaki sposób można tworzyć obiekty COM oraz kontrolki ActiveX. Należy to może do bardziej zaawansowanych czynności programistycznych, ale może się przydać ? chociażby do dynamicznego tworzenia stron WWW wzbogaconych o kontrolki ActiveX.

Załączniki:

de25kp.jpg Więcej informacji

Delphi 2005. Kompendium programisty
Adam Boduch
Format: B5, stron: 1048
oprawa twarda
Zawiera CD-ROM
[[Delphi/Kompendium|Spis treści]]

[[Delphi/Kompendium/Prawa autorskie|©]] Helion 2003. Autor: Adam Boduch. Zabrania się rozpowszechniania tego tekstu bez zgody autora.

1 komentarz

Nalezaloby ujednolicic terminologie w tym tekscie. Zamiennie stosowalem pojecia zmienna/pole oraz metoda/procedura. Jest to niedopowiedzenie.