News:

Download Pelles C here: http://www.pellesc.se

Main Menu

Task Schedule 2.0 examples

Started by TimoVJL, December 09, 2025, 08:42:00 AM

Previous topic - Next topic

TimoVJL

https://learn.microsoft.com/en-us/windows/win32/taskschd/displaying-task-names-and-state--c---
https://github.com/MicrosoftDocs/win32/blob/docs/desktop-src/TaskSchd/displaying-task-names-and-state--c---.md

TaskEnum2.c
Still things to do.
/********************************************************************
 This sample enumerates through the tasks on the local computer and
 displays their name and state.
********************************************************************/
#define WIN32_LEAN_AND_MEAN
#define _WIN32_DCOM

#include <windows.h>
#include <stdio.h>
//#include <comdef.h>
//  Include the task header file.
#include <taskschd.h>
#pragma comment(lib, "taskschd.lib")
//#pragma comment(lib, "comsupp.lib")
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "oleaut32.lib")

int __cdecl wmain(void)
{
    //  ------------------------------------------------------
    //  Initialize COM.
    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if( FAILED(hr) )
    {
        printf("\nCoInitializeEx failed: %x\n", hr );
        return 1;
    }

    //  Set general COM security levels.
    hr = CoInitializeSecurity(
        NULL,
        -1,
        NULL,
        NULL,
        RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
        RPC_C_IMP_LEVEL_IMPERSONATE,
        NULL,
        0,
        NULL);

    if( FAILED(hr) )
    {
        printf("\nCoInitializeSecurity failed: %x\n", hr );
        CoUninitialize();
        return 1;
    }

    //  ------------------------------------------------------
    //  Create an instance of the Task Service.
    ITaskService *pService = NULL;
    hr = CoCreateInstance( &CLSID_TaskScheduler,
                           NULL,
                           CLSCTX_INPROC_SERVER,
                           &IID_ITaskService,
                           (void**)&pService ); 
    if (FAILED(hr))
    {
          printf("Failed to CoCreate an instance of the TaskService class: %x", hr);
          CoUninitialize();
          return 1;
    }
    VARIANT v1, v2, v3, v4;
    VariantInit(&v1);
    VariantInit(&v2);
    VariantInit(&v3);
    VariantInit(&v4);
    //  Connect to the task service.
    hr = pService->lpVtbl->Connect(pService, v1, v2,
        v3, v4);
    if( FAILED(hr) )
    {
        printf("ITaskService::Connect failed: %x\n", hr );
        pService->lpVtbl->Release(pService);
        CoUninitialize();
        return 1;
    }

    //  ------------------------------------------------------
    //  Get the pointer to the root task folder.
    ITaskFolder *pRootFolder = NULL;
    hr = pService->lpVtbl->GetFolder(pService, L"\\" , &pRootFolder );
   
    pService->lpVtbl->Release(pService);
    if( FAILED(hr) )
    {
        printf("Cannot get Root Folder pointer: %x", hr );
        CoUninitialize();
        return 1;
    }
   
    //  -------------------------------------------------------
    //  Get the registered tasks in the folder.
    IRegisteredTaskCollection* pTaskCollection = NULL;
    hr = pRootFolder->lpVtbl->GetTasks(pRootFolder, 0, &pTaskCollection );

    pRootFolder->lpVtbl->Release(pRootFolder);
    if( FAILED(hr) )
    {
        printf("Cannot get the registered tasks.: %x", hr);
        CoUninitialize();
        return 1;
    }

    LONG numTasks = 0;
    hr = pTaskCollection->lpVtbl->get_Count(pTaskCollection, &numTasks);

    if( numTasks == 0 )
     {
        printf("\nNo Tasks are currently running" );
        pTaskCollection->lpVtbl->Release(pTaskCollection);
        CoUninitialize();
        return 1;
     }

    printf("\nNumber of Tasks : %d", numTasks );

    TASK_STATE taskState;
    VARIANT v5;
    VariantInit(&v5);
    v5.vt = VT_I4;

    for(LONG i=1; i <= numTasks; i++)
    {
        IRegisteredTask* pRegisteredTask = NULL;
        v5.lVal = i;
        hr = pTaskCollection->lpVtbl->get_Item(pTaskCollection,  v5, &pRegisteredTask );
       
        if( SUCCEEDED(hr) )
        {
            BSTR taskName = NULL;
            hr = pRegisteredTask->lpVtbl->get_Name(pRegisteredTask, &taskName);
            if( SUCCEEDED(hr) )
            {
                printf("\nTask Name: %ls", taskName);
                SysFreeString(taskName);

                hr = pRegisteredTask->lpVtbl->get_State(pRegisteredTask, &taskState);
                if (SUCCEEDED (hr) )
                    printf("\n\tState: %d", taskState);
                else
                    printf("\n\tCannot get the registered task state: %x", hr);
            }
            else
            {
                printf("\nCannot get the registered task name: %x", hr);
            }
            pRegisteredTask->lpVtbl->Release(pRegisteredTask);
        }
        else
        {
            printf("\nCannot get the registered task item at index=%d: %x", i+1, hr);
        }
    }

    pTaskCollection->lpVtbl->Release(pTaskCollection);
    CoUninitialize();
    printf("\n");
    return 0;
}
May the source be with you

Vortex

Hi Timo,

Thanks, the application works fine on Windows 11 2024 H2.
Code it... That's all...

John Z

Timo - wow,

This is very interesting work. When I get through some other things I'll grab it.

In the past I've used vbs and ps scripts to enable applications to set schedules.  Your work might lead to using straight C to do the same thing.


John Z

Vortex

Timo's work is nice. Additionaly, MrBcx created some useful applications too :

https://bcxbasiccoders.com/smf/index.php?topic=1443.0
Code it... That's all...

TimoVJL

#4
I just converted a MS code example from C++ to C.

TaskCreate2.c
This needs some fixing.
/********************************************************************
 This sample schedules a task to start on a daily basis.
********************************************************************/
#define WIN32_LEAN_AND_MEAN
#define _WIN32_DCOM

#include <windows.h>
//#include <iostream>
#include <stdio.h>
//#include <comdef.h>
#include <wincred.h>
//  Include the task header file.
#include <taskschd.h>
#pragma comment(lib, "taskschd.lib")
//#pragma comment(lib, "comsupp.lib")
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "oleaut32.lib")
#pragma comment(lib, "credui.lib")

//using namespace std;

int __cdecl wmain(void)
{
    //  ------------------------------------------------------
    //  Initialize COM.
    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if( FAILED(hr) )
    {
        printf("\nCoInitializeEx failed: %x", hr );
        return 1;
    }

    //  Set general COM security levels.
    hr = CoInitializeSecurity(
        NULL,
        -1,
        NULL,
        NULL,
        RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
        RPC_C_IMP_LEVEL_IMPERSONATE,
        NULL,
        0,
        NULL);

    if( FAILED(hr) )
    {
        printf("\nCoInitializeSecurity failed: %x", hr );
        CoUninitialize();
        return 1;
    }

    //  ------------------------------------------------------
    //  Create a name for the task.
    LPCWSTR wszTaskName = L"Daily Trigger Test Task";

    //  Get the windows directory and set the path to notepad.exe.
    LPCWSTR wstrExecutablePath = _wgetenv( L"WINDIR");
    //wstrExecutablePath += L"\\SYSTEM32\\NOTEPAD.EXE";

   

    //  ------------------------------------------------------
    //  Create an instance of the Task Service.
    ITaskService *pService = NULL;
    hr = CoCreateInstance( &CLSID_TaskScheduler,
                           NULL,
                           CLSCTX_INPROC_SERVER,
                           &IID_ITaskService,
                           (void**)&pService ); 
    if (FAILED(hr))
    {
        printf("Failed to create an instance of ITaskService: %x", hr);
        CoUninitialize();
        return 1;
    }
    VARIANT v1, v2, v3, v4;
VariantInit(&v1);
VariantInit(&v2);
VariantInit(&v3);
VariantInit(&v4);
 
    //  Connect to the task service.
    //hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
hr = pService->lpVtbl->Connect(pService, v1, v2, v3, v4);
    if( FAILED(hr) )
    {
        printf("ITaskService::Connect failed: %x", hr );
        pService->lpVtbl->Release(pService);
        CoUninitialize();
        return 1;
    }

    //  ------------------------------------------------------
    //  Get the pointer to the root task folder.  This folder will hold the
    //  new task that is registered.
    ITaskFolder *pRootFolder = NULL;
    hr = pService->lpVtbl->GetFolder(pService, L"\\" , &pRootFolder );
    if( FAILED(hr) )
    {
        printf("Cannot get Root Folder pointer: %x", hr );
        pService->lpVtbl->Release(pService);
        CoUninitialize();
        return 1;
    }
   
    // If the same task exists, remove it.
    pRootFolder->lpVtbl->DeleteTask(pRootFolder, (wchar_t*)wszTaskName, 0  );
   
    //  Create the task builder object to create the task.
    ITaskDefinition *pTask = NULL;
    hr = pService->lpVtbl->NewTask(pService, 0, &pTask );
   
    pService->lpVtbl->Release(pService);  // COM clean up.  Pointer is no longer used.
    if (FAILED(hr))
    {
        printf("Failed to CoCreate an instance of the TaskService class: %x", hr);
        pRootFolder->lpVtbl->Release(pRootFolder);
        CoUninitialize();
        return 1;
    }
           
    //  ------------------------------------------------------
    //  Get the registration info for setting the identification.
    IRegistrationInfo *pRegInfo= NULL;
    hr = pTask->lpVtbl->get_RegistrationInfo(pTask, &pRegInfo );
    if( FAILED(hr) )
    {
        printf("\nCannot get identification pointer: %x", hr );
        pRootFolder->lpVtbl->Release(pRootFolder);
        pTask->lpVtbl->Release(pTask);
        CoUninitialize();       
        return 1;
    }
   
    hr = pRegInfo->lpVtbl->put_Author(pRegInfo, L"Author Name" );
    pRegInfo->lpVtbl->Release(pRegInfo);  // COM clean up.  Pointer is no longer used.
    if( FAILED(hr) )
    {
        printf("\nCannot put identification info: %x", hr );
        pRootFolder->lpVtbl->Release(pRootFolder);
        pTask->lpVtbl->Release(pTask);
        CoUninitialize();
        return 1;
    }
   
    //  ------------------------------------------------------
    //  Get the trigger collection to insert the daily trigger.
    ITriggerCollection *pTriggerCollection = NULL;
    hr = pTask->lpVtbl->get_Triggers(pTask, &pTriggerCollection );
    if( FAILED(hr) )
    {
        printf("\nCannot get trigger collection: %x", hr );
        pRootFolder->lpVtbl->Release(pRootFolder);
        pTask->lpVtbl->Release(pTask);
        CoUninitialize();
        return 1;
    }
       
    //  Add the daily trigger to the task.
    ITrigger *pTrigger = NULL;   
    hr = pTriggerCollection->lpVtbl->Create(pTriggerCollection, TASK_TRIGGER_DAILY, &pTrigger );
    pTriggerCollection->lpVtbl->Release(pTriggerCollection);
    if( FAILED(hr) )
    {
        printf("\nCannot create the trigger: %x", hr );
        pRootFolder->lpVtbl->Release(pRootFolder);
        pTask->lpVtbl->Release(pTask);
        CoUninitialize();
        return 1;
    }     
   
    IDailyTrigger *pDailyTrigger = NULL;
    hr = pTrigger->lpVtbl->QueryInterface(pTrigger,
        &IID_IDailyTrigger, (void**) &pDailyTrigger );
    pTrigger->lpVtbl->Release(pTrigger);
    if( FAILED(hr) )
    {
        printf("\nQueryInterface call on IDailyTrigger failed: %x", hr );
        pRootFolder->lpVtbl->Release(pRootFolder);
        pTask->lpVtbl->Release(pTask);
        CoUninitialize();
        return 1;
    }
   
    hr = pDailyTrigger->lpVtbl->put_Id(pDailyTrigger, L"Trigger1" );
    if( FAILED(hr) )
        printf("\nCannot put trigger ID: %x", hr);

    //  Set the task to start daily at a certain time. The time
    //  format should be YYYY-MM-DDTHH:MM:SS(+-)(timezone).
    //  For example, the start boundary below
    //  is January 1st 2005 at 12:05
    hr = pDailyTrigger->lpVtbl->put_StartBoundary(pDailyTrigger, L"2005-01-01T12:05:00" );
    if( FAILED(hr) )
        printf("\nCannot put start boundary: %x", hr);
   
    //  Set the time when the trigger is deactivated.
    hr = pDailyTrigger->lpVtbl->put_EndBoundary(pDailyTrigger, L"2007-05-02T12:05:00" );
    if( FAILED(hr) )
        printf("\nCannot put the end boundary: %x", hr);
 
    //  Define the interval for the daily trigger. An interval of 2 produces an
    //  every other day schedule
    hr = pDailyTrigger->lpVtbl->put_DaysInterval(pDailyTrigger, (short)2 );
    if( FAILED(hr) )
    {
        printf("\nCannot put days interval: %x", hr );
        pRootFolder->lpVtbl->Release(pRootFolder);
        pDailyTrigger->lpVtbl->Release(pDailyTrigger);
        pTask->lpVtbl->Release(pTask);
        CoUninitialize();
        return 1;
    }

    // Add a repetition to the trigger so that it repeats
    // five times.
    IRepetitionPattern *pRepetitionPattern = NULL;
    hr = pDailyTrigger->lpVtbl->get_Repetition(pDailyTrigger, &pRepetitionPattern );
    pDailyTrigger->lpVtbl->Release(pDailyTrigger);
    if( FAILED(hr) )
    {
        printf("\nCannot get repetition pattern: %x", hr );
        pRootFolder->lpVtbl->Release(pRootFolder);
        pTask->lpVtbl->Release(pTask);
        CoUninitialize();
        return 1;
    }
   
    hr = pRepetitionPattern->lpVtbl->put_Duration(pRepetitionPattern, L"PT4M");
    if( FAILED(hr) )
    {
        printf("\nCannot put repetition duration: %x", hr );
        pRootFolder->lpVtbl->Release(pRootFolder);
        pRepetitionPattern->lpVtbl->Release(pRepetitionPattern);
        pTask->lpVtbl->Release(pTask);
        CoUninitialize();
        return 1;
    }

    hr = pRepetitionPattern->lpVtbl->put_Interval(pRepetitionPattern, L"PT1M");
    pRepetitionPattern->lpVtbl->Release(pRepetitionPattern);
    if( FAILED(hr) )
    {
        printf("\nCannot put repetition interval: %x", hr );
        pRootFolder->lpVtbl->Release(pRootFolder);
        pTask->lpVtbl->Release(pTask);
        CoUninitialize();
        return 1;
    }   
 

    //  ------------------------------------------------------
    //  Add an action to the task. This task will execute notepad.exe.     
    IActionCollection *pActionCollection = NULL;

    //  Get the task action collection pointer.
    hr = pTask->lpVtbl->get_Actions(pTask, &pActionCollection );
    if( FAILED(hr) )
    {
        printf("\nCannot get task collection pointer: %x", hr );
        pRootFolder->lpVtbl->Release(pRootFolder);
        pTask->lpVtbl->Release(pTask);
        CoUninitialize();
        return 1;
    }
       
    //  Create the action, specifying that it is an executable action.
    IAction *pAction = NULL;
    hr = pActionCollection->lpVtbl->Create(pActionCollection, TASK_ACTION_EXEC, &pAction );
    pActionCollection->lpVtbl->Release(pActionCollection);
    if( FAILED(hr) )
    {
        printf("\nCannot create action: %x", hr );
        pRootFolder->lpVtbl->Release(pRootFolder);
        pTask->lpVtbl->Release(pTask);
        CoUninitialize();
        return 1;
    }

    IExecAction *pExecAction = NULL;
    hr = pAction->lpVtbl->QueryInterface(pAction,
        &IID_IExecAction, (void**) &pExecAction );
    pAction->lpVtbl->Release(pAction);
    if( FAILED(hr) )
    {
        printf("\nQueryInterface call failed for IExecAction: %x", hr );
        pRootFolder->lpVtbl->Release(pRootFolder);
        pTask->lpVtbl->Release(pTask);
        CoUninitialize();
        return 1;
    }

    //  Set the path of the executable to notepad.exe.
    hr = pExecAction->lpVtbl->put_Path(pExecAction, (wchar_t *)wstrExecutablePath );
    pExecAction->lpVtbl->Release(pExecAction);
    if( FAILED(hr) )
    {
        printf("\nCannot put the executable path: %x", hr );
        pRootFolder->lpVtbl->Release(pRootFolder);
        pTask->lpVtbl->Release(pTask);
        CoUninitialize();
        return 1;
    }

    //  ------------------------------------------------------
    //  Securely get the user name and password. The task will
    //  be created to run with the credentials from the supplied
    //  user name and password.
    CREDUI_INFOW cui;
    WCHAR pszName[CREDUI_MAX_USERNAME_LENGTH] = L"";
    WCHAR pszPwd[CREDUI_MAX_PASSWORD_LENGTH] = L"";
    BOOL fSave;
    DWORD dwErr;

    cui.cbSize = sizeof(CREDUI_INFO);
    cui.hwndParent = NULL;
    //  Ensure that MessageText and CaptionText identify
    //  what credentials to use and which application requires them.
    cui.pszMessageText = L"Account information for task registration:";
    cui.pszCaptionText = L"Enter Account Information for Task Registration";
    cui.hbmBanner = NULL;
    fSave = FALSE;

    //  Create the UI asking for the credentials.
    dwErr = CredUIPromptForCredentialsW(
        &cui,                             //  CREDUI_INFO structure
        L"",                         //  Target for credentials
        NULL,                             //  Reserved
        0,                                //  Reason
        pszName,                          //  User name
        CREDUI_MAX_USERNAME_LENGTH,       //  Max number for user name
        pszPwd,                           //  Password
        CREDUI_MAX_PASSWORD_LENGTH,       //  Max number for password
        &fSave,                           //  State of save check box
        CREDUI_FLAGS_GENERIC_CREDENTIALS |  //  Flags
        CREDUI_FLAGS_ALWAYS_SHOW_UI |
        CREDUI_FLAGS_DO_NOT_PERSIST); 

    if(dwErr)
    {
        printf("Did not get credentials.\n");
        CoUninitialize();
        return 1;     
    }
   
    //  ------------------------------------------------------
    //  Save the task in the root folder.
VariantInit(&v1);
VariantInit(&v2);
VariantInit(&v3);
v1.vt = VT_BSTR;
v1.bstrVal = SysAllocString(pszName);
v2.vt = VT_BSTR;
v2.bstrVal = SysAllocString(pszPwd);

    IRegisteredTask *pRegisteredTask = NULL;
    hr = pRootFolder->lpVtbl->RegisterTaskDefinition(pRootFolder,
            (wchar_t *)wszTaskName,
            pTask,
            TASK_CREATE_OR_UPDATE,
            v1, //_variant_t(_bstr_t(pszName)),
            v2, //_variant_t(_bstr_t(pszPwd)),
            TASK_LOGON_PASSWORD,
            v3, //_variant_t(L""),
            &pRegisteredTask);
    if( FAILED(hr) )
    {
        printf("\nError saving the Task : %x", hr );
        pRootFolder->lpVtbl->Release(pRootFolder);
        pTask->lpVtbl->Release(pTask);
        CoUninitialize();
        SecureZeroMemory(pszName, sizeof(pszName));
        SecureZeroMemory(pszPwd, sizeof(pszPwd));
        return 1;
    }

    printf("\n Success! Task successfully registered. " );

    //  Clean up
    pRootFolder->lpVtbl->Release(pRootFolder);
    pTask->lpVtbl->Release(pTask);
    pRegisteredTask->lpVtbl->Release(pRegisteredTask);
    CoUninitialize();
    SecureZeroMemory(pszName, sizeof(pszName));
    SecureZeroMemory(pszPwd, sizeof(pszPwd));
    return 0;
}
May the source be with you

MrBcx

#5
Quote from: Vortex on December 09, 2025, 11:26:23 AMAdditionaly, MrBcx created some useful applications too :

https://bcxbasiccoders.com/smf/index.php?topic=1443.0

To get at my code, one mostly needs to be a registered user and logged in.

Here is a list of my most recent uploads:

* Communicate with an AI Server using COM
* COM Demos For Using Windows Task Scheduler Services
* Send a JSON record via WinHttp
* Fetch a JSON record via WinHttp
* REST API toolkit in BCX
 
Only the BCX BASIC source code is provided for each. 
If you want the Pelles C compatible code, you'll need to use the BCX Translator.
Bcx Basic to C/C++ Translator
https://www.bcxbasiccoders.com

Vortex

The cron jobs of UNIX\Linux are much more simple to maintain.
Code it... That's all...

John Z

Agree for Unix, I used cron for many things.  However the windows Task Scheduler interactive GUI is very easy to use and access.  Programmatically it is a bit harder.

Here is a powershell script creator in C to schedule a task...
typedef struct names{
  char AppPath[MAX_PATH];
  char VBSTime[50];
  char VBETime[50];
  char TaskName[50];
  char UserSTime[50];
  char Message[50];
}names;

names name;

/****************************************************************************
 *                                                                          *
 * Function: Create_Script_PS1                                              *
 *                                                                          *
 * Purpose : create runnable Powershell script for task                     *
 *                                                                          *
 * History : Date      Reason                                               *
 *           01/31/25  Created   John Z                                     *
 *                                                                          *
 * Globals :SYSTEMTIME dpst, SYSTEMTIME dpet;                               *
 ****************************************************************************/
void Create_Script_PS1(void)
{   int RetVal; 
char FileName [MAX_PATH*2]={0};
FILE *p_file;
    char buf2[4000]={0};
    char *p_buf2;

p_buf2 = buf2;  //wide char


    strcpy(FileName,name.AppPath);
strcat(FileName,"scr1.ps1");

p_file = fopen(FileName,"wb+"); //For Binary Access Write
    if (p_file == NULL)
      {
snprintf(buf2,MAX_PATH*2,"Unable to open file.\r\nFilename: %s",FileName);
MessageBoxA( NULL, buf2, "File Access Error",
MB_OK | MB_ICONERROR | MB_TOPMOST);
return;
  }

    snprintf(p_buf2, 3999,"%s\x0D\x0A%s\x0D\x0A%s\x0D\x0A%s\x0D\x0A",
             "$TriggerTypeTime = 1",
             "$ActionTypeExec = 0",
             "$service = New-Object -ComObject Schedule.Service",
             "$service.Connect()"
             );
    RetVal = fwrite(p_buf2, sizeof(char), strlen(p_buf2), p_file);


    snprintf(p_buf2, 3999,"%s\x0D\x0A%s\x0D\x0A%s\x0D\x0A",
             "$rootFolder = $service.GetFolder(\"\\\")",
             "$taskDefinition = $service.NewTask(0)",
             "$taskDefinition.RegistrationInfo.Description = \"Start Alert at a certain time\""
             );
    RetVal = fwrite(p_buf2, sizeof(char), strlen(p_buf2), p_file);
          fflush(p_file);

    snprintf(p_buf2, 3999,"%s\x0D\x0A%s\x0D\x0A%s\x0D\x0A%s\x0D\x0A",
             "$taskDefinition.RegistrationInfo.Author = \"CalendarZ\"",
             "$taskDefinition.Principal.LogonType = 3",
             "$taskDefinition.Settings.Enabled = $true",
             "$taskDefinition.Settings.StartWhenAvailable = $true"
             );
    RetVal = fwrite(p_buf2, sizeof(char), strlen(p_buf2), p_file);


    snprintf(p_buf2, 3999,"%s\x0D\x0A%s\x0D\x0A%s\x0D\x0A%s\x0D\x0A",
             "$taskDefinition.Settings.Hidden = $false",
             "$taskDefinition.Settings.DeleteExpiredTaskAfter = \"PT10M\"",
             "$triggers = $taskDefinition.Triggers",
             "$trigger = $triggers.Create($TriggerTypeTime)"
             );
    RetVal = fwrite(p_buf2, sizeof(char), strlen(p_buf2), p_file);
          fflush(p_file);

   snprintf(p_buf2, 3999,"%s'%s'\x0D\x0A%s'%s'\x0D\x0A%s\x0D\x0A%s\x0D\x0A",
             "$startTime = ",name.VBSTime,
             "$endTime = ",name.VBETime,
             "$trigger.StartBoundary = $startTime",
             "$trigger.EndBoundary = $endTime"
             );
    RetVal = fwrite(p_buf2, sizeof(char), strlen(p_buf2), p_file);
          fflush(p_file);
   snprintf(p_buf2, 3999,"%s\x0D\x0A%s\x0D\x0A%s\"%s%s\"\x0D\x0A",
             "$actions = $taskDefinition.Actions",
             "$action = $actions.Create($ActionTypeExec)",
             "$action.Path = ",
              name.AppPath,
         "Alert.exe"
             );
    RetVal = fwrite(p_buf2, sizeof(char), strlen(p_buf2), p_file);

   snprintf(p_buf2, 3999,"%s%s -%s\"\x0D\x0A",
             "$Action.Arguments = \"-",
          name.UserSTime,
          name.Message
             );
    RetVal = fwrite(p_buf2, sizeof(char), strlen(p_buf2), p_file);

    snprintf(p_buf2, 3999,"%s\x0D\x0A%s,\x0D\x0A%s\x0D\x0A%s\x0D\x0A%s\x0D\x0A%s\x0D\x0A%s\x0D\x0A%s\x0D\x0A)",
             "$rootFolder.RegisterTaskDefinition(",
         name.TaskName,
             "    $taskDefinition,",
             "    6, # Task creation flag (6 = CREATE_OR_UPDATE)",
             "    $null, # User",
         "    $null, # Password",
             "    $null, # Logon type",
             "    $null # SDDL"
             );
    RetVal = fwrite(p_buf2, sizeof(char), strlen(p_buf2), p_file);
          fflush(p_file);

    fclose(p_file);
//MessageBox(NULL,"Completed!","Create Alert",MB_OK);


}/* end create_script_ps1 */

Not too bad, I have one for VBS too if interested...
I would have preferred in C but couldn't quite manage, so I'll learn from Timo's effort.

John Z

TimoVJL

#8
My idea was to learn convert MS C++ code to Pelles C code.

Nice to have also MrBcx in this topic too.

I didn't use ComCpp2C2 for conversion.

One example:
https://learn.microsoft.com/en-us/windows/win32/api/taskschd/nf-taskschd-itaskservice-connect
    //  Connect to the task service.
    //hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
    hr = pService->lpVtbl->Connect(pService, v1, v2, v3, v4);
those variants are optional, but C compiler don't allow NULL for them.
Just an one empty VARIANT could be used too ?

If you don't have msvcrt.libs, just remove USE_MSVCRT define from projects
May the source be with you

MrBcx

I have attached my four Bcx Basic Task Scheduler source codes, so that people
can examine the codes without needing to sign up on the Bcx forum. 

The BCX Translator is needed, if you want to examine the resulting  C/C++ codes.

I successfully tested each using Pelles C, MSVC, Mingw64, and Clang.

Bcx Basic to C/C++ Translator
https://www.bcxbasiccoders.com

Vortex

Hi Timo,

Thanks for your ComCpp2C2 tool. Kindly, could you please provide an example explaining how to use the tool?
Code it... That's all...

TimoVJL

#11
https://learn.microsoft.com/en-us/windows/win32/taskschd/displaying-task-names-and-state--c---

usage example:
ComCpp2C2.exe EnumTasks2.cpp > EnumTasks2.cit just convert from cpp
pTaskCollection->Release();to C
pTaskCollection->lpVtbl->Release(pTaskCollection);
/********************************************************************
 This sample enumerates through all running tasks on the local computer and
 displays their name and state.
********************************************************************/
#define WIN32_LEAN_AND_MEAN
#define _WIN32_DCOM

#include <windows.h>
#include <stdio.h>
//#include <comdef.h>
//  Include the task header file.
#include <taskschd.h>
#pragma comment(lib, "taskschd.lib")
//#pragma comment(lib, "comsupp.lib")
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "oleaut32.lib")

int __cdecl wmain()
{
    //  ------------------------------------------------------
    //  Initialize COM.
    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if( FAILED(hr) )
    {
        printf("\nCoInitializeEx failed: %x", hr );
        return 1;
    }

    //  Set general COM security levels.
    hr = CoInitializeSecurity(
        NULL,
        -1,
        NULL,
        NULL,
        RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
        RPC_C_IMP_LEVEL_IMPERSONATE,
        NULL,
        0,
        NULL);

    if( FAILED(hr) )
    {
        printf("\nCoInitializeSecurity failed: %x", hr );
        CoUninitialize();
        return 1;
    }

    //  ------------------------------------------------------
    //  Create an instance of the Task Service.
    ITaskService *pService = NULL;
    hr = CoCreateInstance( &CLSID_TaskScheduler,
                           NULL,
                           CLSCTX_INPROC_SERVER,
                           &IID_ITaskService,
                           (void**)&pService ); 
    if (FAILED(hr))
    {
          printf("Failed to CoCreate an instance of the TaskService class: %x", hr);
          CoUninitialize();
          return 1;
    }
       
    //  Connect to the task service.
    VARIANT v1;
    VariantInit(&v1);
    hr = pService->lpVtbl->Connect(pService, v1, v1, v1, v1);
    if( FAILED(hr) )
    {
        printf("ITaskService::Connect failed: %x", hr );
        pService->lpVtbl->Release(pService);
        CoUninitialize();
        return 1;
    }

       // Get the running tasks.
       IRunningTaskCollection* pRunningTasks = NULL;
       hr = pService->lpVtbl->GetRunningTasks(pService,TASK_ENUM_HIDDEN, &pRunningTasks);

    pService->lpVtbl->Release(pService);
    if( FAILED(hr) )
    {
        printf("Cannot get Root Folder pointer: %x", hr );
        CoUninitialize();
        return 1;
    }
       
    LONG numTasks = 0;
    hr = pRunningTasks->lpVtbl->get_Count(pRunningTasks,&numTasks);

    if( numTasks == 0 )
     {
        printf("\nNo Tasks are currently running" );
        pRunningTasks->lpVtbl->Release(pRunningTasks);
        CoUninitialize();
        return 1;
     }

    printf("\nNumber of running tasks : %d", numTasks );

    TASK_STATE taskState;
    v1.vt = VT_I4;
    for(LONG i=1; i <= numTasks; i++)
    {
        v1.lVal = i;
        IRunningTask* pRunningTask = NULL;
        hr = pRunningTasks->lpVtbl->get_Item(pRunningTasks, v1, &pRunningTask );
       
        if( SUCCEEDED(hr) )
        {
            BSTR taskName = NULL;
            hr = pRunningTask->lpVtbl->get_Name(pRunningTask,&taskName);
            if( SUCCEEDED(hr) )
            {
                printf("\nTask Name: %ls", taskName);
                SysFreeString(taskName);

                hr = pRunningTask->lpVtbl->get_State(pRunningTask,&taskState);
                if (SUCCEEDED (hr) )
                    printf("\n\tState: %d", taskState);
                else
                    printf("\n\tCannot get the registered task state: %x", hr);
            }
            else
            {
                printf("\nCannot get the registered task name: %x", hr);
            }
            pRunningTask->lpVtbl->Release(pRunningTask);
        }
        else
        {
            printf("\nCannot get the registered task item at index=%d: %x", i+1, hr);
        }
    }

    pRunningTasks->lpVtbl->Release(pRunningTasks);
    CoUninitialize();
    return 0;
}
May the source be with you

TimoVJL

Quote from: MrBcx on December 10, 2025, 04:21:11 PMI have attached my four Bcx Basic Task Scheduler source codes, so that people
can examine the codes without needing to sign up on the Bcx forum. 

The BCX Translator is needed, if you want to examine the resulting  C/C++ codes.

I successfully tested each using Pelles C, MSVC, Mingw64, and Clang.


BCX seems to be powerful  :)
May the source be with you

Vortex

Hi Timo,

Exactly, BCX is very powerful. The BCX translator has a very rich library of functions.
Code it... That's all...

John Z

#14
Here is plain C working code to schedule a task.  It is implemented in my upcoming version of the calendar which already has a vbs task scheduler and a powershell task scheduler, now has a C task scheduler...covering my basis  ;)

I like to mention that it uses TimoVJL's Variant setup (which I did not know how to do) THANKS Timo!

Example inputs are also provided within the code at various places, //EX:

/*
    COM Initialization: The code initializes the COM library and sets up security.
    Task Scheduler Connection: It connects to the Task Scheduler service.
    Task Definition Creation: It creates a new task definition and sets its properties, including registration info, principal logon type, and settings.
    Trigger Creation: It creates a time-based trigger with specified start and end boundaries.
    Action Creation: It creates an action that executes a specified executable with arguments.
    Task Registration: Finally, it registers the task in the Task Scheduler.
    Cleanup: Releases all allocated resources and uninitializes COM.
*/
/****************************************************************************
 *                                                                          *
 * File    : C_TaskScheduler.C                                              *
 *                                                                          *
 * Purpose : Schedule one shot event using windows task scheduler using C   *
 *                                                                          *
 * History : Date      Reason                                               *
 *           06/25/26  Created John Z                                       *
 *                                                                          *
 * Note    : Uses TimoVJL's Variant setup (thanks!)                         *
 *         : Schedule is a one shot, no repeat, so add if wanted see Timo's *
 *           example in thread above for that                               *
 *         : Example input settings are shown throughout the code           *
 ****************************************************************************/
#include <windows.h>
#include <taskschd.h>
#include <combaseapi.h>  //pelles
#include <stdio.h>
#include <wtypes.h>
#include <wchar.h>
#include <oaidl.h>
#include <oleauto.h>
#include <oleauto.h>

#include "main.h"  // includes setting up names structure example below

#pragma comment(lib, "taskschd.lib")
#pragma comment(lib,"Ole32.lib")
#pragma comment(lib,"OleAut32.lib")

extern names name;
/*
typedef struct names{
  wchar_t AppPathW[MAX_PATH*2]; // ex: L"C:\\Program Files\\PellesC\\Files\\calendar\\Calendar.exe"
  wchar_t VBSTimeW[100];   // ex: L"2026-06-27T21:10:20"
  wchar_t VBETimeW[100];   // ex: L"2026-06-27T21:21:20"
  wchar_t TaskNameW[100];  // ex: L"My schedule"
  wchar_t UserArgsW[200];  // ex: L"-2026 06 28 23:20:20 -Take a BREAK!"
  wchar_t MessageW[100];   // not used directly
}names;
*/

/****************************************************************************
 *                                                                          *
 * Function: Schedule_Task                                                  *
 *                                                                          *
 * Purpose : create and schedule a windows task                             *
 *                                                                          *
 * History : Date      Reason                                               *
 *           06/25/26  Created   John Z                                     *
 *                                                                          *
 * Schedule and alert for the zcalendar program                             *
 * Does the same job as the VBS script, and the Powershell script           *
 ****************************************************************************/
int Schedule_Task(void) {
    int RetVal=0;
    HRESULT hr;
    char msg[250]={0};  // generic for error messages


    // Initialize COM
    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if (FAILED(hr)) {
        snprintf(msg,200,"Failed to initialize COM library. Error: %x\n", hr);
        MessageBox(NULL,msg,"CoInitializeEx",MB_OK|MB_ICONERROR);
        return 1;
    }

    // Initialize COM security
    hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT,
                              RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
    if (FAILED(hr)) {
        snprintf(msg,200,"Failed to initialize security. Error: %x\n", hr);
        MessageBox(NULL,msg,"CoInitializeSecurity",MB_OK|MB_ICONERROR);
        CoUninitialize();
        return 1;
    }

    // Create a pointer to the ITaskService interface
    ITaskService *pService = NULL;
    hr = CoCreateInstance(&CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, &IID_ITaskService, (LPVOID *)&pService);
    if (FAILED(hr)) {
        snprintf(msg,200,"Failed to create an instance of the Task Scheduler. Error: %x\n", hr);
        MessageBox(NULL,msg,"CoCreateInstance",MB_OK|MB_ICONERROR);
        CoUninitialize();
        return 1;
    }

// from TimoVJL
    VARIANT v1, v2, v3, v4;
    VariantInit(&v1);
    VariantInit(&v2);
    VariantInit(&v3);
    VariantInit(&v4);


    // Connect to the Task Scheduler
    //hr = pService->lpVtbl->Connect(pService, VT_NULL, NULL, NULL, NULL);
    hr = pService->lpVtbl->Connect(pService, v1, v2, v3, v4); // TimoVJL
    if (FAILED(hr)) {
        snprintf(msg,200,"Failed to connect to the Task Scheduler. Error: %x\n", hr);
        pService->lpVtbl->Release(pService);
        MessageBox(NULL,msg,"Connect",MB_OK|MB_ICONERROR);
        CoUninitialize();
        return 1;
    }

    // Get the root folder
    ITaskFolder *pRootFolder = NULL;
    hr = pService->lpVtbl->GetFolder(pService, L"\\", &pRootFolder);
    if (FAILED(hr)) {
        snprintf(msg,200,"Failed to get the root folder. Error: %x\n", hr);
        pService->lpVtbl->Release(pService);
        MessageBox(NULL,msg,"GetFolder (Root)",MB_OK|MB_ICONERROR);
        CoUninitialize();
        return 1;
    }

    // Create a new task definition
    ITaskDefinition *pTaskDefinition = NULL;
    hr = pService->lpVtbl->NewTask(pService, 0, &pTaskDefinition);
    if (FAILED(hr)) {
        snprintf(msg,200,"Failed to create a new task definition. Error: %x\n", hr);
        pRootFolder->lpVtbl->Release(pRootFolder);
        pService->lpVtbl->Release(pService);
        MessageBox(NULL,msg,"NewTask Def.",MB_OK|MB_ICONERROR);
        CoUninitialize();
        return 1;
    }

    // Set task registration info
    IRegistrationInfo *pRegistrationInfo = NULL;
    pTaskDefinition->lpVtbl->get_RegistrationInfo(pTaskDefinition, &pRegistrationInfo);
    pRegistrationInfo->lpVtbl->put_Description(pRegistrationInfo, L"Start Alert at a certain time");
    pRegistrationInfo->lpVtbl->put_Author(pRegistrationInfo, L"CalendarZ");
    pRegistrationInfo->lpVtbl->Release(pRegistrationInfo);

    // Set principal logon type
    IPrincipal *pPrincipal = NULL;
    pTaskDefinition->lpVtbl->get_Principal(pTaskDefinition, &pPrincipal);
    pPrincipal->lpVtbl->put_LogonType(pPrincipal, 3);
    pPrincipal->lpVtbl->Release(pPrincipal);

    // Set task settings
    ITaskSettings *pSettings = NULL;
    pTaskDefinition->lpVtbl->get_Settings(pTaskDefinition, &pSettings);
    pSettings->lpVtbl->put_Enabled(pSettings, VARIANT_TRUE);
    pSettings->lpVtbl->put_StartWhenAvailable(pSettings, VARIANT_TRUE);
    pSettings->lpVtbl->put_Hidden(pSettings, VARIANT_FALSE);
    pSettings->lpVtbl->put_DeleteExpiredTaskAfter(pSettings, L"PT10M");// fixed setting for now
    pSettings->lpVtbl->Release(pSettings);

    // Create a trigger
    ITriggerCollection *pTriggerCollection = NULL;
    pTaskDefinition->lpVtbl->get_Triggers(pTaskDefinition, &pTriggerCollection);
    ITrigger *pTrigger = NULL;
    hr = pTriggerCollection->lpVtbl->Create(pTriggerCollection, TASK_TRIGGER_TIME, &pTrigger);
    if (FAILED(hr)) {
        snprintf(msg,200,"Failed to create a trigger. Error: %x\n", hr);
        pTriggerCollection->lpVtbl->Release(pTriggerCollection);
        pTaskDefinition->lpVtbl->Release(pTaskDefinition);
        pRootFolder->lpVtbl->Release(pRootFolder);
        pService->lpVtbl->Release(pService);
        MessageBox(NULL,msg,"get_Triggers",MB_OK|MB_ICONERROR);
        CoUninitialize();
        return 1;
    }

    // Set trigger start and end boundaries
    ITimeTrigger *pTimeTrigger = NULL;
    pTrigger->lpVtbl->QueryInterface(pTrigger, &IID_ITimeTrigger, (void**)&pTimeTrigger);

//EX:    pTimeTrigger->lpVtbl->put_StartBoundary(pTimeTrigger, L"2026-06-27T21:10:20");
    pTimeTrigger->lpVtbl->put_StartBoundary(pTimeTrigger, name.VBSTimeW);

//EX:    pTimeTrigger->lpVtbl->put_EndBoundary(pTimeTrigger, L"2026-06-27T21:21:20");
    pTimeTrigger->lpVtbl->put_EndBoundary(pTimeTrigger, name.VBETimeW);


    pTimeTrigger->lpVtbl->Release(pTimeTrigger);
    pTrigger->lpVtbl->Release(pTrigger);
    pTriggerCollection->lpVtbl->Release(pTriggerCollection);

    // Create an action
    IActionCollection *pActionCollection = NULL;
    pTaskDefinition->lpVtbl->get_Actions(pTaskDefinition, &pActionCollection);
    IAction *pAction = NULL;
    hr = pActionCollection->lpVtbl->Create(pActionCollection, TASK_ACTION_EXEC, &pAction);
    if (FAILED(hr)) {
        snprintf(msg,200,"Failed to create an action. Error: %x\n", hr);
        pActionCollection->lpVtbl->Release(pActionCollection);
        pTaskDefinition->lpVtbl->Release(pTaskDefinition);
        pRootFolder->lpVtbl->Release(pRootFolder);
        pService->lpVtbl->Release(pService);
        MessageBox(NULL,msg,"get_Actions",MB_OK|MB_ICONERROR);
        CoUninitialize();
        return 1;
    }

    // Set action properties (program to run for example)
    IExecAction *pExecAction = NULL;
    pAction->lpVtbl->QueryInterface(pAction, &IID_IExecAction, (void**)&pExecAction);

//EX:    pExecAction->lpVtbl->put_Path(pExecAction, L"C:\\Program Files\\PellesC\\Files\\calendar\\Calendar.exe");
    pExecAction->lpVtbl->put_Path(pExecAction, name.AppPathW);


//EX:    pExecAction->lpVtbl->put_Arguments(pExecAction, L"-2026 06 28 23:20:20 -Take Pills");
    pExecAction->lpVtbl->put_Arguments(pExecAction, name.UserArgsW);

    pExecAction->lpVtbl->Release(pExecAction);
    pAction->lpVtbl->Release(pAction);
    pActionCollection->lpVtbl->Release(pActionCollection);

//  TimoVJL method
    VariantInit(&v1);
    VariantInit(&v2);
    VariantInit(&v3);
    VariantInit(&v4);

    v1.vt = VT_BSTR;  // use the logon user credentials
    v1.bstrVal = L"";
    v2.vt = VT_BSTR;
    v2.bstrVal = L"";
    v3.vt = 3;
    v4.vt = VT_BSTR;
    v4.bstrVal = L"";


    // Register the task
    IRegisteredTask *pRegisteredTask = NULL;
    hr = pRootFolder->lpVtbl->RegisterTaskDefinition(
        pRootFolder,
        name.TaskNameW,
        pTaskDefinition,
        6,     // Task creation flag (6 = CREATE_OR_UPDATE)
        v1,    // User
        v2,    // Password
        v3.vt, // Logon type
        v4,    // SDDL
        &pRegisteredTask
    );

    if (FAILED(hr)) {
        snprintf(msg,200,"Failed to register the task. Error: %x\n", hr);
        MessageBox(NULL,msg,"Register Task",MB_OK|MB_ICONERROR);
        RetVal = 1; // return error
    } else {
        snprintf(msg,200,"Task registered successfully.\n");
        MessageBox(NULL,msg,"Register Task",MB_OK|MB_ICONERROR);
    }

    // Clean up
    pTaskDefinition->lpVtbl->Release(pTaskDefinition);
    pRootFolder->lpVtbl->Release(pRootFolder);
    pService->lpVtbl->Release(pService);
    CoUninitialize();

    return RetVal;
}


It is a complete working 'one time' scheduler, just created the names structure and fill in that should be most all of the work.

If needing a daily or some repeated schedule look at TimoVJL's code near the top of this stream, he's done it.

John Z