2004. augusztus 31., kedd
How to load and launch a Control Panel applet
Problem/Question/Abstract:
How to load and launch a Control Panel applet
Answer:
{
Unit AppletLauncher
Version:
1.0 Created: 23.08.98, 11:14:39
Last Modified: 23.08.98, 11:14:39
Author : P. Below
Project: Win32 utilities
Delphi version: 3.x (not tested on 4.x)
Description:
Provides a class to load and launch a control panel applet. The applet is loaded when an
instance of this class is created and unloaded when it is destroyed. Applet dialogs are not
shown unless explicitely requested. The required API definitions are obtained from the Delphi
unit CPL. Processing is complicated by the fact that even Win32 applets may not respond to
CPL_NEWINQUIRE. This requires storage for both old (CPL_INQUIRE) and new style info
records. For a dialog only one of the list will contain a valid pointer to an info record, the other
will contain Nil in that slot.
Note:
There are several errors in the win32 docs for the return values of CPL_ messages. Generally
the CPlApplet entry point will return 0 (False) if the function fails and <> 0 (TRUE) if it succeeds.
The docs indicate the inverse.
}
unit AppletLauncher;
interface
uses
Classes, Windows, CPL, Graphics, Sysutils;
type
TAppletLauncher = class
private
hCPL: HMODULE; { applet module handle }
hParent: HWND; { container window handle }
CPlApplet: TCPlApplet; { applet entry point }
FDialogCount: Integer; { number of dialogs available }
FDialogData: TList; { stores TNewCPLnfo records for the dialogs }
FOldDialogData: TList; { stores TCPlInfo records for the dialog }
FAppletName: string; { filename of applet }
protected
function GetDialogNames(index: Integer): string;
function GetDialogInfotext(index: Integer): string;
function GetDialogIcon(index: Integer): HICON;
function GetDeleteIcon(index: Integer): Boolean;
function DialogLData(index: Integer): Longint;
function LoadCPLResourceString(strid: Integer): string;
procedure ValidateIndex(index: Integer); virtual;
public
constructor Create(anAppletName: string; hwndParent: HWND);
destructor Destroy; override;
procedure ShowDialog(index: Integer);
property DialogCount: Integer read FDialogCount;
property DialogNames[index: Integer]: string read GetDialogNames; default;
property DialogInfotext[index: Integer]: string read GetDialogInfotext;
property DialogIcons[index: Integer]: HIcon read GetDialogIcon;
property DeleteIcon[index: Integer]: Boolean read GetDeleteIcon;
property AppletName: string read FAppletName;
end;
EAppletError = class(Exception);
implementation
resourcestring { change to Const for Win16 }
errCannotLoadApplet = 'TAppletLauncher: cannot load applet %s, reason: %s.';
errInvalidApplet = 'TAppletLauncher: %s is not a control panel applet, it does ' + 'not export
the CPlApplet entry point.';
errAppletInitializationFailed = 'TAppletLauncher: initialization of applet %s failed.';
errAppletHasNoDialogs = 'TAppletLauncher: applet %s provides no dialogs.';
errCannotGetDialogInfo = 'TAppletLauncher: applet %s does not respond to CPL_INQUIRE, ' +
'the launcher is unable to obtain the required information about ' +
'the applets dialogs.';
errSelectDialogFailed = 'TAppletLauncher: applet %s failed to open dialog #%d (%s).';
errIndexOutOfBounds = 'TAppletLauncher: dialog index %d is invalid for applet %s, ' +
'the allowed range is 0..%d.';
{Methods of TAppletLauncher}
{
Constructor TAppletLauncher.Create
Parameters:
anAppletName:
file name of the applet to load. This name must contain the CPL extention and can contain a
full path, if the applet does not reside in the windows or system directories.
hwndParent:
handle of window that serves as control panel replacement. Use the main forms handle, for
example. This window is used as parent for the applets dialogs. If 0 is passed we use the
active window as parent.
Call method: static
Description:
Loads the applet DLL, creates the internal list and fills it with information records for the dialogs
the applet provides. Several messages are send to the applets entry point during construction.
Error Conditions:
An exception will be raised if the applet could not be loaded or if it does not respond in the
expected way to the send messages. The object is destroyed automatically in this case.
}
constructor TAppletLauncher.Create(anAppletName: string; hwndParent: HWND);
var
i: Integer;
pData: PNewCPlInfo;
pOldData: PCPlInfo;
begin
inherited Create;
if hwndParent = 0 then
begin
hwndparent := GetActiveWindow;
end;
hParent := hwndParent;
{ Try to load the applet DLL. }
hCPL := LoadLibrary(Pchar(anAppletName));
if hCPL = 0 then
begin
{ Error, applet not found. Note: change logic for Win16! }
raise EAppletError.CreateFmt(errCannotLoadApplet, [anAppletName,
SysErrorMessage(GetLastError)]);
end;
FAppletName := anAppletName;
{ Find applet entry point }
@CPlApplet := GetProcAddress(hCPL, 'CPlApplet');
if @CPlApplet = nil then
begin
{ Entry point not found, this is not a control panel applet! }
raise EAppletError.CreateFmt(errInvalidApplet, [anAppletName]);
end;
{ Send CPL_INIT to the applet }
if CPlApplet(hParent, CPL_INIT, 0, 0) = 0 then
begin
{ Applet failed to initialize, bail out. }
raise EAppletError.CreateFmt(errAppletInitializationFailed, [anAppletName]);
end;
{ Get number of dialogs the applet supports }
FDialogCount := CPlApplet(hParent, CPL_GETCOUNT, 0, 0);
if FDialogCount = 0 then
begin
raise EAppletError.CreateFmt(errAppletHasNoDialogs, [anAppletName]);
end;
{ Create list for the dialog information }
FDialogData := TList.Create;
FDialogData.Capacity := FDialogCount;
FOldDialogData := TList.Create;
FOldDialogData.Capacity := FDialogCount;
{ Get the information for the dialogs }
for i := 0 to FDialogCount - 1 do
begin
New(pData);
FillChar(pData^, Sizeof(pData^), 0);
pData^.dwSize := Sizeof(pData^);
if CPlApplet(hParent, CPL_NEWINQUIRE, i, longint(pData)) = 0 then
begin
{ Failed, try CPL_INQUIRE instead }
Dispose(pData);
New(pOldData);
if CPlApplet(hParent, CPL_INQUIRE, i, longint(pOldData)) = 0 then
begin
{ Failed also, bail out }
Dispose(pOldData);
raise EAppletError.CreateFmt(errCannotGetDialogInfo, [anAppletName]);
end
else
begin
FOldDialogData.Add(pOldData);
FDialogData.Add(nil);
end;
end { If }
else
begin
{ CPL_NEWINQUIRE succeeded, store the data }
FDialogData.Add(pData);
FOldDialogData.Add(nil);
end;
end;
{ Setup is complete }
end;
{
Destructor TAppletLauncher.Destroy
Parameters: none
Call method: virtual, overridden
Description:
Releases memory for the dialog data records, destroys the list holding the records, tells the
applet to clean up its act and finally unloads the applet. The destructor can be called on
a partially initialized object if an exception is raised in the constructor.
Error Conditions: none
}
destructor TAppletLauncher.Destroy;
var
i: Integer;
begin
{ Tell applet to clean up its dialogs and release the dialog data, if initialization
completed successfully }
if Assigned(FDialogData) then
begin
for i := 0 to FDialogData.Count - 1 do
begin
CPlApplet(hParent, CPL_STOP, i, DialogLData(i));
if Assigned(FDialogData[i]) then
begin
Dispose(pNewCPlInfo(FDialogData[i]));
end;
end;
FDialogData.Free;
end;
if Assigned(FOldDialogData) then
begin
for i := 0 to FOldDialogData.Count - 1 do
begin
if Assigned(FOldDialogData[i]) then
begin
Dispose(pCPlInfo(FOldDialogData[i]));
end;
end;
FOldDialogData.Free;
end;
{ Tell applet to clean up, if load was successful. Note: this code is executed even if CPL_INIT
failed, I don't know if this may cause a problem. }
if Assigned(@CPlApplet) then
begin
CPlApplet(hParent, CPL_EXIT, 0, 0);
end;
{ Unload the applet, if it was loaded }
if hCPL <> 0 then
begin
FreeLibrary(hCPL);
end;
inherited Destroy;
end;
{
Procedure TAppletLauncher.ShowDialog
Parameters:
index: dialog index, has to be in the range 0..DialogCount-1
Call method: static
Description:
Tells the applet to open the requested dialog.
Error Conditions:
Exceptions will be raised if the passed index is out of bounds or the applet fails to launch the dialog.
}
procedure TAppletLauncher.ShowDialog(index: Integer);
begin
ValidateIndex(index);
if CPlApplet(hParent, CPL_DBLCLK, index, DialogLData(index)) = 0 then
begin
raise EAppletError.CreateFmt(errSelectDialogFailed, [FAppletName, index, DialogNames[index]]);
end;
end;
{
Function TAppletLauncher.GetDialogNames
Parameters:
index: dialog index, has to be in the range 0..DialogCount-1
Returns:
the szname field of the dialog info record for this dialog.
Call method: static
Description:
This method implements read access to the DialogNames property.
Error Conditions:
An exceptions will be raised if the passed index is out of bounds.
}
function TAppletLauncher.GetDialogNames(index: Integer): string;
begin
ValidateIndex(index);
if Assigned(FDialogData[index]) then
begin
result := Strpas(pNewCPlInfo(FDialogData[index])^.szName);
end
else
result := LoadCPLResourceString(pCPlInfo(FOldDialogData[index])^.idName);
end;
{
Function TAppletLauncher.GetDialogInfotext
Parameters:
index: dialog index, has to be in the range 0..DialogCount-1
Returns:
the szinfo field of the dialog info record for this dialog.
Call method: static
Description:
This method implements read access to the DialogInfotext property.
Error Conditions:
An exceptions will be raised if the passed index is out of bounds.
}
function TAppletLauncher.GetDialogInfotext(index: Integer): string;
begin
ValidateIndex(index);
if Assigned(FDialogData[index]) then
begin
result := Strpas(pNewCPlInfo(FDialogData[index])^.szInfo);
end
else
result := LoadCPLResourceString(pCPlInfo(FOldDialogData[index])^.idInfo);
end;
{
Function TAppletLauncher.GetDialogIcon
Parameters:
index: dialog index, has to be in the range 0..DialogCount-1
Returns:
the icon handle for the icon to display for this dialog. Note that the handle can be 0!
Use DrawIconEx to display this icon on a canvas, or create a TIcon and assign the
return value to its Handle property.
Call method: static
Description:
This method implements read access to the DialogIcons property.
PROBLEM ALERT!
For applets that respond to CPL_NEWINQUIRE the icon handle is owned by the applet and
must not be deleted by the application. For old-style applets that respond only to CPL_INQUIRE,
however, the icon is created from a resource and the application must delete it to prevent a
resource leak! Check the DeleteIcon property to determine what to do.
Error Conditions:
An exceptions will be raised if the passed index is out of bounds.
}
function TAppletLauncher.GetDialogIcon(index: Integer): HICon;
begin
ValidateIndex(index);
if Assigned(FDialogData[index]) then
begin
result := pNewCPlInfo(FDialogData[index])^.hIcon;
end
else
result := LoadIcon(hCPL, MakeIntResource(pCPlInfo(FOldDialogData[index])^.idIcon));
end;
{
Function TAppletLauncher.GetDeleteIcon
Parameters:
index: dialog index, has to be in the range 0..DialogCount-1
Returns:
True if caller needs to delete an icon retrieved via DialogIcons for this index, false otherwise.
Call method: static
Description:
See Problem Alert entry under GetDialogIcon.
Error Conditions:
An exceptions will be raised if the passed index is out of bounds.
}
function TAppletLauncher.GetDeleteIcon(index: Integer): Boolean;
begin
ValidateIndex(index);
Result := not Assigned(FDialogData[index]);
end;
{
Procedure TAppletLauncher.ValidateIndex
Parameters:
index: index to validate
Call method: virtual
Error Conditions:
raises an exception if the passed index is out of bounds. You can override this method to
change the behaviour.
}
procedure TAppletLauncher.ValidateIndex(index: Integer);
begin
if (index < 0) or (index >= FDialogCount) then
begin
raise EAppletError.CreateFmt(errIndexOutOfBounds, [index, FAppletName, FDialogCount - 1]);
end;
end;
{
Function TAppletLauncher.DialogLData
Parameters:
index: dialog index, has to be in the range 0..DialogCount-1
Returns:
the ldata member of the dialog data.
Call method: static
Description:
Helper function to deal with the different data record formats we can have.
Error Conditions: none
}
function TAppletLauncher.DialogLData(index: Integer): Longint;
begin
if Assigned(FDialogData[index]) then
Result := pNewCPlInfo(FDialogData[index])^.ldata
else
Result := pCPlInfo(FOldDialogData[index])^.ldata
end;
{
Function TAppletLauncher.LoadCPLResourceString
Parameters:
strid: resource id of string to load
Returns:
the string
Call method: static
Description:
Helper function to get a resource string from the applet.
Error Conditions: none
}
function TAppletLauncher.LoadCPLResourceString(strid: Integer): string;
begin
SetLength(result, 1024);
SetLength(result, LoadString(hCPL, strid, @result[1], 1024));
end;
end.
Feliratkozás:
Megjegyzések küldése (Atom)
Nincsenek megjegyzések:
Megjegyzés küldése