Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How do I write a Control Panel Applet?

How To

How do I write a Control Panel Applet?

by  Glenn9999  Posted    (Edited  )
This gives a short example of how to write a control panel applet in Delphi. While I am aware that later Delphis have the option to create a "control panel" application, this shows what is needed to construct one otherwise. This FAQ will only show the remarkable steps and parts of a Control Panel app and will leave out other things.

Much of this is based off the "Creating Control Panel Applets with Delphi" by Ted Houts entry that was copied to a searched web page. Source has been cleaned up and documented.

Resource File
A number of resource entries are required. A string table listing the title and information regarding each applet your file will support is required. As well, an icon for each applet entry is required. You will need to build this file using BRCC32. An example for that is to come.

cp_resource.rc
Code:
STRINGTABLE { 
//the "title" of applet #1 
10, "Demo Current Time #1" 
//the description of applet #1 
11, "Shows the current system time..." 
//the "title" of applet #2 
20, "Demo Current Time #2" 
//the description of applet #2 
21, "Shows the current system time..." }

12 ICON applet1.ico //the icon for applet #1 
22 ICON applet2.ico //the icon for applet #2

Application Source
The next major question is the application source itself. CPL files are DLL files with a different extension and support the export by name of a function called "CPlApplet". The forms as defined in this file are standard VCL forms which may be developed as regular applications for testing, since there is nothing remarkable that we need to do for control panel applications within those. Hopefully the documentation in the comments should be sufficient:

myapplets.dpr
Code:
library myapplets;
  uses SysUtils, Classes, Windows, cpl //a unit in Delphi 3,
       frmApplet1 in 'frmApplet1.pas',
       frmApplet2 in 'frmApplet2.pas';

{ Demo control panel application
  based off of a link to "Creating Control Panel Applets with Delphi" by Ted Houts
  cleaned up & posted on tek-tips by Glenn9999 }

{$E cpl}                 //changes the extension to CPL from DLL
{$R cp_resource.RES}     //resource built containing the icons and string for the applets in this .CPL
const
  APPLETS_COUNT = 2;     //the number of applets (items that show up in Control Panel)

var
  Frm_Applet1 : TFrm_Applet1; //applet #1
  Frm_Applet2 : TFrm_Applet2; //applet #2
//This procedure puts the action of opening applet dialog boxes into one place

procedure OpenDialogBox(DlgNo,PageNo : Integer);
{
  This project has 2 applets (dialogs) which are roughly identical.
  If zero is passed, open the first applet; otherwise, one should be passed,
  but just in case, any other value for dialog number will open the second applet.
}
  begin
    if DlgNo = 0 then
      begin
        Frm_Applet1 := TFrm_Applet1.Create(nil);
        // this line puts the icon resource associated with this form in control panel
        // to the VCL form itself
        Frm_Applet1.Icon.Handle :=
               LoadIcon(hInstance, MakeIntResource( (10 * (DlgNo + 1)) + 2));
        Frm_Applet1.ShowModal;
      end
    else
      begin
        Frm_Applet2 := TFrm_Applet2.Create(nil);
        // this line puts the icon resource associated with this form in control panel
        // to the VCL form itself
        Frm_Applet2.Icon.Handle :=
               LoadIcon(hInstance, MakeIntResource( (10 * (DlgNo + 1)) + 2));
        Frm_Applet2.ShowModal;
      end;
   end;

{
CPlApplet is the callback function that processes all messages from
"controlling application", i.e. Control Panel
}

function CPlApplet(hWndCPL : hWnd; iMessage : integer; lParam1 : longint;
         lParam2 : longint) : LongInt stdcall;
  begin
    case iMessage of
      CPL_INIT :
      //first message, sent once: this is the time to create global objects
        begin
          Result := 1;
          exit;
        end;
      CPL_GETCOUNT :
      //second message sent once, return the number of applets supported by this file
        begin
          Result := APPLETS_COUNT; //number of dialog boxes supported
          exit;
        end;
      CPL_INQUIRE :
     //Filling in the CPLINFO record with resource information. NOTE resource
     //file has clever IDs so as to make the code based on the dialog number,
     //see cp_resource.rc. lParam1 is zero-based dialog (or applet) number.
     //lParam2 is a pointer to a CPLINFO record that's filled with the applet
     //information, including "lData," which can point to application-specific
     //stuctures. Fill the record and return 0.
        begin
          PCplInfo(lParam2)^.idName := (10 * (lParam1 + 1)) + 0;
          PCplInfo(lParam2)^.idInfo := (10 * (lParam1 + 1)) + 1;
          PCplInfo(lParam2)^.idIcon := (10 * (lParam1 + 1)) + 2;
          PCplInfo(lParam2)^.lData := 0;
          Result := 0; //handled, returning zero
          exit;
        end;
      CPL_NEWINQUIRE :
     //This has an identical function to CPL_INQUIRE except that it requires
     //a different format set for data.  See above for notes.
        begin
          PNewCplInfo(lParam2)^.dwSize := sizeof(TNewCplInfo);
          PNewCplInfo(lParam2)^.lData := 0;
          PNewCplInfo(lParam2)^.HIcon :=
               LoadIcon(hInstance, MakeIntResource( (10 * (lParam1 + 1)) + 2));
          LoadString(hInstance, (10 * (lParam1 + 1)) + 0,
               @PNewCplInfo(lParam2)^.szName, sizeof(PNewCplInfo(lParam2).szName));
          LoadString(hInstance, (10 * (lParam1 + 1)) + 1,
               @PNewCplInfo(lParam2)^.szInfo, sizeof(PNewCplInfo(lParam2).szInfo));
          PNewCplInfo(lParam2)^.dwHelpContext := 0;
          PNewCplInfo(lParam2)^.szHelpFile[0] := #0;
          Result := 0; //handled, returning zero
          exit;
        end;
      CPL_SELECT :
      // obsolete message supported for backwards compatibility.
        begin
          Result := 0;
          exit;
        end;
      CPL_DBLCLK :
      //user has double-clicked one of your icons in the control panel, respond here
        begin
          OpenDialogBox(lParam1,0);
          Result := 0; //handled, returning zero
          exit;
        end;
      CPL_STOP :
      //sent once per applet, do form shutdown code here.
        begin
          if lParam1 = 0 then
            Frm_Applet1.Free  //user has closed Applet #1 form
          else
            Frm_Applet2.Free; //user has closed Applet #2 form
          Result := 0; //handled, returning zero
          exit;
        end;
      CPL_EXIT :
        begin //sent once this is the time to free global objects
          Result := 0; //handled, return 0
          exit;
        end;
      CPL_STARTWPARMS : //CPL_STARTWPARMSA, CPL_STARTWPARMSW
        //sent when applet started via rundll32.exe this .CPL will support an
        //integer param to indicate the page number, default to page one
        //if invalid
        //lParam2 is the parameter string passed in the command line.
        //lParam1 and return value are the same as CPL_DBLCLK
        begin
          OpenDialogBox(lParam1, StrToIntDef(String(lParam2),1));
          Result := 1; //handled, so return 1
          exit;
        end;
      else
        begin
          Result := 0; // return 0 to indicate that this message isn't processed
          exit;
        end;
    end;
end;

//as required export CPLApplet by name
  exports CPlApplet name 'CPlApplet';

begin
end.

The above code is all that is required to write a control panel application.

Testing
Testing, however, is another story. To generate the RES file for the RC posted above, one needs to call BRCC32 against this file. I like to make a batch file and have it run the compile against the project as well, since I find this is usually required if a change is required in the RC file. A sample is below and you will need to change it to match what your system is.

BUILD.BAT
Code:
"C:\Program Files\Borland\Delphi 3\Bin\brcc32.exe" cp_resource.rc
"C:\Program Files\Borland\Delphi 3\Bin\dcc32.exe" myapplets.dpr
rem To Deploy to the system directory if necessary.
rem copy myapplets.cpl C:\Windows\system32

rem clean up old files
del *.~*
pause

In running the code, you can either make the file so control panel picks it up and test there, or use RUNDLL32. Doing the former involves either:
1. Copy the .CPL file to the system directory.
2. Copy the .CPL file to the directory where the controlling application exists (Control.exe).
3. Setting a direct Install Location String: Place key in HKCU/Control Panel/MMCPL Key should be in format: <AppletName>=<path to CPL file>

To run with RUNDLL32, use the following call (case sensitivity matters):
Code:
rundll32 shell32.dll,Control_RunDLL "myapplets.cpl", @0

This can be beneficial in testing your code as well, as to not run into WFP on later versions of Windows. It is useful to test your control panel app using both methods just to be sure.
Register to rate this FAQ  : BAD 1 2 3 4 5 6 7 8 9 10 GOOD
Please Note: 1 is Bad, 10 is Good :-)

Part and Inventory Search

Back
Top