Zurueck zum Inhaltsverzeichnis
ActiveX / OLE
OLE steht
für
Object
Linking
and Embedding ("Objekte
verknüpfen
und einbetten"). Die ursprünglich mit OLE bezeichnete Technologie
befindet sich in einer ständigen Weiterentwicklung. Die
weiterführenden
Elemente von OLE - also alles, was über das Verknüpfen und
Einbetten
hinausgeht - gehört jetzt zur Active Technology.
Folgende Techniken
gehören inzwischen zu OLE / Active
Technology: |
Die sogenannten
ActiveX-Steuerelemente
sind die aktuelle Form der OLE-Steuerelemente. Es handelt sich hierbei
um wiederverwendbare Softwarekomponenten, die OLE-Funktionen
unterstützen.
In MSDN findet man folgende Definition:
ActiveX: A set of technologies that enables software components to interact with one another in a networked environment, regardless of the language in which they were created. ActiveX™ is built on the Component Object Model (COM). |
ActiveX dient also
dazu,
daß sich Softwarekomponenten (auch über Netzwerke)
miteinander
verstehen, selbst wenn sie in verschiedenen Programmiersprachen
erstellt
wurden. Die Grundlage von ActiveX ist COM. In MSDN findet man hierzu
folgendes:
COM
(Component Object Model): An open architecture for cross-platform development of client/server applications based on object-oriented technology. Clients have access to an object through interfaces implemented on the object. COM is language neutral, so any language that produces ActiveX™ components can also produce COM applications. |
Der Kern dieser Technik ist also die Objektorientierung / Wiederverwendbarkeit und die Unabhängigkeit von Programmiersprachen und Betriebssystemen. Die Softwarekomponenten greifen objektorientiert über definierte Schnittstellen (engl.: interface) aufeinander zu.
Die OLE-Klassen der MFC umhüllen und vereinfachen COM für den Programmierer. Ein ActiveX-Steuerelement (ActiveX control) kann ein Server sein, der in einem OLE-Container (Client) verwendet wird. Die Interaktion der ActiveX-Komponente mit dem Container erfolgt in der MFC-Programmierung z.B. über die von CWnd abgeleitete Control-Klasse COleControl:
CObject --> CCmdTarget --> CWnd --> COleControl
COleControl ist eine Control-Klasse wie z.B. CButton, CEdit oder CProgressCtrl.
Originalton MSDN:
The COleControl class is a
powerful
base class for developing OLE controls. Derived from CWnd, this class
inherits
all the functionality of a Windows window object plus additional
functionality
specific to OLE, such as event firing and the ability
to
support
methods and properties. OLE controls can be inserted into OLE
container
applications and communicate with the container by using a two-way
system
of event firing and exposing methods and properties to the container.
Note
that standard OLE containers only support the basic functionality of an
OLE control. They are unable to support extended features of an OLE
control.
Event firing occurs when events are sent to the container as a result
of
certain actions taking place in the control. In turn, the container
communicates
with the control by using an exposed set of methods and properties
analogous
to the member functions and data members of a C++ class. This approach
allows the developer to control the appearance of the control and
notify
the container when certain actions occur.
Der Client, hier eine mit MFC erstellte Anwendung, greift also z.B. über COleControl auf die umhüllende Klasse der integrierten ActiveX-Komponente zu. Vereinfachend kann man sagen, daß ActiveX-Komponenten COM-basierte Objekte sind, die sich selbst in ein eigenes Fenster zeichnen können, auf Ereignisse (z.B. Mausklick, Tastatur) reagieren und an ihrer Schnittstelle über Eigenschaften und Methoden verfügen, die eine Interaktion zulassen.
Solange wir noch keine eigenen ActiveX-Komponenten erstellen wollen, reicht dieses grundlegende Verständnis aus.
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Wie verwendet man ActiveX-Komponenten nun praktisch in der Windows-Programmierung mit MFC?
Für den praktischen Einstieg hilft ein Beispiel unter Verwendung einer vorhandenen ActiveX-Komponente:
Erstellen Sie bitte mit dem Assistenten wie gewohnt eine MFC-Dialog-Anwendung. Wir wollen nun eine vorhandene ActiveX-Komponente in unsere Anwendung einfügen. Wie macht man dies?
Sie befinden sich in
der
Ressourcen-Ansicht und wählen im Menü:
Projekt - Dem Projekt
hinzufügen - Komponenten und Steuerelemente
Dort öffnen Sie den Ordner "Registered ActiveX Controls".
Suchen Sie dort bitte "Microsoft
MonthView Control, version 6.0".
Die Statuszeile zeigt an,
daß die Komponente sich im File ...\system32\MSCOMCT2.OCX
befindet.
Unter "Weitere
Informationen"
finden Sie z.B. folgendes:
Das
MonthView-Steuerelement
gehört zu einer Gruppe von ActiveX-Steuerelementen in der Datei
MSCOMCT2.OCX.
Wenn also dieses
Steuerelement
verwendet werden soll, muß dem Projekt die Datei MSCOMCT2.OCX
hinzugefügt
werden.
Beim Vertrieb Ihrer
Anwendung
muß die Datei auf dem Computer des Benutzers im Verzeichnis
System
oder System32 von Microsoft Windows installiert werden.
Das heißt, ohne MSCOMCT2.OCX wird unsere noch zu erstellende Anwendung nicht lauffähig sein.
Nun wagen wir es und drücken auf "Einfügen". Folgende Sicherheitsabfrage werden wir natürlich mit "OK" beantworten:
Jetzt klappt die nächste Information auf, die wir ebenfalls mit "OK" akzeptieren:
Lassen Sie alle drei MFC-Klassen ausgewählt. Es wird jeweils ein header- und ein cpp-file angelegt. Das sind also die MFC-Klassen, die gegenüber dem OLE-Container die Schnittstelle des noch zu modellierenden ActiveX-Objektes bilden. Die Oberfläche unseres Objektes bildet vor allem die Klasse CMonthView.
Jetzt schließen wir unsere Komponenten- und Steuerelemente-Sammlung und finden im Menü "Steuerelemente" ein neues Icon. Das ist unsere hinzugefügte ActiveX-Komponente (Nicht mit dem bereits vorhandenen Steuerelement "Monatskalender" verwechseln).
Im nächsten Schritt fügen wir diese Komponente in unsere Dialogfeld-Ressource ein. Das statische Feld "ZU ERLEDIGEN: Dialogfeld-Steuerelemente hier plazieren." entfernen wir sofort. Das sieht dann ungefähr wie folgt aus:
Wenn wir nun speichern und kompilieren, erhalten wir folgende Meldung:
Ressourcen werden kompiliert...
Kompilierung läuft...
StdAfx.cpp
Kompilierung läuft...
ActiveX_Kalender.cpp
ActiveX_KalenderDlg.cpp
monthview.cpp
font.cpp
picture.cpp
Generieren von Code...
Linker-Vorgang läuft...
ActiveX_Kalender.exe - 0 Fehler, 0 Warnung(en)
und die Anwendung stellt sich folgendermaßen dar:
Sie können nun durch die Jahrhunderte scrollen (01.01.1753 - 31.12.9999), einzelne Tage anklicken oder in den leeren roten Kreis klicken, um auf das heutige Datum zu springen. Funktionalitäten in Bezug auf unsere Anwendung sind nicht erkennbar. Die muß der Programmierer der Client-Server-Anwendung Dialogfeld + ActiveX erst noch verpassen.
Bevor wir tiefer
eindringen
und eigenen Code hinzufügen, sollten wir uns das bisher
"Zusammengeklickte"
näher anschauen.
Beginnen wir mit der
Klassenansicht.
Dort findet man die durch die ActiveX-Komponente offenbar notwendig
gewordenen
Klassen CMonthView, COleFont und CPicture. Innerhalb dieser Klassen
findet
man insbesondere Get- und Set-Memberfunktionen, die für den
Informationstransport
über die Schnittstellen der ActiveX-Komponente hinweg
zuständig
sind.
CMonthView ist direkt von CWnd abgeleitet, während COleFont und CPicture von COleDispatchDriver abgeleitet sind. Die MFC-Klasse COleDispatchDriver ist eine reine Support-Klasse ohne weitere Basisklasse. Diese Klasse sorgt für die OLE Automatisierung des Clients. Mittels der OLE Dispatch-Schnittstelle greift man auf die Methoden und Eigenschaften der ActiveX-Komponente zu. Die Member-Funktionen von COleDispatchDriver sorgen für eine "dispatch connection" vom Typ IDispatch. Other member functions use variable argument lists to simplify calling IDispatch::Invoke.
Greifen wir als Beispiel die beiden Funktionen GetDay() und SetDay(...) der Klasse CMonthView heraus:
#include "stdafx.h"
#include "monthview.h"
...
/////////////////////////////////////////////////////////////////////////////
//
Operationen
CMonthView
...
short CMonthView::GetDay()
{
short result;
InvokeHelper(0x1,
DISPATCH_PROPERTYGET,
VT_I2, (void*)&result, NULL);
return result;
}
void CMonthView::SetDay(short
nNewValue)
{
static BYTE parms[] =
VTS_I2;
InvokeHelper(0x1,
DISPATCH_PROPERTYPUT,
VT_EMPTY, NULL, parms, nNewValue);
}
Wenn Sie den inneren Aufbau der Funktionen GetDay() und SetDay(...) schon jetzt verstehen wollen, sollten Sie sich die Syntax von CWnd::InvokeHelper(...) in MSDN ansehen:
Call this member function to invoke the ActiveX control method or property specified by dwDispID, in the context specified by wFlags. The pbParamInfo parameter specifies the types of the parameters passed to the method or property. The variable list of arguments is represented by ... in the syntax declaration.
void InvokeHelper( DISPID
dwDispID,
WORD wFlags, VARTYPE vtRet, void* pvRet, const BYTE* pbParamInfo, ... );
throw( COleException );
throw( COleDispatchException );
dwDispID
Identifies the method or property
to be invoked. This value is usually supplied by Component Gallery.
wFlags
Flags describing the context of the
call to IDispatch::Invoke. For possible wFlags values,
seeIDispatch::Invoke
in the Win32 SDK OLE Programmer's Reference.
vtRet
Specifies the type of the return
value.
For possible values, see the Remarks section for
COleDispatchDriver::InvokeHelper.
pvRet
Address of the variable that will
that will receive the property value or return value. It must match the
type specified by vtRet.
pbParamInfo
Pointer to a null-terminated string
of bytes specifying the types of the parameters following pbParamInfo.
For possible values, see the Remarks section for
COleDispatchDriver::InvokeHelper.
...
Variable List of parameters, of types
specified in pbParamInfo.
Includiert ist die
Datei
monthview.h, in der wir die Deklarationen aller Memberfunktionen
(Operationen)
der Klasse CMonthView
finden
(Getday() und SetDay(...) sind rot markiert):
class CMonthView : public CWnd
{
...
public:
// Operationen
...
long GetAppearance();
void SetAppearance(long
nNewValue);
unsigned long GetBackColor();
void SetBackColor(unsigned
long newValue);
long GetBorderStyle();
void SetBorderStyle(long
nNewValue);
short
GetDay();
void
SetDay(short nNewValue);
BOOL GetDayBold(DATE
dateIndex);
void SetDayBold(DATE
dateIndex,
BOOL bNewValue);
long GetDayOfWeek();
void SetDayOfWeek(long
nNewValue);
BOOL GetEnabled();
void SetEnabled(BOOL
bNewValue);
COleFont GetFont();
void SetFont(LPDISPATCH
newValue);
void SetRefFont(LPDISPATCH
newValue);
unsigned long GetForeColor();
void SetForeColor(unsigned
long newValue);
long GetHWnd();
void SetHWnd(long nNewValue);
DATE GetMaxDate();
void SetMaxDate(DATE
newValue);
short GetMaxSelCount();
void SetMaxSelCount(short
nNewValue);
DATE GetMinDate();
void SetMinDate(DATE
newValue);
long GetMonth();
void SetMonth(long nNewValue);
unsigned long
GetMonthBackColor();
void
SetMonthBackColor(unsigned
long newValue);
short GetMonthColumns();
void SetMonthColumns(short
nNewValue);
short GetMonthRows();
void SetMonthRows(short
nNewValue);
CPicture GetMouseIcon();
void SetMouseIcon(LPDISPATCH
newValue);
void
SetRefMouseIcon(LPDISPATCH
newValue);
long GetMousePointer();
void SetMousePointer(long
nNewValue);
BOOL GetMultiSelect();
void SetMultiSelect(BOOL
bNewValue);
long GetOLEDropMode();
void SetOLEDropMode(long
nNewValue);
short GetScrollRate();
void SetScrollRate(short
nNewValue);
DATE GetSelEnd();
void SetSelEnd(DATE newValue);
DATE GetSelStart();
void SetSelStart(DATE
newValue);
BOOL GetShowToday();
void SetShowToday(BOOL
bNewValue);
BOOL GetShowWeekNumbers();
void SetShowWeekNumbers(BOOL
bNewValue);
long GetStartOfWeek();
void SetStartOfWeek(long
nNewValue);
unsigned long
GetTitleBackColor();
void
SetTitleBackColor(unsigned
long newValue);
unsigned long
GetTitleForeColor();
void
SetTitleForeColor(unsigned
long newValue);
unsigned long
GetTrailingForeColor();
void
SetTrailingForeColor(unsigned
long newValue);
DATE GetValue();
void SetValue(DATE newValue);
DATE GetVisibleDays(short
sIndex);
void SetVisibleDays(short
sIndex, DATE newValue);
short GetWeek();
void SetWeek(short nNewValue);
short GetYear();
void SetYear(short nNewValue);
void ComputeControlSize(short
Rows, short Columns, float* Width, float* Height);
long HitTest(long x, long
y, DATE* Date);
void OLEDrag();
void Refresh();
};
Sie sehen, daß unsere ActiveX-Komponente geschmückt mit seinen "Wrapper-Klassen / IDispatch-Kapselungsklassen" eine reichhaltig ausgestattete Schnittstelle bietet. Betrachten Sie das Ganze, wie einen Getränkeautomaten. Im Moment sind Sie nur Aufsteller/Nutzer des Automaten. Die Innereien müssen Sie erst interessieren, wenn Sie solche Automaten bauen wollen. SetDay(...) ist also wie Geld einwerfen, und GetDay() ist wie Getränk entnehmen.
Das probieren wir nun
in
der Praxis an unserer Anwendung aus. Erstellen Sie bitte sechs Buttons
mit folgenden Eigenschaften:
ID | Titel |
IDC_SONNTAG | Sonntag |
IDC_MONTAG | Montag |
IDC_TAG1 | 1. Tag |
IDC_TAG31 | 31. Tag |
IDC_YEAR1800 | Jahr 1800 |
IDC_YEAR3000 | Jahr 3000 |
Nun greifen wir als
nächstes
mit den sechs Buttons auf verschiedene Set-Funktionen der
ActiveX-Komponente
zu.
Zunächst müssen
wir für die ActiveX-Komponente eine Member-Variable erstellen (Sie
ordnen IDC_MONTHVIEW1 die Variable m_ctrlMonthView zu:
Nun werden wir unserem Button "1.Tag" (IDC_TAG1) bezüglich der Nachricht "BN_CLICKED" die Funktion SetDay(...) zuordnen:
Bitte geben Sie
folgenden
Code ein:
Das war das erste Mal,
das
wir eine Zeile C++-Code eingegeben haben!
Das Ergebnis ist eine
Verbindung
zwischen einem Button und einer Schnittstellen-Funktion unserer
ActiveX-Komponente.
Das gleiche Prozedere
wiederholen
wir mit dem Button "31.Tag":
Da der Februar keinen 31. Tag hat, sind wir auf das Ergebnis gespannt. Eine "intelligente" ActiveX-Komponente hätte hier vielleicht den 28.02. gewählt. Aber unsere Komponente ist einfach gestrickt (Sie kennen ja die Funktion von innen) und gibt uns die Fehlermeldung "Ungültiger Eigenschaftswert" aus:
Standardisiert beginnt die Woche in Deutschland seit 1976 am Montag. Damit Sie den Wochenanfang auch auf Sonntag (wie vor 1976 üblich) setzen können, haben wir die Buttons "Sonntag" und "Montag" aufgenommen. Folgende Funktionen werden zugeordnet:
void
CActiveX_KalenderDlg::OnMontag()
{
m_ctrlMonthView.SetStartOfWeek(2);
}
Die Programmierung unter Verwendung einer ActiveX-Komponente ist, wie Sie sehen, nicht schwierig. Das know-how liegt im Kennenlernen der Programmierschnittstelle einer Komponente.
Jetzt werden wir unseren letzten beiden Knöpfen auch noch ihre Set-Funktionen verpassen:
void
CActiveX_KalenderDlg::OnYear3000()
{
m_ctrlMonthView.SetYear(3000);
}
Bisher haben wir sozusagen nur Geld in den Automaten eingeworfen (Set-Funktionen). Jetzt werden wir auch noch ein Getränk auswerfen lassen (Get-Funktionen). Hierzu benötigen wir ein Ausgabefeld. Am einfachsten ist ein Static-Feld (ID: IDC_STATIC1, Titel:""). Wir ordnen die Member-Variable m_strOutput als CString zu.
Das im Kalender
angezeigte/ausgewählte
Datum basteln wir uns über die Funktionen GetDay(), GetMonth() und
GetYear() zusammen.
Als Auslöser zur
Anzeige
wählen wir das Ereignis SelChange unserer
Kalender-ActiveX-Komponente
IDC_MONTHVIEW1:
Innerhalb dieser neu hinzugefügten Funktion geben Sie bitte folgenden Code ein:
m_strOutput.Format("%d.%d.%d",iDay,iMonth,iYear);
UpdateData(FALSE);
}
Wenn man nun ein Datum auswählt, wird das entsprechende Datum im Static-Feld ausgegeben.
An diesem Beispiel
haben
Sie nun gesehen, wie man eine vorhandene ActiveX-Komponente
objektorientiert
in ein Projekt einbindet.
Die Schnittstelle für
den Programmierer ist die Wrapper-Klasse CMonthView mit all ihren Set-
und Get-Funktionen.
MFC übernimmt hier
den Kleinkram, sodaß der Programmierer sich auf sein
eigentliches
Ziel, nämlich die Erstellung einer individuellen Anwendung,
konzentrieren
kann.