home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / sysmgmt / tasksched / sa.cpp < prev    next >
C/C++ Source or Header  |  1997-08-29  |  29KB  |  945 lines

  1. // ===========================================================================
  2. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  3. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  4. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  5. // PARTICULAR PURPOSE.
  6. //
  7. // Copyright 1997 Microsoft Corporation.  All Rights Reserved.
  8. // ===========================================================================
  9. //
  10. //+--------------------------------------------------------------------------
  11. //
  12. // File:        sa.cpp
  13. //
  14. // Contents:    All code needed to build the simple job submission tool
  15. //
  16. // Purpose:     Demonstration of calls to methods of the following new
  17. //              interfaces:
  18. //                  1. ITaskScheduler
  19. //                  2. ITask
  20. //                  3. ITaskTrigger
  21. //                  4. IEnumWorkItems
  22. //
  23. // Comments:    This application is similar in functionality to
  24. //              the "AT" command provided with Windows NT.  However,
  25. //              the AT command functions through the NetSchedule APIs,
  26. //              as opposed to the COM interfaces demonstrated here.
  27. //
  28. //              The Task Scheduler replaces ATSVC.EXE and implements
  29. //              those APIs as a restricted subset of the new ones
  30. //              demonstrated here.
  31. //
  32. //              On Win9x systems, the Task Scheduler replaces
  33. //              the System Agent, providing more flexible and reliable
  34. //              scheduling options than previously available.
  35. //              However, although the SAGE jobs are converted
  36. //              to Task Scheduler work items, the actual
  37. //              SAGE API has been abandoned and is not implemented.
  38. //
  39. //              The Task Scheduler APIs provide a greater degree of
  40. //              freedom and robustness than those APIs.  This code
  41. //              demonstrates a stripped version of AT rewritten to
  42. //              use the new APIs.
  43. //
  44. // Additional:  The Task Scheduler service must be running.  It is easily
  45. //              started by the command "net start schedule" if it is not
  46. //              running.  It is also trivial to start it through
  47. //              the service control manager APIs. Doing it in the code
  48. //              directly makes a good enhancement to the program below.
  49. // 
  50. //              Since there is not a service controller on Win9x, 
  51. //              the user may start the service by executing the
  52. //              binary directly from the command line (mstask.exe).
  53. //              Win9x users will see a system tray icon if the service 
  54. //              is running.
  55. //
  56. //              The service may also be started from the folder UI available
  57. //              directly by opening "My Computer" and then opening the 
  58. //              "Scheduled Tasks" folder.  The start command appears on the
  59. //              "Advanced" menu.
  60. //
  61. //              Sample code to start and stop the service, on both 
  62. //              Windows NT and Win9x based systems appears in the
  63. //              documentation.
  64. //
  65. //              If the Task Scheduler service is not running, most 
  66. //              of this program will fail to execute.  If there are 
  67. //              tasks waiting to execute after login, the service 
  68. //              will have been started by default.
  69. //             
  70. //---------------------------------------------------------------------------
  71.  
  72. #include <wchar.h>
  73. #include <windows.h>
  74. #include <mbctype.h>
  75. #include <initguid.h>
  76. #include <mstask.h>
  77. #include <msterr.h>
  78.  
  79. //+--------------------------------------------------------------------------
  80. // Global variables
  81. //---------------------------------------------------------------------------
  82.  
  83. ITaskScheduler *g_pITaskScheduler = NULL;
  84.  
  85. //+--------------------------------------------------------------------------
  86. // Function Prototypes
  87. //---------------------------------------------------------------------------
  88.  
  89. HRESULT Init(void);
  90. void Cleanup(void);
  91. void DisplayHelp(LPWSTR);
  92. HRESULT EnumerateJobs(void);
  93. HRESULT DeleteJob(LPWSTR);
  94. HRESULT AddJob(LPWSTR, LPSYSTEMTIME, LPWSTR, LPWSTR, LPWSTR);
  95. HRESULT ConvertToSystemTime(LPWSTR, LPSYSTEMTIME);
  96.  
  97.  
  98. //+--------------------------------------------------------------------------
  99. //
  100. // Function:        main
  101. //
  102. // Synopsis:        Entry point for code.  Parses command line,
  103. //                  and calls appropriate subfunction as a result.
  104. //
  105. //  Arguments:      See DisplayHelp().  Uses argv, argc to get 
  106. //                  command line args.
  107. //
  108. //  Returns:        S_OK for success or the failure code for failure.
  109. //
  110. //---------------------------------------------------------------------------
  111.  
  112. LONG _CRTAPI1 main (int argc, char **argv)
  113. {
  114.     HRESULT hr = S_OK;
  115.     UINT uCodePage;
  116.     char *lpcszDeleteFlag = "/DELETE";
  117.     WCHAR lpwszJobName[255], lpwszTime[32];
  118.     WCHAR lpwszUserName[64], lpwszPassword[64], lpwszCommand[255];
  119.     WCHAR lpwszProgName[255];
  120.     SYSTEMTIME tTime;
  121.  
  122.     // For protection
  123.  
  124.     g_pITaskScheduler = NULL;
  125.  
  126.     // String conversion initialization
  127.  
  128.     uCodePage = _getmbcp();
  129.    
  130.     MultiByteToWideChar(uCodePage, 0, argv[0],
  131.                         -1, lpwszProgName,
  132.                         255);
  133.  
  134.     // Check number of command line arguments.
  135.     //      1 argument      =   enumerate work items 
  136.     //      2 args          =   display help (don't init OLE)
  137.     //      3 args          =   delete work item 
  138.     //      4, 5 or 6 args  =   add a work item 
  139.     //      all others      =   error, display help
  140.  
  141.     if ((argc == 2) || (argc > 6))
  142.     {
  143.         DisplayHelp(lpwszProgName);
  144.         return hr;
  145.     }
  146.  
  147.     // Attempt to initialize OLE and fill in the global g_pITaskScheduler
  148.  
  149.     hr = Init();
  150.     if (FAILED(hr))
  151.     {
  152.         wprintf(L"Error: OLE initialization and instantiation failed\n");
  153.         return hr;
  154.     }
  155.     
  156.     switch(argc)
  157.     {
  158.         case 1:
  159.             // User would like to enumerate work items 
  160.  
  161.             hr = EnumerateJobs();
  162.             break;
  163.  
  164.         case 3:
  165.             if (! lstrcmpiA(argv[2], lpcszDeleteFlag))
  166.             {
  167.                 // User would like to delete work item in argv[1]
  168.  
  169.                 MultiByteToWideChar(uCodePage, 0, argv[1], 
  170.                                     -1, lpwszJobName,
  171.                                     255);
  172.  
  173.                 hr = DeleteJob(lpwszJobName);
  174.             }
  175.             else
  176.             {
  177.                 // User has made an error in command line args                
  178.                 
  179.                 wprintf(L"Error: Must be %s taskname.job /DELETE\n", lpwszProgName);
  180.                 wprintf(L"\n %s /?  for more help.\n", lpwszProgName);
  181.                 
  182.                 Cleanup();
  183.                 return E_FAIL;
  184.             }
  185.             break;
  186.  
  187.         case 4:
  188.         case 5:
  189.         case 6:
  190.             // User would like to add a work item.  The following
  191.             // argument cases apply:
  192.             //      4 - User has specified work item name, but not given 
  193.             //          username or password.  Will prompt later.
  194.             //      5 - User has specified work item name and username,
  195.             //          but no password.  Will prompt later.            
  196.             //      6 - User has specified all information.
  197.             
  198.                 uCodePage = _getmbcp();
  199.     
  200.                 MultiByteToWideChar(uCodePage, 0, argv[1], 
  201.                                     -1, lpwszJobName,
  202.                                     255);
  203.                 MultiByteToWideChar(uCodePage, 0, argv[2],
  204.                                     -1, lpwszTime,
  205.                                     32);
  206.  
  207.             hr = ConvertToSystemTime(lpwszTime, &tTime);
  208.             if (FAILED(hr))
  209.             {
  210.                 wprintf(L"Error: Conversion of command line time to system time failed.\n");
  211.                 Cleanup();
  212.                 return hr;
  213.             }
  214.             
  215.             *lpwszUserName = L'\0';
  216.             *lpwszPassword = L'\0';
  217.  
  218.             MultiByteToWideChar(uCodePage, 0, argv[3],
  219.                                 -1, lpwszCommand,
  220.                                 255);
  221.  
  222.             if (argc > 4)
  223.             {
  224.  
  225.                 MultiByteToWideChar(uCodePage, 0, argv[4],
  226.                                     -1, lpwszUserName,
  227.                                     64);
  228.             }
  229.             if (argc == 6)
  230.             {
  231.  
  232.                 MultiByteToWideChar(uCodePage, 0, argv[5],
  233.                                     -1, lpwszPassword,
  234.                                     64);
  235.             }
  236.             
  237.             hr = AddJob(lpwszJobName, &tTime, lpwszCommand, lpwszUserName, lpwszPassword);
  238.             
  239.             break;
  240.  
  241.         default:
  242.             hr = E_FAIL;
  243.             DisplayHelp(lpwszProgName);
  244.     }
  245.     
  246.     Cleanup();
  247.     return hr;
  248. }
  249.  
  250.  
  251. //+--------------------------------------------------------------------------
  252. //
  253. // Function:        Init()
  254. //
  255. // Synopsis:        Called to initialize and instantiate a task 
  256. //                  scheduler object.
  257. //
  258. // Arguments:       none (void)
  259. //
  260. // Returns:         HRESULT indicating success or failure.  S_OK on success.
  261. //
  262. // Side effect:     Sets global pointer g_pITaskScheduler, for use in other
  263. //                  functions.  
  264. //
  265. //---------------------------------------------------------------------------
  266.  
  267. HRESULT Init()
  268. {
  269.     HRESULT hr = S_OK;
  270.  
  271.     // Bring in the library
  272.  
  273.     hr = CoInitialize(NULL);
  274.     if (FAILED(hr))
  275.     {
  276.         return hr;
  277.     }
  278.  
  279.     // Create the pointer to Task Scheduler object
  280.     // CLSID from the header file mstask.h
  281.  
  282.     hr = CoCreateInstance(
  283.         CLSID_CTaskScheduler,
  284.         NULL,
  285.         CLSCTX_INPROC_SERVER,
  286.         IID_ITaskScheduler,
  287.         (void **) &g_pITaskScheduler);
  288.  
  289.     // Should we fail, unload the library
  290.  
  291.     if (FAILED(hr))
  292.     {
  293.         CoUninitialize();
  294.     }
  295.  
  296.     return hr;
  297. }
  298.  
  299.  
  300. //+--------------------------------------------------------------------------
  301. //
  302. // Function:    Cleanup()
  303. //
  304. // Synopsis:    Called to clean up OLE, memory, etc. before termination
  305. //
  306. // Arguments:   none (void)
  307. //
  308. // Returns:     nothing (void)
  309. //
  310. // Side effect: Cancels the global pointer g_pITaskScheduler.
  311. //
  312. //---------------------------------------------------------------------------
  313.  
  314. void Cleanup()
  315. {
  316.     if (g_pITaskScheduler)
  317.     {
  318.         g_pITaskScheduler->Release();
  319.         g_pITaskScheduler = NULL;
  320.     }
  321.    
  322.     // Unload the library, now that our pointer is freed.
  323.  
  324.     CoUninitialize();
  325.     return;
  326. }
  327.  
  328.  
  329. //+--------------------------------------------------------------------------
  330. //
  331. // Function:    DisplayHelp() 
  332. //
  333. // Synopsis:    Prints out help and usage information
  334. //
  335. // Arguments:   lpwszProgName - a pointer to a WSTR containing argv[0]
  336. //
  337. // Returns:     nothing (void)
  338. //
  339. //---------------------------------------------------------------------------
  340.  
  341. void DisplayHelp(LPWSTR lpwszProgName)
  342. {
  343.     wprintf(L"%s -- a small program to replicate some\n", lpwszProgName); 
  344.     wprintf(L"\tfunctionality of the AT command, using the\n");
  345.     wprintf(L"\tnew Task Scheduler interfaces.\n\n");
  346.  
  347.     wprintf(L"Usage:\n");
  348.     wprintf(L"\t%s /?\n\t\t\t\t\t\t- Display this help\n",lpwszProgName);
  349.     wprintf(L"\t%s\n\t\t\t\t\t\t- Show all work items\n",lpwszProgName);
  350.     wprintf(L"\t%s TaskName.job /DELETE \n\t\t\t\t\t\t- delete work item\n",lpwszProgName);
  351.     wprintf(L"\t%s TaskName.job Time Command [UserName [Password]]\n\t\t\t\t\t\t- submit a new work item\n\n",lpwszProgName);
  352.  
  353.     wprintf(L"TaskName.job is the name of the work item object on disk.\n");
  354.     wprintf(L"The work item will appear in the Scheduled Tasks folder as TaskName\n");
  355.     wprintf(L"but must be given the extension \".job\" for the service \n");
  356.     wprintf(L"to recognize and run it.\n\n");
  357.  
  358.     wprintf(L"Task Time is in 24 hour format (such as 15:30) and is the\n");
  359.     wprintf(L"next instance of this time within 24 hours.\n\n");
  360.     
  361.     wprintf(L"The Command should contain the name of the executable to run.\n");
  362.     wprintf(L"Note that the pathname may NOT contain spaces.  If the\n");
  363.     wprintf(L"program requires command line parameters, enclose the entire\n");
  364.     wprintf(L"command string in quotation marks.\n\n");
  365.  
  366.     wprintf(L"Username and password are required to run the specified work item under Windows NT.\n");
  367.     wprintf(L"Under Windows NT, if not specified, you will be prompted.\n");
  368.     wprintf(L"Under Windows 95, both fields are ignored.  If not specified\n");
  369.     wprintf(L"on the command line, you will not be prompted.\n\n");
  370.     
  371.     return;
  372. }
  373.  
  374.  
  375. //+--------------------------------------------------------------------------
  376. //
  377. // Function:    EnumerateJobs()
  378. //
  379. // Synopsis:    Goes through all scheduled work items on a system and 
  380. //              lists them, along with a short trigger string.
  381. //
  382. // Arguments:   none (void).  Requires g_pITaskScheduler.
  383. //
  384. // Returns:     HRESULT indicating success (S_OK) or other.
  385. //
  386. //---------------------------------------------------------------------------
  387.  
  388. HRESULT EnumerateJobs()
  389. {
  390.     HRESULT hr = S_OK, hrLoop = S_OK;
  391.     IEnumWorkItems *pIEnumWorkItems;
  392.     IUnknown *pIU;
  393.     ITask *pITask;
  394.     ULONG ulTasksToGet = 1, ulActualTasksRetrieved = 0;
  395.     LPWSTR *rgpwszNames, pwszTrigger;
  396.     WORD wTrigCount = 0;
  397.     WORD wTemp;
  398.  
  399.     
  400.     //
  401.     // Get an enumeration pointer, using ITaskScheduler::Enum
  402.     //
  403.  
  404.     hr = g_pITaskScheduler->Enum(&pIEnumWorkItems);
  405.     if (FAILED(hr))
  406.     {
  407.         wprintf(L"Failed to get enumerator\n");
  408.         return hr;
  409.     }
  410.  
  411.     do
  412.     {
  413.         // Get a single work item, using IEnumWorkItems::Next
  414.  
  415.         hrLoop = pIEnumWorkItems->Next(ulTasksToGet, &rgpwszNames,
  416.                                        &ulActualTasksRetrieved);
  417.         if (hrLoop == S_FALSE)
  418.         {
  419.             // There are no more waiting tasks to look at
  420.             break;
  421.         }
  422.  
  423.         // Attach to the work item, using ITaskScheduler::Activate
  424.  
  425.         hr = g_pITaskScheduler->Activate(rgpwszNames[0], IID_ITask, &pIU);
  426.         if (FAILED(hr))
  427.         {
  428.             wprintf(L"Error: Activate Failed\n",hr);
  429.             break;
  430.         }
  431.  
  432.         // QI pIU for pITask
  433.    
  434.         hr = pIU->QueryInterface(IID_ITask, (void **) &pITask);
  435.         pIU->Release();
  436.         pIU = NULL;
  437.         if (FAILED(hr))
  438.         {
  439.             wprintf(L"Error: QI for ITask failed in Activate.\n");
  440.             break;
  441.         }
  442.  
  443.         // Display task name
  444.  
  445.         wprintf(L"Task: %s\n",rgpwszNames[0]); 
  446.  
  447.         // Use ITask::GetTriggerCount to get count of triggers
  448.  
  449.         hr = pITask->GetTriggerCount(&wTrigCount);
  450.         if (FAILED(hr))
  451.         {
  452.             wprintf(L"Error: Failed to count triggers\n");
  453.             pITask->Release();
  454.             break;
  455.         }
  456.         
  457.         for (wTemp = 0; wTemp < wTrigCount; wTemp++)
  458.         {
  459.             // Dump Triggers using ITask::GetTriggerString
  460.  
  461.             hr = pITask->GetTriggerString(wTemp, &pwszTrigger);
  462.             if (FAILED(hr))
  463.             {
  464.                 wprintf(L"Error: Failed to get trigger string\n");
  465.                 pITask->Release();
  466.                 break;
  467.             }
  468.  
  469.             wprintf(L"\tTrigger: %s\n",pwszTrigger);
  470.  
  471.             // Clean up the memory we were allocated for trig string
  472.  
  473.             CoTaskMemFree(pwszTrigger);
  474.         }
  475.  
  476.         // Clean up each element in the array of job names, then
  477.         // clean up the final array.
  478.  
  479.         CoTaskMemFree(rgpwszNames[0]);
  480.         CoTaskMemFree(rgpwszNames);
  481.         
  482.         // Free the ITask pointer
  483.     
  484.         pITask->Release();
  485.  
  486.     } while(1);
  487.  
  488.     // Release the enumeration pointer
  489.     
  490.     pITask = NULL;
  491.  
  492.     pIEnumWorkItems->Release();
  493.     pIEnumWorkItems = NULL;
  494.  
  495.     return hr; 
  496. }    
  497.     
  498.  
  499. //+--------------------------------------------------------------------------
  500. //
  501. // Function:        DeleteJob()
  502. //
  503. // Synopsis:        Deletes a work item from the Scheduled Tasks folder.
  504. //
  505. // Arguments:       lpwszJobName - the name of the work item to delete
  506. //
  507. // Returns:         HRESULT indicating success (S_OK) or failure.
  508. //
  509. //---------------------------------------------------------------------------
  510.  
  511. HRESULT DeleteJob(LPWSTR lpwszJobName)
  512. {
  513.     HRESULT hr = S_OK;
  514.  
  515.     hr = g_pITaskScheduler->Delete(lpwszJobName);
  516.  
  517.     return hr;
  518. }
  519.  
  520.  
  521. //+--------------------------------------------------------------------------
  522. //
  523. // Function:        AddJob()
  524. //
  525. // Synopsis:        Adds a new work item to the Scheduled Tasks folder.
  526. //
  527. // Arguments:       lpwszJobName - name of the task file
  528. //                  lptTime      - pointer to SYSTEMTIME struct containing
  529. //                                      the time the job should run.
  530. //                  lpwszCommand - name of application (command) to run.
  531. //                  lpwszUserName- user name to run job under
  532. //                  lpwszPassword- password for that user
  533. //
  534. // Returns:         HRESULT indicating success (S_OK) or failure.
  535. //
  536. // Notes:           The password or BOTH the username and password
  537. //                  may be passed in as '\0' strings, in which case
  538. //                  this function will prompt on stdout for password
  539. //                  and username
  540. //
  541. //---------------------------------------------------------------------------
  542.  
  543. HRESULT AddJob(LPWSTR lpwszJobName, LPSYSTEMTIME lptTime,
  544.                 LPWSTR lpwszCommand, LPWSTR lpwszUserName,
  545.                 LPWSTR lpwszPassword)
  546. {
  547.     HRESULT hr = S_OK;
  548.     IUnknown *pIU;
  549.     IPersistFile *pIPF;
  550.     ITask *pITask;
  551.     ITaskTrigger *pITaskTrig;
  552.     DWORD dwTaskFlags, dwTrigFlags;
  553.     WORD wTrigNumber;
  554.     TASK_TRIGGER TaskTrig;
  555.     WCHAR lpwszAppName[255];
  556.     int i;
  557.     
  558.     // Add the task.  Most likely failure is that work item already exists.
  559.     // Uses ITaskScheduler::NewWorkItem
  560.  
  561.     hr = g_pITaskScheduler->NewWorkItem(lpwszJobName, 
  562.                                         CLSID_CTask, 
  563.                                         IID_ITask, 
  564.                                         &pIU);
  565.     if (FAILED(hr))
  566.     {
  567.         wprintf(L"Error:  Create New Task failure\n");
  568.         return hr;
  569.     }
  570.  
  571.     // We now have an IUnknown pointer.  This is queried for an ITask
  572.     // pointer on the work item we just added.
  573.     
  574.     hr = pIU->QueryInterface(IID_ITask, (void **) &pITask);
  575.     if (FAILED(hr))
  576.     {
  577.         wprintf(L"Error: IUnknown failed to yield ITask\n");
  578.         pIU->Release();
  579.         return hr;
  580.     }
  581.     
  582.     // Cleanup pIUnknown, as we are done with it.
  583.     
  584.     pIU->Release();
  585.     pIU = NULL;
  586.  
  587.     //
  588.     // We need to see if we support security, and we
  589.     // do this by calling ITask::SetAccountInformation
  590.     // and checking if the failure code is SCHED_E_NO_SECURITY_SERVICES
  591.     //
  592.  
  593.     hr = pITask->SetAccountInformation(lpwszUserName, lpwszPassword);
  594.     if (hr != SCHED_E_NO_SECURITY_SERVICES)
  595.     {
  596.     
  597.         // Check to see if username is null
  598.  
  599.         if (*lpwszPassword == L'\0')
  600.         {
  601.             // If password was null, chance username is, too.
  602.  
  603.             if (*lpwszUserName == L'\0')
  604.             {
  605.                 wprintf(L"Enter username to run job %s as: ",lpwszJobName);
  606.                 wscanf(L"%s",lpwszUserName);
  607.                 wprintf(L"\n");
  608.             }
  609.     
  610.             wprintf(L"Enter password to user %s: ",lpwszUserName);
  611.             wscanf(L"%s",lpwszPassword);
  612.             wprintf(L"\n");
  613.         }
  614.     }
  615.  
  616.     // Set the account information using ITask::SetAccountInformation
  617.     // This fails for Win9x, but we ignore the failure.
  618.  
  619.     hr = pITask->SetAccountInformation(lpwszUserName, lpwszPassword);
  620.     if ((FAILED(hr)) && (hr != SCHED_E_NO_SECURITY_SERVICES))
  621.     {
  622.         wprintf(L"Error: Failed to set credentials on task object %x\n",hr);
  623.         pITask->Release();
  624.         return hr;
  625.     }
  626.  
  627.     // Extract parameters from the application name
  628.     // Note that there might not be any parameters at all.
  629.     // We butcher a path like C:\Program Files\foo.exe, because
  630.     // we break on space, but this is ok, because user could say
  631.     // C:\progra~1\foo.exe instead.
  632.  
  633.     i = 0;
  634.     while ((*lpwszCommand != L' ') && (*lpwszCommand != '\0'))
  635.     {
  636.         lpwszAppName[i] = *lpwszCommand;
  637.         i++;
  638.         lpwszCommand++;
  639.     }
  640.     lpwszAppName[i] = L'\0';
  641.     if (*lpwszCommand == L' ')
  642.     {
  643.         lpwszCommand++;
  644.     }
  645.     
  646.     // Set command name with ITask::SetApplicationName
  647.  
  648.     hr = pITask->SetApplicationName(lpwszAppName);
  649.     if (FAILED(hr))
  650.     {
  651.         wprintf(L"Error: Failed to set command name (with parms)\n"); 
  652.         pITask->Release();
  653.         return hr;
  654.     }
  655.  
  656.  
  657.     // Set task parameters with ITask::SetParameters
  658.  
  659.     hr = pITask->SetParameters(lpwszCommand);
  660.     if (FAILED(hr))
  661.     {
  662.         wprintf(L"Error: Failed to set parameters\n");
  663.         pITask->Release();
  664.         return hr;
  665.     }
  666.         
  667.     // Set the comment, so we know how this job go there
  668.     // Uses ITask::SetComment
  669.  
  670.     hr = pITask->SetComment(L"This scheduled task created by command line SDK sample tool");
  671.     if (FAILED(hr))
  672.     {
  673.         wprintf(L"Error: Task comment could not be set\n");
  674.         pITask->Release();
  675.         return hr;
  676.     }
  677.  
  678.     // Set the flags on the task object
  679.     // Use ITask::SetFlags
  680.  
  681.     dwTaskFlags = TASK_FLAG_DELETE_WHEN_DONE;
  682.     hr = pITask->SetFlags(dwTaskFlags);
  683.     if (FAILED(hr))
  684.     {
  685.         wprintf(L"Error: Could not set task flags\n");
  686.         pITask->Release();
  687.         return hr;
  688.     }
  689.  
  690.     // Now, create a trigger to run the task at our specified time.
  691.     // Uses ITask::CreateTrigger()
  692.  
  693.     hr = pITask->CreateTrigger(&wTrigNumber, &pITaskTrig);
  694.     if (FAILED(hr))
  695.     {
  696.         wprintf(L"Error: Could not create a new trigger\n");
  697.         pITask->Release();
  698.         return hr;
  699.     }
  700.  
  701.     // Now, fill in the trigger as necessary.
  702.  
  703.     dwTrigFlags = 0;
  704.  
  705.     TaskTrig.cbTriggerSize = sizeof(TASK_TRIGGER);
  706.     TaskTrig.Reserved1 = 0;
  707.     TaskTrig.wBeginYear = lptTime->wYear;
  708.     TaskTrig.wBeginMonth = lptTime->wMonth;
  709.     TaskTrig.wBeginDay = lptTime->wDay;
  710.     TaskTrig.wEndYear = 0;
  711.     TaskTrig.wEndMonth = 0;
  712.     TaskTrig.wEndDay = 0;
  713.     TaskTrig.wStartHour = lptTime->wHour;
  714.     TaskTrig.wStartMinute = lptTime->wMinute;
  715.     TaskTrig.MinutesDuration = 0;
  716.     TaskTrig.MinutesInterval = 0;
  717.     TaskTrig.rgFlags = dwTrigFlags;
  718.     TaskTrig.TriggerType = TASK_TIME_TRIGGER_ONCE;
  719.     TaskTrig.wRandomMinutesInterval = 0;
  720.     TaskTrig.Reserved2 = 0;
  721.  
  722.     // Add this trigger to the task using ITaskTrigger::SetTrigger
  723.  
  724.     hr = pITaskTrig->SetTrigger(&TaskTrig);
  725.     if (FAILED(hr))
  726.     {
  727.         wprintf(L"Error: Failed to set trigger to desired values\n");
  728.         pITaskTrig->Release();
  729.         pITask->Release();
  730.         return hr;
  731.     }
  732.  
  733.     // Make the changes permanent
  734.     // Requires using IPersistFile::Save()
  735.  
  736.     hr = pITask->QueryInterface(IID_IPersistFile, (void **) &pIPF); 
  737.     if (FAILED(hr))
  738.     {
  739.         wprintf(L"Error: Could not get IPersistFile on task\n");
  740.         pITaskTrig->Release();
  741.         pITask->Release();
  742.         return hr;
  743.     }
  744.  
  745.     hr = pIPF->Save(NULL, FALSE);
  746.     if (FAILED(hr))
  747.     {
  748.         wprintf(L"Error: Could not save object\n");
  749.         pITaskTrig->Release();
  750.         pITask->Release();
  751.         pIPF->Release();
  752.         return hr;
  753.     }
  754.  
  755.     // We no longer need ITask
  756.     
  757.     pITask->Release();
  758.     pITask = NULL;
  759.  
  760.     // Done with ITaskTrigger pointer
  761.     
  762.     pITaskTrig->Release();
  763.     pITaskTrig = NULL;
  764.  
  765.     // Done with IPersistFile
  766.     
  767.     pIPF->Release();
  768.     pIPF = NULL;
  769.  
  770.     return hr;
  771.  
  772.  
  773. //+--------------------------------------------------------------------------
  774. //
  775. // Function:        ConvertToSystemTime()
  776. //
  777. // Synopsis:        Given a text string from the command line of the form
  778. //                  XX:YY where XX = 0 - 23, and YY = 0 - 59,
  779. //                  obtain the next instance in 24 hours of said time
  780. //                  and return this time in a SYSTEMTIME structure
  781. //                  so that it can be used to set triggers
  782. //
  783. // Arguments:       lpwszTime    -   The text string of time
  784. //                  lptTime     -   The SYSTEMTIME pointer
  785. //
  786. // Returns:         HRESULT of success (S_OK) or failure
  787. //
  788. //---------------------------------------------------------------------------
  789.  
  790. HRESULT ConvertToSystemTime(LPWSTR lpwszTime, LPSYSTEMTIME lptTime)
  791. {
  792.     WORD wMin = 0, wHr = 0, wMaxFeb = 28;
  793.     WCHAR szTemp[3], *szEnd1, *szEnd2;
  794.     SYSTEMTIME tNow;
  795.     int i, j;
  796.  
  797.     // Extract minutes and hour from string
  798.  
  799.     if ((wcslen(lpwszTime) > 5) || (wcslen(lpwszTime) < 4))
  800.     {
  801.         wprintf(L"Error: Invalid time string given\n");
  802.         return E_FAIL;
  803.     }
  804.  
  805.     // Hours first.
  806.     
  807.     i = 0;
  808.     while (*lpwszTime != ':')
  809.     {
  810.         szTemp[i] = *lpwszTime;
  811.         i++;
  812.         if (i > 2)
  813.         {
  814.             // User gave a bad time string
  815.             wprintf(L"Error: Bad hours in time string\n");
  816.             return E_FAIL;
  817.         }
  818.         lpwszTime++;
  819.     }
  820.     szTemp[i] = L'\0';
  821.     lpwszTime++;
  822.  
  823.     // Convert to value
  824.  
  825.     wHr = (WORD) wcstoul(szTemp, &szEnd1, 10);
  826.     
  827.     // Now do minutes
  828.  
  829.     i = wcslen(lpwszTime);
  830.     j = 0;
  831.     while (i)
  832.     {
  833.         szTemp[j] = *lpwszTime;
  834.         j++;
  835.         i--;
  836.         lpwszTime++;
  837.     }
  838.     szTemp[j] = L'\0';
  839.  
  840.     wMin = (WORD) wcstoul(szTemp, &szEnd2, 10);
  841.  
  842.     // Now figure out if we are running today or tomorrow
  843.     
  844.     GetLocalTime(&tNow);
  845.     if ((wHr < tNow.wHour) || 
  846.        ((wHr == tNow.wHour) && (wMin < tNow.wMinute)))
  847.     {
  848.         // Job is tomorrow - we must figure out what tomorrow is
  849.         
  850.         switch(tNow.wMonth)
  851.         {
  852.             case 4:
  853.             case 6:
  854.             case 9:
  855.             case 11:
  856.                 // Thirty day months
  857.                 
  858.                 if ((tNow.wDay + 1) > 30)
  859.                 {
  860.                     tNow.wDay = 1;
  861.                     tNow.wMonth++;
  862.                 }
  863.                 else
  864.                 {
  865.                     tNow.wDay++;
  866.                 }
  867.                 break;
  868.  
  869.             case 2:
  870.                 // February
  871.                 // Leap Year?
  872.                 
  873.                 if ((tNow.wYear % 4) == 0) 
  874.                 {
  875.                     wMaxFeb = 29;
  876.                 }
  877.                 if (((tNow.wYear % 100) == 0) &&
  878.                     ((tNow.wYear % 400) != 0))
  879.                 {
  880.                     wMaxFeb = 28;
  881.                 }
  882.  
  883.                 if ((tNow.wDay + 1) > wMaxFeb)
  884.                 {
  885.                     tNow.wDay = 1;
  886.                     tNow.wMonth++;
  887.                 }
  888.                 else
  889.                 {
  890.                     tNow.wDay++;
  891.                 }
  892.                 break;
  893.  
  894.             default:
  895.                 // 31 day months.  Handle Dec. later
  896.                 
  897.                 if ((tNow.wDay + 1) > 31)
  898.                 {
  899.                     tNow.wDay = 1;
  900.                     tNow.wMonth++;
  901.                 }
  902.                 else
  903.                 {
  904.                     tNow.wDay++;
  905.                 }
  906.                 
  907.                 if (tNow.wMonth > 12)
  908.                 {
  909.                     tNow.wMonth = 1;
  910.                     tNow.wYear++;
  911.                 }
  912.         }
  913.     }
  914.  
  915.     if ((wMin < 0) || (wMin > 59))
  916.     {
  917.         wprintf(L"Error: Invalid minutes (need 0 - 59)\n");
  918.         return E_FAIL;
  919.     }
  920.     else
  921.     {
  922.         tNow.wMinute = wMin;
  923.     }
  924.     if ((wHr < 0) || (wHr > 23))
  925.     {
  926.         wprintf(L"Error: Invalid hours (need 0 - 23)\n");
  927.         return E_FAIL;
  928.     }
  929.     else
  930.     {
  931.         tNow.wHour = wHr;
  932.     }
  933.  
  934.     lptTime->wHour = tNow.wHour;
  935.     lptTime->wMinute = tNow.wMinute;
  936.     lptTime->wYear = tNow.wYear;
  937.     lptTime->wMonth = tNow.wMonth;
  938.     lptTime->wDay = tNow.wDay;
  939.     lptTime->wDayOfWeek = 0;
  940.     lptTime->wSecond = 0;
  941.     lptTime->wMilliseconds = 0;
  942.     return S_OK;
  943.