Previous     Contents     Index     Next     
Setup Util Programmer's Guide



Chapter 4   Writing Pre-Installation Programs


This chapter shows you how to integrate custom installation programs and DLLs with the Setup Util and Common Install Shell. It contains the following sections:



What Pre-Installation Programs Do

A pre-installation program is used to gather information before the Common Install Shell unzips a product archive. On UNIX, the program is a stand-alone executable that is written with the Setup Util API. On Windows NT, it is part of a DLL.

Typically, pre-installation programs involve one or more dialogs that are used for the following purposes:

  • Requesting configuration information

  • Asking "Yes or No" setup questions

  • Passing information or warnings to the user

On both UNIX and Windows NT, pre-installation programs are seamlessly integrated into the overall installation procedure. The end user only runs one setup program (the Common Install Shell). Any pre-installation programs are invoked behind the scenes.

From a developer's standpoint, the pre-installation program is a discrete element of the installation procedure. First the Common Install Shell displays an introduction and enables navigation controls. Then, the pre-installation program is launched and the user is prompted to enter information. When the pre-installation program exits, the Common Install Shell resumes, displays a summary screen, and performs the installation.



Writing UNIX Pre-Installation Programs



Creating a pre-installation program for UNIX involves the following:

  1. Including the necessary header files

  2. Finding out which components are selected and installed

  3. Writing code to display dialogs and process user input

  4. Storing user input

  5. Compiling and packaging the program

  6. Making the appropriate changes to the master and package information files

This section shows you how to use the Setup Util to write a pre-installation program for UNIX.


Including Header Files

The Setup Util includes a number of header files that you can include in your pre-installation program. These files are summarized in Table 4-1.


Table 4-1 Setup Util header files that you can use in pre-installation programs.

Header File

Description

code.h  

Functions for encoding and decoding SHA-1 and Base64 encryption.  

dialog.h  

Classes for creating and using UNIX dialogs.  

global.h  

Global installation classes and functions.  

ldapu.h  

LDAP utility functions used during installation.  

nsdefs.h  

General functions and classes.  

setupapi.h  

Common functions shared between UNIX and Windows NT.  

setupdefs.h  

Common definitions for UNIX and Windows NT installations.  

setupinst.h  

Setup Util installer definitions.  

setupldap.h  

LDAP functions used by the Setup Util.  

setupnvpair.h  

Functions for handling value pairs.  

utf8.h  

Utilities for converting between UTF-8 and local encoding.  

ux-util.h  

UNIX utility functions used during installation.  

The different functions and classes in each of these header files are listed in Part 4, "Reference of Setup Util Libraries."


Finding Out Information about Components

During pre-installation, you can query the Setup Util for a list of selected and installed components. A selected component is a package that the user has chosen to install during this session. An installed component is a package that is already installed on the user's system.


To Get a List of Selected Components

  • Use the following code to get a list of selected components from the installation cache:

    char *list;
    list = setupGetInfString("General",
                             "SelectedComponents",
                             "",
                             cacheFile);


To Get a List of Installed Components

  • Use the following code to get a list of components that are already installed in the selected server root:

    char *list;
    list = setupGetInfString("General",
                             "InstalledComponents",
                             "",
                             cacheFile);


Writing Code to Handle Dialogs and Input

In order to request and handle user input, you must create dialog boxes and write data processing functions. Since the UNIX version of the Setup Util does not use a windowing environment, dialog boxes are displayed as onscreen text. Use the functions and classes in dialog.h to create these textual dialog boxes. You must write your own functions to handle user input.

Dialog boxes are discussed in Chapter 5 "Creating Dialogs."


Storing User Input

You can use the data gathered during pre-installation to change package information files or configure your product. Changing a package information file involves calling setupWriteInfString or setupWriteInfInt. Configuring your product involves writing data to a cache file.


To Change Your Package Information File

  1. Gather and process all pre-installation data.

  2. Write one or more functions to determine what directives to change. You should base these functions on the data gathered above.

  3. Modify your package information file.

    Call setupWriteInfString to modify strings. Call setupWriteInfInt to modify integers.

    The functions have the following syntax

    setupWriteInfString(productNickname, directiveName, infString, cacheFile);

    setupWriteInfInt(productNickname, directiveName, infInt, cacheFile);

    The arguments are as follows:

    productNickname is your product's nickname (for instanace, slapd or admin).

    directiveName is the directive to change. Use the variable names listed in setupdefs.h. For a complete discussion of directives, see Chapter 3 "Information Files."

    infString is the string value you wish to set for the listed directive.

    infInt is the integer value you wish to set for the listed directive.

    cacheFile is the name of the file in which you will save your directives (typically install.inf).

  4. When you are finished modifying your package information file, you can save the file or return an exit value to the Common Install Shell.


Compiling and Packaging the Program

Once you have written your pre-installation program, you must compile and package it. For complete compilation instructions, see Chapter 9 "Compiling and Linking."


To Package Your Pre-Installation Program

  • Copy the pre-installation executable to the directory containing your product archive and package information files. This directory is specified by the SourcePath directive in your master information file. For more information, see Chapter 3 "Information Files."


Updating Information Files

Once you have written, compiled, and packaged your pre-installation program, you must modify your package information file to use it. On UNIX, this involves specifying the PreInstall directive. For more information on using directives, see Chapter 3 "Information Files."



Writing Windows NT Pre-Installation Programs



Pre-installation programs for Windows NT are different from those for UNIX in that they include graphical dialog boxes and are stored in DLLs. Whereas on UNIX dialogs are created with the Setup Util, on Windows they are designed in a C++ development environment. Integrating the DLL with the Common Install Shell is also handled differently on the two platforms.

Writing a pre-installation program for Windows NT involves the following steps:

  1. Finding out which components are selected and installed

  2. Creating dialog boxes in a C++ development environment

  3. Writing code to manage the dialogs and process user input

  4. Compiling and packaging the DLL

  5. Making the appropriate changes to the package information file


Finding Out Information about Components

During pre-installation, you can query the Setup Util for a list of selected and installed components. A selected component is a package that the user has chosen to install during this session. An installed component is a package that is already installed on the user's system. Both lists are maintained as global variables in nssetup32.dll.


To Get a List of Selected Components

  • Use the following code to get a list of selected components:

    char *list;
    list = SELECTEDCOMPONENTS;


To Get a List of Installed Components

  • Use the following code to get a list of components that are already installed in the selected server root:

    char *list;
    list = INSTALLEDCOMPONENTS;


Creating Dialog Boxes

An integral part of developing a pre-installation program for Windows NT is creating dialog boxes. You can use any tool you like to create the dialogs. Typically, Visual C++ is used when drawing dialog boxes for use with the Setup Util.

For more information on creating Windows NT dialog boxes, see Chapter 5 "Creating Dialogs."


Managing Dialogs and Processing Input

Once you have finished designing your dialog boxes, you must write a DLL to manage them. This DLL designates the order in which the dialogs are displayed, handles user input, and writes important data to the installation cache.

In order to perform these operations, you must define a number of functions within your DLL. Some of these functions have required names, while others are specified as package information file directives. Table 4-2 lists all functions that you must define in your DLL. If a function name is specified as a directive, it appears in brackets. For information on using directives to specify function names, see Modifying your Package Information File" on page 78.


Table 4-2 Functions for pre-installation DLLs.

Function Name

Description

PreInstall  

This function is called by the installation framework before the user is asked any questions. You should use this function to determine if all prerequisites for installing this component have been met. If this operation succeeds return TRUE, otherwise display an error message and return FALSE to abort installation.  

[AskOptions]  

This function queries the user for information about a component. It manages the dialog boxes that are used to ask all installation questions. These dialog boxes should be presented as a series of wizard property sheets.

The name of this function is specified by the AskOptions directive. See Modifying your Package Information File" on page 78 for details.  

[GetSummary]  

This function is called by the installation framework after all questions have been asked for all components. It should provide a detailed summary of the user's selections.

Each line of the summary must end in a carriage return/line feed combination ("\r\n"). This string is used in an edit control that does not properly handle single "\n" end-of-line characters.

The name of this function is specified by the GetSummary directive. See Modifying your Package Information File" on page 78 for details.  

[ReadGlobalCache]  

This function is called by the installation framework during a silent installation. It initializes data from the Global section of the installation cache and should read any necessary information.

If this operation succeeds, return TRUE, otherwise display an error message and return FALSE.

The name of this function is specified by the ReadGlobalCache directive. See Modifying your Package Information File" on page 78 for details.  

[WriteGlobalCache]  

This function is called by the installation framework when the user reaches the summary screen and clicks Next. It is used to write information to the Global section of the installation cache for use during silent installation.

Data written to the cache is not interpreted by the framework. It may consist of any values that you will need when performing a silent installation. This includes user input as well as values supplied by your installer.

If this operation succeeds, return TRUE, otherwise display an error message and return FALSE.

The name of this function is specified by the WriteGlobalCache directive. See Modifying your Package Information File" on page 78 for details.  

[ReadLocalCache]  

This function is called by the installation framework during a silent installation. It initializes data from the product-specific (local) section of the installation cache and should read any necessary information.

If this operation succeeds, return TRUE, otherwise display an error message and return FALSE.

The name of this function is specified by the ReadLocalCache directive. See Modifying your Package Information File" on page 78 for details.  

[WriteLocalCache]  

This function is called by the installation framework when the user reaches the summary screen and clicks Next. It is used to write information to the product-specific (local) section of the installation cache for use during silent installation.

Data written to the cache is not interpreted by the framework. It may consist of any values that you will need when performing a silent installation. This includes user input as well as values supplied by your installer.

If this operation succeeds, return TRUE, otherwise display an error message and return FALSE.

The name of this function is specified by the WriteLocalCache directive. See Modifying your Package Information File" on page 78 for details.  

DllMain  

The main entry point for the Windows DLL. This function is called when the DLL is loading into memory. It performs all initialization in the DLL_PROCESS_ATTACH reason handler and releases any resources that were allocated by the DLL_PROCESS_DETACH message handler.

For more information on this function, see the Windows Util documentation.  

The Common Install Shell calls the functions specified in Table 4-2 in the following order:

  1. PreInstall

  2. AskOptions

  3. GetSummary

  4. WriteLocalCache

  5. WriteGlobalCache

  6. ReadLocalCache

  7. ReadGlobalCache

  8. DLLMain

If you are installing multiple components, the Common Install Shell will call all functions for the first component, then all functions for the second component, and so on.


Sample Pre-Installation DLL

The following DLL illustrates the use of most functions listed in Table 4-2. It uses the two property pages shown above in Creating Dialog Boxes." In this example, assume the following directive assignments:

AskOptions=QueryUser

GetSummary=RetrieveUserOptions

WriteLocalCache=WriteProductCache

ReadLocalCache=ReadProductCache

For more information on using directives to specify function names, see Modifying your Package Information File" on page 78.


static void _RefreshConfigDSInfo(HWND hwndDlg)
{
   BOOL bResult = FALSE;
   bResult = SetDlgItemText(hwndDlg, IDC_LDAP_URL,
                            cd.szConfigDSURL);
}

static void _SaveConfigDSInfo(HWND hwndDlg)
{
   int nPort = 0;
   GetDlgItemText(hwndDlg, IDC_LDAP_URL, cd.szConfigDSURL,
                  MAX_PATH);
}

static BOOL CALLBACK
_DialogProcConfigDSInfo(HWND hwndDlg, UINT uMsg, WPARAM wParam,
                        LPARAM lParam)
{
   BOOL bValueReturned = FALSE;
   BOOL bResult = TRUE;
   switch(uMsg)
   {
      case WM_INITDIALOG:

      {
         HGDIOBJ hGdiObj = GetStockObject(DEFAULT_GUI_FONT);
         HWND heditwnd;
         SendMessage(hwndDlg, WM_SETFONT, (WPARAM) ((struct
                     HFONT__ *)hGdiObj) , MAKELPARAM(TRUE, 0));
         heditwnd = GetDlgItem(hwndDlg, IDC_LDAP_URL);
         SendMessage(heditwnd, WM_SETFONT, (WPARAM) ((struct
                     HFONT__ *)hGdiObj) , MAKELPARAM(TRUE, 0));
         SetDlgItemText(hwndDlg, IDC_LDAP_URL, cd.szConfigDSURL);
         cd.hwndPage[PG_FIRST] = hwndDlg;
         _RefreshConfigDSInfo(hwndDlg);
      }
      break;

      case WM_COMMAND:

      // Windows sends WM_COMMAND messages whenever the user
      // clicks on a control in your property page. If you need to
      // perform some special action, such as validating data or
      // responding to a button click, do it here.

      break;

      case WM_NOTIFY:

      // Windows sends WM_NOTIFY messages to your property page
      // whenever something interesting happens to the page. This
      // could be page activation/deactivation, a button click,
      // etc. The wParam parameter contains a pointer to the
      // property page. The lParam parameter contains a pointer
      // to an NMHDR structure. The code field of this structure
      // contains the notification message code being sent. The
      // property sheet API allows you to alter the behavior of
      // these messages by returning a value for each message. To
      // return a value, use the SetWindowLong Windows Util
      // function.

      switch(((NMHDR*)lParam)->code)
      {
         case PSN_SETACTIVE:
         {
            CenterWindow(GetParent(hwndDlg));
            PropSheet_SetWizButtons(GetParent(hwndDlg),
                                    PSWIZB_BACK|PSWIZB_NEXT);
            _RefreshConfigDSInfo(hwndDlg);
            break;
         }

         case PSN_KILLACTIVE:

         // This notification is sent upon deactivation of the
         // property page. Here you can do whatever might be
         // necessary for this action, such as saving the state
         // of the controls. You should also reset thethe state
         // of the wizard buttons here, as both the Back and Next
         // buttons should be active when you leave the
         // QueryUser function.
         //
         // NOTE: If you do not want the page deactivated, return
         // -1.

            PropSheet_SetWizButtons(GetParent(hwndDlg),
                                    PSWIZB_BACK|PSWIZB_NEXT);
            break;

         case PSN_WIZBACK:
            _SaveConfigDSInfo(hwndDlg);
            break;

         case PSN_WIZNEXT:
         {
            UINT uLdapPort = 0;
            char *szLdapHost = NULL;
            char *szLdapSuffix = NULL;
            BOOL bSuffixCheck = TRUE;
            _SaveLdapInfo(hwndDlg);
            ParseLdapUrl(cd.szLdapUrl, &szLdapHost, &uLdapPort,
                         &szLdapSuffix);

            if(!IsValidLdapServer(szLdapHost, uLdapPort, NULL,
               NULL, NULL))
            {
               char szErrMsg[MAX_PATH];
               LoadString(mi.m_hModule, IDS_ERR_BAD_LDAPURL,
                          szErrMsg,sizeof(szErrMsg));

               if(NsSetupMessageBox(GetParent(hwndDlg),
                  szErrMsg, NULL, MB_ICONWARNING | MB_YESNO) ==
                  IDYES)
               {
                  SetFocus(GetDlgItem(hwndDlg, IDC_LDAPURL));
                  SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
                  bValueReturned = TRUE;
                  break;
               }
            }
            if(szLdapHost) setupFree(szLdapHost);
            if(szLdapSuffix) setupFree(szLdapSuffix);
            break;
         }

         case PSN_QUERYCANCEL:

         // This notification is sent when the user clicks the
         // Cancel button. It is also sent in response to the
         // WM_CLOSE messages issued by PSN_WIZBACK and
         // PSN_WIZNEXT. Make sure that we only process this
         // message if the result is not back or next so that we
         // don't nuke the return value assigned by PSN_WIZBACK
         // or PSN_WIZNEXT.
         //
         // NOTE: To prevent the cancel from occuring, return -1.

         if(mi.m_nResult != NS_WIZBACK && mi.m_nResult !=
            NS_WIZNEXT)
         {
            if(QueryExit(hwndDlg))
            {
               mi.m_nResult = NS_WIZCANCEL;
            }
            else
            {
               SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
               bValueReturned = TRUE;
            }
         }
         break;
      }
      break;
   }
   return bValueReturned;
}

static void _RefreshLdapUser(HWND hwndDlg)
{
   BOOL bResult = FALSE;
   bResult = SetDlgItemText(hwndDlg, IDC_USERID, cd.szLdapUser);
   bResult = SetDlgItemText(hwndDlg, IDC_PASSWORD, cd.szLdapPWD);
}

static void _SaveLdapUser(HWND hwndDlg)
{
   BOOL bResult = FALSE;
   char szTemp[MAX_PATH];
   GetDlgItemText(hwndDlg, IDC_USERID, szTemp, MAX_PATH);
   cd.szLdapUser = setupStrdup(szTemp);
   GetDlgItemText(hwndDlg, IDC_PASSWORD, szTemp, MAX_PATH);
   cd.szLdapPWD = setupStrdup(szTemp);
}

static BOOL CALLBACK _DialogProcLdapUser(HWND hwndDlg, UINT uMsg,
                                         WPARAM wParam,
                                         LPARAM lParam)
{
   BOOL bValueReturned = FALSE;
   BOOL bResult = TRUE;
   switch(uMsg)
   {
      case WM_INITDIALOG:
      {
         HGDIOBJ hGdiObj;
         HWND heditwnd;
         hGdiObj = GetStockObject(DEFAULT_GUI_FONT);
         SendMessage(hwndDlg, WM_SETFONT, (WPARAM) ((struct
                     HFONT__ *)hGdiObj) , MAKELPARAM(TRUE, 0));
         heditwnd = GetDlgItem(hwndDlg, IDC_USERID);
         SendMessage(heditwnd, WM_SETFONT, (WPARAM) ((struct
                     HFONT__ *)hGdiObj) , MAKELPARAM(TRUE, 0));
         cd.hwndPage[PG_SECOND] = hwndDlg;
         _RefreshLdapUser(hwndDlg);
      }
      break;

      case WM_COMMAND:

      // Windows sends WM_COMMAND messages whenever the user
      // clicks on a control in your property page. If you need
      // to perform some special action, such as validating data
      // or responding to a button click, do it here.

         break;

      case WM_NOTIFY:

      // Windows sends WM_NOTIFY messages to your property page
      // whenever something interesting happens to the page.
      // This could be page activation/deactivation, a button
      // click, etc. The wParam parameter contains a pointer to
      // the property page. The lParam parameter contains a
      // pointer to an NMHDR structure. The code field of this
      // structure contains the notification message code being
      // sent. The property sheet API allows you to alter the
      // behavior of these messages by returning a value for each
      // message. To return a value, use the SetWindowLong
      // Windows Util function.

      switch(((NMHDR*)lParam)->code)
      {
         case PSN_SETACTIVE:
         {
            CenterWindow(GetParent(hwndDlg));
            PropSheet_SetWizButtons(GetParent(hwndDlg),
                                    PSWIZB_BACK | PSWIZB_NEXT);
            _RefreshLdapUser(hwndDlg);
            break;
         }

         case PSN_KILLACTIVE:

         // This notification is sent upon deactivation of the
         // property page. Here you can do whatever might be
         // necessary for this action, such as saving the state
         // of the controls. You should also reset the state of
         // the wizard buttons here, as both the Back and
         // Nextbuttons should be active when you leave the
         // QueryUser function.
         //
         // NOTE: If you do not want the page deactivated, return
         // -1.

         PropSheet_SetWizButtons(GetParent(hwndDlg),
                                 PSWIZB_BACK | PSWIZB_NEXT);
         break;

         case PSN_WIZBACK:
            _SaveLdapUser(hwndDlg);
            break;

         case PSN_WIZNEXT:
         {
            UINT uLdapPort = 0;
            char *szLdapHost = NULL;
            char *szLdapSuffix = NULL;
            char *szLdapUser = NULL;

            _SaveLdapUser(hwndDlg);
            ParseLdapUrl(cd.szLdapUrl, &szLdapHost, &uLdapPort,
                         &szLdapSuffix);
            szLdapUser = setupStrdup(cd.szLdapUser);

            if(!IsValidLdapUser(szLdapHost, uLdapPort,
               szLdapSuffix, &szLdapUser, cd.szLdapPWD, TRUE))
            {
               char szErrMsg[MAX_PATH];
               LoadString(mi.m_hModule, IDS_ERR_BAD_USER,
                          szErrMsg,sizeof(szErrMsg));

               if(NsSetupMessageBox(GetParent(hwndDlg), szErrMsg,
                  NULL, MB_ICONWARNING | MB_YESNO) == IDYES)
               {
                  bValueReturned = TRUE;
                  SetFocus(GetDlgItem(hwndDlg, IDC_USERID));
                  SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
                  if(szLdapHost) setupFree(szLdapHost);
                  if(szLdapSuffix) setupFree(szLdapSuffix);
                  if(szLdapUser) setupFree(szLdapUser);
                  break;
               }
            }
            if(szLdapHost) setupFree(szLdapHost);
            if(szLdapSuffix) setupFree(szLdapSuffix);
            if(szLdapUser) setupFree(szLdapUser);
            mi.m_nResult = NS_WIZNEXT;
            SendMessage(GetParent(hwndDlg), WM_CLOSE, 0, 0);
            break;
         }

         case PSN_QUERYCANCEL:

         // This notification is sent when the user clicks the
         // Cancel button. It is also sent in response to the
         // WM_CLOSE messages issued by PSN_WIZBACK and
         // PSN_WIZNEXT. Make sure that we only process this
         // message if the result is not back or next so that we
         // don't nuke the return value assigned by PSN_WIZBACK
         // or PSN_WIZNEXT.
         //
         // NOTE: To prevent the cancel from occuring, return -1.

         if(mi.m_nResult != NS_WIZBACK && mi.m_nResult !=
            NS_WIZNEXT)
         {
            if(QueryExit(hwndDlg))
            {
               mi.m_nResult = NS_WIZCANCEL;
            }
            else
            {
               SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
               bValueReturned = TRUE;
            }
         }
         break;
      }
      break;
   }
   return bValueReturned;
}

////////////////////////////////////////////////////////////////
// PreInstall
//
// This function is called by the installation framework before
// asking the user any questions. Here you should determine if
// all of the requisites for installing this component are being
// met. If this operation succeeds return TRUE, otherwise
// display an error message and return FALSE to abort
// installation.

BOOL __declspec(dllexport) PreInstall(void)
{
return TRUE;
}

///////////////////////////////////////////////////////////////
// QueryUser
//
// This function is specified by the AskOptions directive and
// called by the installation framework to query
// the user for information about your component. Here you should
// ask all of the questions required to install your component as
// a series of wizard property sheets.

INT __declspec(dllexport) QueryUser(HWND hwndParent, INT
                                     nDirection)
{
   PROPSHEETPAGE psp[NUM_PROP_PAGES];
   int nPages = 2;
   int nStartPage = 0;
   mi.m_nResult = NS_WIZERROR;
   nStartPage =(nDirection == NS_WIZNEXT) ? PG_FIRST : PG_SECOND;
   AddWizardPage(mi.m_hModule, &psp[PG_FIRST], IDD_LDAP_INFO,
                 _DialogProcConfigDSInfo);
   AddWizardPage(mi.m_hModule, &psp[PG_SECOND], IDD_LDAPUSER,
                 _DialogProcLdapUser);
   if(WizardDialog(mi.m_hModule, hwndParent, psp, nPages,
      nStartPage) < 0)
   {
      mi.m_nResult = NS_WIZERROR;
   }
   return mi.m_nResult;
}

////////////////////////////////////////////////////////////////
// RetrieveUserOptions
//
// This function is called by the installation framework after
// all questions, for all components, have been asked. Here you
// should provide a detailed summary explaining all of the
// choices selected by the user.
//
// IMPORTANT NOTE: Each line MUST end in a carriage return/line
// feed combination ("\r\n") as this string is placed in an edit
// control. Edit controls do not properly handle single "\n"
// end-of-line characters.

VOID __declspec(dllexport) RetrieveUserOptions(char *lpszSummary)
{
   char* psz = lpszSummary;
   if(cd.szLdapUrl)
   {
      psz += sprintf(psz, CONFIG_DS_URL , cd.szLdapUrl);
   }
}

////////////////////////////////////////////////////////////////
// WriteProductCache
//
// This function is called by the installation framework when the
// user clicks Next at the summary screen. Here you should write
// all information entered by the user into the installation
// cache for use during silent installation. Data written to this
// file is not interpreted by the framework, and may consist of
// any values that you will need to perform the installation (not
// just values entered by the user). If this operation succeeds
// return TRUE, otherwise display an error message and return
// FALSE to indicate an error.

BOOL __declspec(dllexport)
WriteProductCache(LPCSTR lpszCacheFileName,LPCSTR
                  lpszSectionName)
{
   char temp [MAX_PATH];
   char * c;
   if (_infoFile == NULL)
   {
      _infoFile = strdup(lpszCacheFileName);
   }
   setupWriteInfString(lpszSectionName, LDAPUSER,
                       cd.szLdapUser, lpszCacheFileName);
   setupWriteInfString(lpszSectionName, LDAPPWD,
                       cd.szLdapPWD, lpszCacheFileName);
   setupWriteInfString(lpszSectionName, CONFIG_DS_URL ,
                       cd.szConfigDSURL, lpszCacheFileName);
   return TRUE;
}

////////////////////////////////////////////////////////////////
// ReadProductCache
//
// This function is called by the installation framework during
// silent install to intialize your data from the local section of
// the cache created above. Here you should read any information
// stored in the installation cache's product-specific section
// that you need. If this operation succeeds return TRUE,
// otherwise display an error message and return FALSE to
// indicate an error.

BOOL __declspec(dllexport)
ReadProductCache(LPCSTR lpszCacheFileName,LPCSTR lpszSectionName)
{
   if (_infoFile == NULL)
   {
      _infoFile = strdup(lpszCacheFileName);
   }
   cd.szLdapUser = setupGetInfString(lpszSectionName, LDAPUSER,
                                     NULL, lpszCacheFileName);
   cd.szLdapPWD = setupGetInfString(lpszSectionName, LDAPPWD,
                                    NULL, lpszCacheFileName);
   cd.szConfigDSURL = setupGetInfString(lpszSectionName,
                                        CONFIG_DS_URL, "",
                                        lpszCacheFileName);
   return TRUE;
}

////////////////////////////////////////////////////////////////
// DllMain
//
// The Windows DLL main entry point. Called upon loading the DLL
// into memory. Perform all initialization in the
// DLL_PROCESS_ATTACH reason handler, and release any resources
// that you have allocated in the DLL_PROCESS_DETACH message
// handler. See the Windows Util documentation for more
// information on this function.

BOOL WINAPI DllMain(HANDLE hModule, ULONG ulReasonForCall,
                    LPVOID lpReserved)
{
   switch(ulReasonForCall)
   {
      case DLL_PROCESS_ATTACH:
         ZeroMemory(&mi, sizeof(MODULEINFO));
         mi.m_hModule = hModule;
         _InitializeControlData();
         break;
      case DLL_PROCESS_DETACH:
         break;
      case DLL_THREAD_ATTACH:
         break;
      case DLL_THREAD_DETACH:
         break;
   }
   return TRUE;
}


Storing User Input

You can use the data gathered during pre-installation to change package information files or configure your product. Changing a package information file involves calling setupWriteInfString or setupWriteInfInt. Configuring your product involves writing data to a cache file.


To Change Your Package Information File

  1. Gather and process all pre-installation data.

  2. Write one or more functions to determine what directives to change. You should base these functions on the data gathered above.

  3. Change your package information file.

    Call setupWriteInfString to modify strings. Call setupWriteInfInt to modify integers.

    The functions have the following syntax

    setupWriteInfString(productNickname, directiveName, infString, cacheFile);

    setupWriteInfInt(productNickname, directiveName, infInt, cacheFile);

    The arguments are as follows:

    productNickname is your product's nickname (for instance, slapd or admin).

    directiveName is the directive to change. Use the variable names listed in include/setupdefs.h. For a complete discussion of directives, see Chapter 3 "Information Files."

    infString is the string value you wish to set for the listed directive.

    infInt is the integer value you wish to set for the listed directive.

    cacheFile is the name of the file in which you will save your cache (typically install.inf).

  4. When you are finished modifying your package information file, you can save the cache or return an exit value to the Common Install Shell.


To Write Data to a Cache File

  1. Gather and process all pre-installation data.

  2. Call writeGlobalCache to save all Common Install Shell information to the installation cache.

  3. Call WriteLocalCache to write any product-specific information to the installation cache.

  4. (Optional) Change your package information file. For more information, see To Change Your Package Information File" on page 76.

  5. Save the cache and return an exit value to the Common Install Shell.


Compiling and Packaging the DLL

When you have finished writing code to manage your dialog boxes and input, you can compile and package your DLL. If you want to use the information you collected during installation to configure your product, you will need to write a post-installation program. This is discussed in Chapter 6.

If you are not going to configure your product or want to test your pre-installation program, follow the directions below.


To Compile the DLL

  • Follow your development environment's instructions for compiling and linking a DLL. The DLL must contain all property screens as well as your pre-installation program. For more information on compiling, see Chapter 9 "Compiling and Linking."


To Package the DLL

  • Copy the DLL into the directory specified by the SourcePath directive in your package information file.


Modifying your Package Information File

Once you have written, compiled, and packaged your pre-installation program, you must modify your package information file to use it. On Windows NT, this involves specifying one or more directives. At a minimum, you must include the AskOptions directive. The value for this directive is the name of the function used to display dialog boxes. This function must be part of your DLL.

For example, if this function is called QueryUser, it should be listed in the package information file as AskOptions=QueryUser.

You can specify the names of additional pre-installation functions. Like AskOptions, these use the directive = function_name syntax. The Common Install Shell will call these functions in the order listed under Managing Dialogs and Processing Input," which begins on page 64.

For a complete list of applicable directives, see Chapter 3 "Information Files."


Previous     Contents     Index     Next     
Copyright (C) 2005 Red Hat, Inc. All rights reserved.
This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later (the latest version is presently available at http://www.opencontent.org/openpub/

Last Updated September 21, 2001