home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / sysmgmt / setup / infinst / doinst.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  16KB  |  497 lines

  1.  
  2. /******************************************************************************\
  3. *       This is a part of the Microsoft Source Code Samples. 
  4. *       Copyright (C) 1995-1997 Microsoft Corporation.
  5. *       All rights reserved. 
  6. *       This source code is only intended as a supplement to 
  7. *       Microsoft Development Tools and/or WinHelp documentation.
  8. *       See these sources for detailed information regarding the 
  9. *       Microsoft samples programs.
  10. \******************************************************************************/
  11.  
  12. #include <windows.h>    // includes basic windows functionality
  13. #include <string.h>     // includes the string functions
  14. #include <shlobj.h>     // To update the desktop
  15. #include <regstr.h>
  16.  
  17. #include "setupapi.h"   // includes the inf setup api 
  18. #include "instwiz.h"    // includes the application-specific information
  19. #include "infinst.h"    // includes the application-specific information
  20. #include "infdesc.h"    // includes the specifics of how this
  21.                         // inf is layed out, like the inf names
  22.                         // the HKeys and the DirIds
  23.  
  24. DWORD InstallFinish(BOOL DoRunOnce);
  25.  
  26. //
  27. //    FUNCTION: BOOL DoInstallation( HWND hWnd, INSTALLINFO * si )
  28. //
  29. //    PURPOSE: Install components via setupapi.dll.
  30. //
  31. //   COMMENTS:
  32. //    
  33. //      The function does inf install operations based
  34. //      on the content of the INSTALLINFO data.
  35. //      These steps could be done during the wizard but
  36. //      this technique allows for installations with user input.
  37. //
  38. // This routine will take a INSTALLINFO
  39. // and do an installation based on those settings
  40. // using the setupapi.dll
  41.  
  42. BOOL DoInstallation( HWND hWnd, INSTALLINFO * si )
  43. {
  44.  
  45.     HINF hInf;
  46.     char szSourcePath[MAX_PATH];
  47.     char szInfFileName[MAX_PATH];
  48.     
  49.     DWORD dwResult;
  50.     BOOL bResult = FALSE;
  51.  
  52.     // Context for my call back routine
  53.     MY_INSTALL_DATA MyInstallData;
  54.  
  55.     HSPFILEQ FileQueue;
  56.  
  57.     //
  58.     // First we setup the inf to what the wizard collected
  59.     // note the hInf will maintain the information and let
  60.     // you build copy list as you go
  61.     // This sample seperates the wizard from the setapi stuff
  62.     // just to keep the setupapi stuff in one place
  63.     //
  64.  
  65.     //
  66.     // The install process overview is: 
  67.     //   TASK                       SETUPAPI
  68.     //   open a specific inf        SetupOpenInfFile
  69.     //   call the wizard for input  CreateWizard (implemented in instwiz.c)
  70.     //   set the directory ids      SetupSetDirectoryId 
  71.     //   create a file queue        SetupOpenFileQueue
  72.     //   create a queue context     SetupInitDefaultQueueCallback
  73.     //   add files to queue         SetupInstallFilesFromInfSection
  74.     //   do the copy                SetupCommitFileQueue
  75.     //???   do the registry stuff      SetupInstallFromInfSection
  76.     //   close the queue            SetupTermDefaultQueueCallback
  77.     //   close the inf              SetupCloseFileQueue
  78.     
  79.     //
  80.     // In this sample we assume the inf is in the base of the
  81.     // base installation source path--it usually is for most installs
  82.     //
  83.     
  84.     GetModuleFileName(NULL, szSourcePath, _MAX_PATH);
  85.     *(strrchr(szSourcePath, '\\') + 1) = '\0';        // Strip setup.exe off path
  86.  
  87.     strcpy(szInfFileName, szSourcePath);
  88.     strcat(szInfFileName, "product.inf");
  89.  
  90.     //
  91.     // Get inf handle
  92.     // must know where the inf is located 
  93.     // SetupOpenInfFile will only look in windows\inf by default
  94.     //
  95.  
  96.     hInf = SetupOpenInfFile ( 
  97.         szInfFileName,       // If path,needs full path, else looks in %windir%\inf
  98.         NULL,                // Inf Type, matches Class in [Version] section SetupClass=SAMPLE
  99.         INF_STYLE_WIN4,      // or INF_STYLE_OLDNT
  100.         NULL                 // Line where error occurs if inf is has a problem
  101.         );
  102.  
  103.     if (hInf == INVALID_HANDLE_VALUE) 
  104.     {
  105.         dwResult = GetLastError();
  106.  
  107.         //   
  108.         // TODO: handle case where inf cannot be opened
  109.         //       by asking the user to locate the inf
  110.     
  111.         return FALSE;
  112.     }
  113.  
  114.     // Run the wizard
  115.     if (CreateWizard(hWnd, si->hInst))
  116.     {
  117.         RuntimeRegistration(si);
  118.     }
  119.     else
  120.     {
  121.         SetupCloseInfFile(hInf);
  122.         return FALSE;
  123.     }
  124.  
  125.     //
  126.     // Special case the uninstall destitation path
  127.     // this is because we need to get the registry value
  128.     // for where to delete the files from. 
  129.     //
  130.  
  131.     if (IDC_INSTALL_TYPE_UNINSTALL == si->iInstall_Type)
  132.     {
  133.         // get path installed to here!
  134.         // query registry for si->pszDestPath
  135.  
  136.         GetRegString (MYPRODUCT_KEY, TEXT("DestinationPath"),
  137.                 si->pszDestPath);
  138.     }
  139.  
  140.     //
  141.     // Directory ids are set per HINF
  142.     //
  143.     // TODO: verify dest path as a valid file name
  144.  
  145.     bResult = SetupSetDirectoryId(hInf,          // the HINF that will hold these ids
  146.                         (DWORD) DESTINATION_DIR, // the id that matchs one in the inf
  147.                         si->pszDestPath);        // the user's string
  148.     
  149.     if(!bResult) 
  150.     {
  151.         dwResult = GetLastError();
  152.         
  153.         // TODO: handle case where we cannot set dirids, should we default or bail?
  154.         //       perhaps write this out to an error log
  155.         // 
  156.         // Close the inf file and return
  157.         //
  158.  
  159.         SetupCloseInfFile(hInf);
  160.         return FALSE;
  161.     }
  162.  
  163.     //
  164.     // Create a Setup file queue and initialize the default Setup
  165.     // queue callback routine.
  166.     //
  167.  
  168.     FileQueue = SetupOpenFileQueue();
  169.  
  170.     if(!FileQueue || (FileQueue == INVALID_HANDLE_VALUE)) 
  171.     {
  172.         dwResult = ERROR_NOT_ENOUGH_MEMORY;
  173.         
  174.         //
  175.         // Close the queue and return
  176.         //
  177.  
  178.         SetupCloseInfFile(hInf);
  179.         return FALSE;
  180.     }
  181.  
  182.     //
  183.     // This initialize memory for the default call back
  184.     // This context MUST be used if our private callback routine 
  185.     //     wants to callback into the default routine, we do it here with 
  186.     //     a global value for the default context
  187.     //
  188.     // We want to use the default progress dialog.
  189.     // We could override this by specifying a window that
  190.     // will handle the ui in param 2.  
  191.     // By not passing a progress HWND this is the same as 
  192.     // using SetupInitDefaultQueueCallback.
  193.     //
  194.  
  195.     MyInstallData.pDefaultContext = SetupInitDefaultQueueCallbackEx(
  196.                     hWnd,  // HWND of owner window
  197.                     NULL,  // HWND of alternate progress dialog which receives -- for example
  198.                            //      if you wanted to display your progress bar right in the wizard
  199.                     0,     // Message sent to above window indicating a progress message
  200.                     0,     // DWORD Reserved
  201.                     NULL   // PVOID Reserved
  202.                     );
  203.  
  204.     if(!(MyInstallData.pDefaultContext)) 
  205.     {
  206.         dwResult = ERROR_NOT_ENOUGH_MEMORY;
  207.  
  208.         //
  209.         // Close the queue and the inf file and return
  210.         //
  211.  
  212.         SetupCloseFileQueue(FileQueue);
  213.         SetupCloseInfFile(hInf);
  214.         return FALSE;
  215.     }
  216.  
  217.     //
  218.     // Now that we have a FileQueue, a Queue Context, and an HINF
  219.     // we want to map the DirectoryIds in the INF to the ones
  220.     // obtained by the user 
  221.  
  222.     //
  223.     // Queue file operations and commit the queue.
  224.     //
  225.  
  226.     //
  227.     // Install each option they have selected
  228.     //
  229.  
  230.     if (IDC_INSTALL_TYPE_UNINSTALL == si->iInstall_Type)
  231.     {
  232.         //TODO: allow the user to remove specific components
  233.         //      note you would want to update the wizard to reflect this
  234.         
  235.         bResult = SetupInstallFilesFromInfSection(
  236.                   hInf,     // HINF that has the directory ids set above
  237.                   NULL,          // layout.inf if you have one, this a convient
  238.                                  //     place to do all of your file to media id mapping
  239.                   FileQueue,     // Queue to add files to
  240.                   TEXT("MyProgramUninstall"),   // SectionName,
  241.                   szSourcePath,    // Path where the source files are located
  242.                   SP_COPY_NEWER);// The controls how to version check
  243.                                  // and how to prompt
  244.     }
  245.     else
  246.     {
  247.  
  248.       if (si->iCustom_Options1){
  249.         bResult = SetupInstallFilesFromInfSection(
  250.                       hInf,     // HINF that has the directory ids set above
  251.                       NULL,          // layout.inf if you have one, this a convient
  252.                                      //     place to do all of your file to media id mapping
  253.                       FileQueue,     // Queue to add files to
  254.                       INF_OPTION1,   // SectionName,
  255.                       szSourcePath,    // Path where the source files are located
  256.                       SP_COPY_NEWER);// The controls how to version check
  257.                                      // and how to prompt
  258.         }
  259.         
  260.         if (si->iCustom_Options2) {
  261.             bResult = SetupInstallFilesFromInfSection(hInf, NULL, 
  262.                 FileQueue, INF_OPTION2, szSourcePath, SP_COPY_NEWER );
  263.         }
  264.  
  265.         if (si->iCustom_Options3) {
  266.             bResult = SetupInstallFilesFromInfSection(hInf, NULL, 
  267.                 FileQueue, INF_OPTION3, szSourcePath, SP_COPY_NEWER );
  268.         }
  269.  
  270.         if (si->iCustom_Options4) {
  271.             bResult = SetupInstallFilesFromInfSection(hInf, NULL, 
  272.                 FileQueue, INF_OPTION4, szSourcePath, SP_COPY_NEWER );
  273.         }
  274.     }
  275.  
  276.     //
  277.     // All the files for each component are now in one queue
  278.     // now we commit it to start the copy ui, this way the
  279.     // user has one long copy progress dialog--and for a big install
  280.     // can go get the cup of coffee 
  281.     //
  282.     if(bResult) {
  283.         
  284.         bResult = SetupCommitFileQueue(
  285.                 hWnd,                      // Owner
  286.                 FileQueue,                 // Queue with the file list
  287.                 (PSP_FILE_CALLBACK) MyQueueCallback, 
  288.                                            // This is our handler, it calls the default for us
  289.                                            // NOTE:
  290.                                            // (PSP_FILE_CALLBACK) SetupDefaultQueueCallback
  291.                                            // would use the default message handler automatically
  292.                 &MyInstallData             // Pointer to resources allocated with SetupInitDefaultQueueCallback/Ex                 
  293.                 );
  294.  
  295.         dwResult = bResult ? NO_ERROR : GetLastError();
  296.  
  297.     } else {
  298.         
  299.         dwResult = GetLastError();
  300.     
  301.     }
  302.  
  303.     //
  304.     // Do registry munging, etc. 
  305.     // NOTE: you can do the entire install
  306.     // for a section with this api but in this case
  307.     // we build the file list conditionally and
  308.     // do only out ProductInstall section for registy stuff
  309.     // Also using SPINST_FILES will do the files
  310.     // as above but only one section at a time
  311.     // so the progress bar would keep completing and starting over
  312.     // SPINST_ALL does files, registry and inis
  313.     // 
  314.     if (si->iCustom_Options1)
  315.     {
  316.       bResult = SetupInstallFromInfSection(
  317.             hWnd,
  318.             hInf,
  319.             INF_OPTION1,
  320.             SPINST_REGISTRY | SPINST_INIFILES,
  321.             NULL,
  322.             NULL,//szSourcePath,    // Path where the source files are located
  323.             0,//SP_COPY_NEWER,
  324.             NULL,//(PSP_FILE_CALLBACK) MyQueueCallback, 
  325.             NULL,//&MyInstallData,
  326.             NULL, 
  327.             NULL
  328.             );
  329.     }
  330.  
  331.     //
  332.     // Perserve first non-success error code.
  333.     //
  334.     if(!bResult && (dwResult == NO_ERROR)) {
  335.     
  336.         dwResult = GetLastError();
  337.     
  338.     }
  339.  
  340.     //
  341.     // We're done so free the context, close the queue,
  342.     // and release the inf handle
  343.     //
  344.  
  345.     SetupTermDefaultQueueCallback(MyInstallData.pDefaultContext);
  346.     SetupCloseFileQueue(FileQueue);
  347.     SetupCloseInfFile(hInf);
  348.     
  349.     if(dwResult != NO_ERROR) {
  350.     
  351.         // TODO: Log an error here
  352.     
  353.     }
  354.  
  355.     if (bResult) {
  356.     
  357.         // do whatever you need to complete a successful install
  358.  
  359.         //
  360.         // Refresh the desktop for folders and icons.
  361.         //
  362.         InstallFinish(TRUE);    
  363.  
  364.     } else {
  365.  
  366.         MessageBox( hWnd, 
  367.             TEXT("Installation script did not complete successfully!"), 
  368.             TEXT("Product Sample Install Error"), 
  369.             MB_OK);
  370.     }
  371.     
  372.     return TRUE;
  373. }
  374.  
  375. LRESULT 
  376. WINAPI
  377. MyQueueCallback (
  378.     IN MY_INSTALL_DATA* pMyInstallData,
  379.     IN UINT Notification,
  380.     IN UINT Param1,
  381.     IN UINT Param2
  382.     )
  383. {
  384.     if (SPFILENOTIFY_DELETEERROR == Notification)
  385.     {
  386.         // Skip any file delete errors
  387.         // this sample only deletes files on an unintall
  388.         // so if the delete encounters an error simply skip the operation
  389.         // and continue processing the queue
  390.         return FILEOP_SKIP;
  391.     }
  392.     else
  393.     {
  394.         // Pass all other notifications through without modification
  395.         return SetupDefaultQueueCallback(pMyInstallData->pDefaultContext, 
  396.                                          Notification, Param1, Param2);
  397.     }
  398. }
  399.  
  400.  
  401. DWORD
  402. InstallFinish(
  403.     IN BOOL DoRunOnce
  404.     )
  405. /*++
  406.  
  407. Routine Description:
  408.  
  409.     This routine sets up runonce/grpconv to run after a successful INF installation.
  410.  
  411. Arguments:
  412.  
  413.     DoRunOnce - If TRUE, then invoke (via WinExec) the runonce utility to perform the
  414.         runonce actions.  If this flag is FALSE, then this routine simply sets the
  415.         runonce registry values and returns.
  416.  
  417.         NOTE:  The return code from WinExec is not currently being checked, so the return
  418.         value of InstallStop only reflects whether the registry values were set up
  419.         successfully--_not_ whether 'runonce -r' was successfully run.
  420.  
  421. Return Value:
  422.  
  423.     If successful, the return value is NO_ERROR, otherwise it is the Win32 error code
  424.     indicating the error that was encountered.
  425.  
  426. --*/
  427. {
  428.     HKEY  hKey, hSetupKey;
  429.     DWORD Error;
  430.     LONG l;
  431.  
  432.     //
  433.     // First, open the key "HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce"
  434.     //
  435.     if((l = RegOpenKeyEx(HKEY_LOCAL_MACHINE, 
  436.                          REGSTR_PATH_RUNONCE, 
  437.                          0, KEY_ALL_ACCESS, &hKey)) != ERROR_SUCCESS) {
  438.         return (DWORD)l;
  439.     }
  440.  
  441.     //
  442.     // If we need to run the runonce exe for the setup key...
  443.     //
  444.     if(RegOpenKeyEx(hKey,
  445.                     TEXT("Setup"),
  446.                     0,
  447.                     KEY_READ,
  448.                     &hSetupKey) == ERROR_SUCCESS) {
  449.         //
  450.         // We don't need the key--we just needed to check its existence.
  451.         //
  452.         RegCloseKey(hSetupKey);
  453.  
  454.         //
  455.         // Add the runonce value.
  456.         //
  457.         Error = (DWORD)RegSetValueEx(hKey,
  458.                                      TEXT("Wrapper"),
  459.                                      0,
  460.                                      REG_SZ,
  461.                                      TEXT("runonce"),
  462.                                      sizeof(TEXT("runonce"))
  463.                                     );
  464.     } else {
  465.         //
  466.         // We're OK so far.
  467.         //
  468.         Error = NO_ERROR;
  469.     }
  470.  
  471.     //
  472.     // GroupConv is always run.
  473.     //
  474.     if(RegSetValueEx(hKey,
  475.                      TEXT("GrpConv"),
  476.                      0,
  477.                      REG_SZ,
  478.                      TEXT("grpconv -o"),
  479.                      sizeof(TEXT("grpconv -o"))) != ERROR_SUCCESS) {
  480.         //
  481.         // Since GrpConv is always run, consider it a more serious error than any error
  482.         // encountered when setting 'runonce'.  (This decision is rather arbitrary, but
  483.         // in practice, it should never make any difference.  Once we get the registry key
  484.         // opened, there's no reason either of these calls to RegSetValueEx should fail.)
  485.         //
  486.         Error = (DWORD)l;
  487.     }
  488.  
  489.     RegCloseKey(hKey);
  490.  
  491.     if(DoRunOnce) {
  492.         WinExec("runonce -r", SW_SHOWNORMAL);
  493.     }
  494.  
  495.     return Error;
  496. }
  497.