2004. november 15., hétfő

How to develop a Control Panel applet


Problem/Question/Abstract:

I want to develop an own Application for Win9x Control Panel. First I thought I just have to rename an *.exe File to *.cpl, but that doesn't work. Now I heard, *.cpl files are Dynamic Link Libraries. So, which functions do I have to implement into my DLL interface?

Answer:

Creating a Control Panel Applet is a straight forward process. Simply create a Dynamic Link Library with the extension of .cpl (Control Panel Library) and place it in the Windows system directory. Each cpl file can support multiple control panel applets. The cpl will have a single function entry point called CPlApplet() that must be exported by name. All the following control panel messages will come through this single entry point:

Message:

CPL_INIT
Sent to indicate CPlApplet() was found. Return TRUE to continue the loading process.

CPL_GETCOUNT
Return the number of applets supported by the cpl.

CPL_INQUIRE
Sent for information about each applet supported by the cpl.lParam1 contains the zero based applet number for the inquiry. lParam2 points to a TCplInfo structure. The idIcon field of the TClpInfo structure should be initialized with the resource id for an icon to display, and the idName and idInfo fields should be initialized with the resource string id for the name and description string id.lData can contain applet defined data for use by the applet.

CPL_SELECT
Sent when the applet's icon has been selected by the user. lParam1 contains the applet number that was selected. lParam2 contains the applet's user defined lData value.

CPL_DBLCLK
Sent when the applet's icon has been double-clicked. lParam1 contains the applet number. lParam2 contains the applet's user defined lData value. This message initiates the display of the applet's dialog box.

CPL_STOP
Sent for each applet when the control panel is exiting. lParam1 contains the applet number. lParam2 contains the applet's user defined lData value. Any applet specific cleaning up should be performed during this call.

CPL_EXIT
Sent prior to the control panel call to FreeLibrary. Non-applet specific cleaning up should be performed during this call.

CPL_NEWINQUIRE
Same as CPL_INQUIRE except lParam2 is a pointer to a NEWCPLINFO structure.


Your control panel library will need some additional resources to function. You will need to create a resource file containing a string table containing both the name and description of your applet(s) and icons for each applet in your cpl. You can create a res file from a .rc (resource script file) using the BRCC.EXE or BRCC32.EXE command line resource compiler, or a WYSIWYG resource editor like Borland's Resource Workshop.

Example .rc file containing a string table with two strings and a pointer to a icon file resource:

STRINGTABLE
{
1, "TestApplet"
2, "My Test Applet"
}
2 ICON C:\SOMEPATH\CHIP.ICO

To compile the .rc file to a .res file that can be linked with your application, simply type on the dos command line the full path to the resource compiler, and the full path to the name of the .rc file to compile. Here is an example:

c:\Delphi\Bin\brcc32.exe c:\Delphi\MyRes.rc

When the compiler is finished, you should have a new file with the same name as the .rc file you've compiled, only with an extension of ".res".

If you are developing for multiple platforms, you should create both a 16 and 32 bit res file for linkage with your application.

The following is an example of a control panel applet that executes a secondary executable in response to the CPL_DBLCLK message. You can adapt the code to show a form or dialog box as well. The example is coded to compile for Win16 and Win32 environments.

To build the project, you will need to compile the above .rc file to a res file named either: TCPL32.RES or TCPL16.RES.

library TestCpl;

{$IFDEF WIN32}
uses
  SysUtils, Windows, Messages;

{$ELSE}

uses
  SysUtils, WinTypes, WinProcs, Messages;

{$ENDIF}
{$IFDEF WIN32}
{$R TCPL32.RES}
{$ELSE}
{$R TCPL16.RES}
{$ENDIF}

const
  NUM_APPLETS = 1;

{$IFDEF WIN32}
const
  CPL_DYNAMIC_RES = 0;
{$ENDIF}
const
  CPL_INIT = 1;
const
  CPL_GETCOUNT = 2;
const
  CPL_INQUIRE = 3;
const
  CPL_SELECT = 4;
const
  CPL_DBLCLK = 5;
const
  CPL_STOP = 6;
const
  CPL_EXIT = 7;
const
  CPL_NEWINQUIRE = 8;
{$IFDEF WIN32}
const
  CPL_STARTWPARMS = 9;
{$ENDIF}
const
  CPL_SETUP = 200;

{$IFNDEF WIN32}
type
  DWORD = LongInt;
{$ENDIF}

type
  TCplInfo = record
    idIcon: integer;
    idName: integer;
    idInfo: integer;
    lData: LongInt;
  end;
  PCplInfo = ^TCplInfo;

type
  TNewCplInfoA = record
    dwSize: DWORD;
    dwFlags: DWORD;
    dwHelpContext: DWORD;
    lData: LongInt;
    IconH: HIcon;
    szName: array[0..31] of char;
    szInfo: array[0..63] of char;
    szHelpFile: array[0..127] of char;
  end;
  PNewCplInfoA = ^TNewCplInfoA;

{$IFDEF WIN32}
type
  TNewCplInfoW = record
    dwSize: DWORD;
    dwFlags: DWORD;
    dwHelpContext: DWORD;
    lData: LongInt;
    IconH: HIcon;
    szName: array[0..31] of WChar;
    szInfo: array[0..63] of WChar;
    szHelpFile: array[0..127] of WChar;
  end;
  PNewCplInfoW = ^TNewCplInfoW;
{$ENDIF}

type
  TNewCplInfo = TNewCplInfoA;
type
  PNewCplInfo = ^TNewCplInfoA;

function CPlApplet(hWndCPL: hWnd; iMEssage: integer; lParam1: longint;
  lParam2: longint): Longint{$IFDEF WIN32} stdcall;
{$ELSE}; export;
{$ENDIF}
begin
  case iMessage of
    CPL_INIT:
      begin
        Result := 1;
        exit;
      end;
    CPL_GetCount:
      begin
        Result := NUM_APPLETS;
        exit;
      end;
    CPL_Inquire:
      begin
        PCplInfo(lParam2)^.idIcon := 2;
        PCplInfo(lParam2)^.idName := 1;
        PCplInfo(lParam2)^.idInfo := 2;
        PCplInfo(lParam2)^.lData := 0;
        Result := 1;
        exit;
      end;
    CPL_NewInquire:
      begin
        PNewCplInfo(lParam2)^.dwSize := sizeof(TNewCplInfo);
        PNewCplInfo(lParam2)^.dwHelpContext := 0;
        PNewCplInfo(lParam2)^.lData := 0;
        PNewCplInfo(lParam2)^.IconH := LoadIcon(hInstance, MakeIntResource(2));
        lStrCpy(@PNewCplInfo(lParam2)^.szName, 'TestCPL');
        lStrCpy(PNewCplInfo(lParam2)^.szInfo, 'My Test CPL');
        PNewCplInfo(lParam2)^.szHelpFile[0] := #0;
        Result := 1;
        exit;
      end;
    CPL_SELECT:
      begin
        Result := 0;
        exit;
      end;
    CPL_DBLCLK:
      begin
        WinExec('Notepad.exe', SW_SHOWNORMAL);
        Result := 1;
        exit;
      end;
    CPL_STOP:
      begin
        Result := 0;
        exit;
      end;
    CPL_EXIT:
      begin
        Result := 0;
        exit;
      end
  else
    begin
      Result := 0;
      exit;
    end;
  end;
end;

exports CPlApplet name 'CPlApplet';

begin
end.

Nincsenek megjegyzések:

Megjegyzés küldése