1

I'm developing a Blazor Server app that exposes Windows Task Scheduler tasks for remote control with limited controls (I know I can remote to the server's Task Scheduler but this is a request to make it available to some other users inside my organization via local website). The library to access Windows Task Scheduler is Microsoft.Win32.TaskScheduler from NuGet. The app is pretty simple with only 1 component, user is authenticated with Windows Authentication, the app is hosted on an application server's IIS. I'm also new to Blazor and I'm learning by doing it.

Razor page

@page "/"

<h3>Task Manager</h3>

<div>
    @if (TaskList == null)
    {
        <div class="spinner"></div>
    }
    else
    {
        @foreach (var task in TaskList)
        {
            <p>
                @task.Name -
                @task.State -
                @task.Enabled
            </p>
            <p>
                @task.LastRunTime -
                @task.NextRunTime -
                @task.LastTaskResult
            </p>
            <p>
                <input id="btnPause" type="button" disabled="@(task.Enabled ? false : true)" value="Pause" @onclick="() => DisableTask(task)" />
                <input id="btnResume" type="button" disabled="@(task.Enabled ? true : false)" value="Resume" @onclick="() => EnableTask(task)" />
                <input id="btnRun" type="button" disabled="@(task.Enabled ? false : true)" value="Run now" @onclick="() => RunTask(task)" />
                <input id="btnStop" type="button" disabled="@(task.Enabled ? false : true)" value="Stop" @onclick="() => EndTask(task)" />
            </p>
            <br />
        }
    }
</div>

Code behind

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Win32.TaskScheduler;
using Microsoft.AspNetCore.Components;

namespace TaskSchedulerManager.Pages
{
    public partial class TaskManager: ComponentBase
    {
        public IEnumerable<Task> TaskList { get; set; }

        protected override System.Threading.Tasks.Task OnInitializedAsync()
        {
            LoadScheduleTask();
            return base.OnInitializedAsync();
        }

        private void LoadScheduleTask()
        {
            using (TaskService ts = new TaskService())
            {
                TaskFolder root = ts.RootFolder;
                TaskFolder sub = root.SubFolders["Task Scheduler Folder Name"];
                List<Task> tsList = new List<Task>();
                foreach (Task task in sub.Tasks)
                {
                    tsList.Add(task);
                }
                TaskList = new List<Task>(tsList);
            }
        }

        private void DisableTask(Task task)
        {
            task.Enabled = false;
            InvokeAsync(() =>
            {
                StateHasChanged();
            });
        }

        private void EnableTask(Task task)
        {
            task.Enabled = true;
            InvokeAsync(() =>
            {
                StateHasChanged();
            });
        }

        private void RunTask(Task task)
        {
            task.Run();
            InvokeAsync(() =>
            {
                StateHasChanged();
            });
        }

        private void EndTask(Task task)
        {
            task.Stop();
            InvokeAsync(() =>
            {
                StateHasChanged();
            });
        }
    }
}

When I run it locally, all the tasks are displayed on the page as intended. When I publish the app to IIS, the page only shows the header Task Manager, below that it's empty, on the same machine or from local machine. What did I miss?

Vy Do
  • 46,709
  • 59
  • 215
  • 313
  • Most probably the IIS user doesn't have permissions to access the tasks – Stilgar Apr 19 '21 at 19:44
  • How do I add permissions to read tasks? And to which user specifically? – toan nguyen khanh Apr 19 '21 at 20:17
  • I don't know. Some googling says you should give access to the scheduled tasks folder C:\Windows\System32\Tasks . The user account that needs this access is probably IUSR. Not sure though – Stilgar Apr 19 '21 at 20:51
  • I found a partial solution. In the using statement, if I enter a remote server and credentials, it will work for that server, per the instructions: ` using (TaskService ts = new TaskService(@"\\RemoteServer", "username", "domain", "password")) ` If I don't pass server credentials (aka local service), it will not work – toan nguyen khanh Apr 19 '21 at 20:52
  • not sure but if you use .net-core would a dotnet run run the app inside an express version ? so you wouldn't need a IIS ? – user3732793 Apr 20 '21 at 07:44
  • I need to host it as a website so other users can use. – toan nguyen khanh Apr 20 '21 at 14:53

1 Answers1

0

Webpage was just an empty page because the order of rendering was incorrect.

protected override System.Threading.Tasks.Task OnInitializedAsync()
{
    LoadScheduleTask();
    return base.OnInitializedAsync();
}

change to

return base.OnInitializedAsync();
LoadScheduleTask();
// terminal task...

or something another, always remember order of HTML rendering. Blazor's nature is render HTML by WebAssembly code.

Vy Do
  • 46,709
  • 59
  • 215
  • 313