Moving Multiple Scheduled Tasks into a Folder on Windows Server 2008

Windows Server logo

Let’s say you have a whole lot of scheduled tasks you want moved into a subfolder in the Task Scheduler.  Since you can’t just drag and drop the tasks, and must export the jobs into XML files to be imported, we will use similar commands as yesterday’s post.

For this, you will need an empty folder.  I use C:temp for this example.  If your folder does not already exist, create one.  Also, you will need a folder in the Task Scheduler GUI to exist.  Create it using the Task Scheduler GUI.

Export all jobs you want to move into XML files:

  1. Open the Command Prompt, and change to the C:WindowsTasks folder.
  2. Type the following command and press Enter:ย FOR /R . %F in (*.job) do schtasks /Query /TN “%~nF” /XML > “C:temp%~nF.xml”

This will create all XML files in the C:temp folder.  You can then delete from that folder any XMLs for jobs that you don’t want moved.

Next, delete the jobs in the original folder either using the command line or through the GUI.

Lastly, enter the following command and press Enter:

FOR /R c:temp %F in (*.xml) do schtasks /Create /S <<server name>> /RU <<username>> /RP <<password>> /XML “c:temp%~nF.xml” /TN “<<Task Scheduler folder name>>%~nF”

This will recreate the job files and put the tasks into the folder (in the GUI) of your choosing.

Importing Multiple Scheduled Tasks from Windows XP/Server 2003 to Windows 7/Server 2008

Windows Server logo

On Windows XP and Windows Server 2003, Scheduled Tasks are stored as binary files with the “.job” extension. They are also stored in this manner on Windows Server 2008. Also, the jobs are stored in the same location in both operating systems, namely, “C:WindowsTasks”. So if you want to copy jobs from one to the other, you should just be able to copy the files, right? No, that would be too easy. For whatever reason, Microsoft chose to make it much more difficult to migrate jobs than this.

To successfully import jobs, several steps are required.

First of all, copy all “.job” files on the XP/2003 box into a folder of your choosing on the 2008 box. I’ll create “C:temptasks” for this example.
Next, copy two files – schtasks.exe and schedsvc.dll – from C:WindowsSystem32 on the source box into the C:temptasks folder on the destination box.
Thirdly, copy (don’t move) the job files from the C:temptasks folder into the C:WindowsTasks folder on the destination box.

Now for the fun part. To import a job, the following command must be issued from the Command Prompt:
schtasks /change /TN <<Scheduled Job Name>> /RU <<Username>> /RP <<Password>>

This command will only import one job – the one with whatever name you put in the command.  If you have many jobs to import, this will not be practical.  You’ll need a script to do this, but fortunately the script is simple.

By using a for loop and a basic regular expression, this can be done at the command line:

c:temptasks>FOR /R . %F in (*.*) do schtasks /change /TN “%~nF” /RU <<Username>> /RP <<Password>>

This will import all of your job files into the Task Scheduler. The tasks may be in an enabled state, so be sure to check this if you don’t want them to run yet.

Fixing the CrystalReportViewer “Next Page” Navigation Problem

logos for Visual Studio and Crystal Reports

Note: This is an update to my last post.

I discovered that the version of the SAP CrystalReportViewer for VS2010 (13.0.2000.0) that I am using appears to have a bug that prevents the user from proceeding past page 2 when using the Next Page button. Pages can still be directly accessed by typing in the page number, but clicking the Next Page button keeps you on page 2.

I read quite a few possible solutions to this, such as moving the code that loads the Crystal Report from Page_Load to Page_Init, but this did not work for me. Also, there is an update to 13.0.2000.0 that might fix this issue, but I am not in a position to update the version of the SAP Crystal Reports software at present.

Fortunately, I was able to code around this bug. First, I disabled the navigation buttons on the viewer itself. Next, I put my own navigation buttons above the viewer component on the ASP.NET Web Form. Lastly, I wrote the code that handles the click events for these buttons.

Here is the code for the Reports.aspx page:

<%@ Page Language="VB" AutoEventWireup="true" CodeFile="Reports.aspx.vb" Inherits="Pages_Reports" %>

<%@ Register Assembly="CrystalDecisions.Web, Version=13.0.2000.0, Culture=neutral, PublicKeyToken=692fbea5521e1304"
    Namespace="CrystalDecisions.Web" TagPrefix="CR" %>

<!DOCTYPE html>

<html>
    <head runat="server">
        <title></title>
        <meta http-equiv="X-UA-Compatible" content="IE=9,chrome=1" />
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
            <asp:ScriptManager ID="ScriptManager1" runat="server" AsyncPostBackTimeout="0" EnableHistory="True">
            </asp:ScriptManager>
            <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
                <ContentTemplate>
                    <asp:Label ID="Label1" runat="server" Text="Page navigation:   "></asp:Label>
                    <asp:Button runat="server" ID="btnFirst"
                        Text="|<--" ToolTip="Go to first page"/>
                    <asp:Button runat="server" ID="btnPrev"
                        Text="<<" ToolTip="Go to previous page"/>
                    <asp:Button runat="server" ID="btnNext"
                        Text=">>" ToolTip="Go to next page"/>
                    <asp:Button runat="server" ID="btnLast"
                        Text="-->|" ToolTip="Go to last page"/>
                    <CR:CrystalReportViewer ID="CrystalReportViewer1" runat="server" AutoDataBind="true"
                    HasCrystalLogo="False" GroupTreeStyle-ShowLines="True"
                        Width="100%" Height="100%"
                    EnableDatabaseLogonPrompt="False" ReuseParameterValuesOnRefresh="True"
                        HasPageNavigationButtons="False" ShowAllPageIds="True"
                        EnableParameterPrompt="False" />
                </ContentTemplate>
            </asp:UpdatePanel>
        </div>
        </form>
    </body>
</html>

…and the code file Reports.aspx.vb…

Imports CrystalDecisions.Shared
Imports System.IO
Imports CrystalDecisions.CrystalReports.Engine
Imports CrystalDecisions.Web

Partial Class Pages_Reports
    Inherits System.Web.UI.Page

    Protected strQueryString As String
    Protected intLastPage As Integer
    Protected intCurPage As Integer

    Protected Sub Page_Init(sender As Object, e As System.EventArgs) Handles Me.Init
        strQueryString = Request.QueryString.ToString()
        Page.Title = Replace(Request.QueryString("rpt").ToString(), ".rpt", "")
        If Not Page.IsPostBack Then
            'Do nothing
        ElseIf Session(strQueryString) IsNot Nothing Then
            CrystalReportViewer1.ReportSource = Session(strQueryString)
        End If
    End Sub

    Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
        If Not Page.IsPostBack Then
            Dim strReportName As String = Request.QueryString("rpt")
            Dim strSingleDate As String = Request.QueryString("single")
            Dim strBeginDate As String = Request.QueryString("begin")
            Dim strEndDate As String = Request.QueryString("end")

            Dim bSingleDate As Boolean = False
            Dim bMultiDate As Boolean = False

            Dim arrSingleDate() As String = _
               Utilities.AppSettingsFunction.getValue("SingleDate").Split(",")
            Dim arrMultiDate() As String = _
               Utilities.AppSettingsFunction.getValue("MultiDate").Split(",")

            If Array.IndexOf(arrSingleDate, strReportName) <> -1 Then
                bSingleDate = True
                bMultiDate = False
            ElseIf Array.IndexOf(arrMultiDate, strReportName) <> -1 Then
                bSingleDate = False
                bMultiDate = True
            End If

            Dim connInfo As New ConnectionInfo
            Dim rptDoc As New ReportDocument

            'setup the connection
            connInfo = Functions.GetConnectionInfo()

            'load the Crystal Report
            rptDoc.Load(Server.MapPath( _
               Utilities.AppSettingsFunction.getValue("ReportFolder") _
               & strReportName))

            'apply logon information
            For Each tbl As CrystalDecisions.CrystalReports.Engine.Table In rptDoc.Database.Tables
                Dim repTblLogonInfo As TableLogOnInfo = tbl.LogOnInfo
                repTblLogonInfo.ConnectionInfo = connInfo
                tbl.ApplyLogOnInfo(repTblLogonInfo)
            Next

            'add required parameters
            If bSingleDate Then
                rptDoc.SetParameterValue("REPORT_DATE", strSingleDate & " 23:59:59")
            End If

            If bMultiDate Then
                rptDoc.SetParameterValue("BEGIN_DATE", strBeginDate & " 00:00:00")
                rptDoc.SetParameterValue("END_DATE", strEndDate & " 23:59:59")
            End If

            'Set, bind, and display Crystal Reports Viewer data source
            Session(strQueryString) = rptDoc
            CrystalReportViewer1.ReportSource = Session(strQueryString)
            CrystalReportViewer1.ShowLastPage()
            CrystalReportViewer1.ShowFirstPage()
            Session(strQueryString + "_pagenum") = 1
            Session(strQueryString + "_lastpagenum") = GetLastCRPageNumber()
        ElseIf Session(strQueryString) IsNot Nothing Then
            CrystalReportViewer1.ReportSource = Session(strQueryString)
        End If
        GetPageNums()
    End Sub

    Private Sub GetPageNums()
        intCurPage = Session(strQueryString + "_pagenum")
        intLastPage = Session(strQueryString + "_lastpagenum")
        ButtonsCheck()
    End Sub

    Private Sub SetCurPageNum(intPage As Integer)
        GetPageNums()
        Session(strQueryString + "_pagenum") = intPage
        ButtonsCheck()
    End Sub

    Protected Sub btnNext_Click(sender As Object, e As System.EventArgs) Handles btnNext.Click
        GetPageNums()
        If intCurPage < intLastPage Then
            SetCurPageNum(intCurPage + 1)
            CrystalReportViewer1.ShowNthPage(intCurPage + 1)
        Else
            CrystalReportViewer1.ShowNthPage(intCurPage)
        End If
    End Sub

    Protected Sub btnPrev_Click(sender As Object, e As System.EventArgs) Handles btnPrev.Click
        GetPageNums()
        If intCurPage > 1 Then
            SetCurPageNum(intCurPage - 1)
            CrystalReportViewer1.ShowNthPage(intCurPage - 1)
        Else
            CrystalReportViewer1.ShowNthPage(intCurPage)
        End If
    End Sub

    Private Function GetCRPageNumber() As Integer
        Dim vi As ViewInfo = CrystalReportViewer1.ViewInfo
        Return vi.PageNumber
    End Function

    Private Function GetLastCRPageNumber() As Integer
        Dim vi As ViewInfo = CrystalReportViewer1.ViewInfo
        Return vi.LastPageNumber
    End Function

    Protected Sub ButtonsCheck()
        If intCurPage = 1 Then
            btnFirst.Enabled = False
            btnPrev.Enabled = False
        Else
            btnFirst.Enabled = True
            btnPrev.Enabled = True
        End If

        If intCurPage = intLastPage Then
            btnLast.Enabled = False
            btnNext.Enabled = False
        Else
            btnLast.Enabled = True
            btnNext.Enabled = True
        End If
    End Sub

    Protected Sub CrystalReportViewer1_Navigate(source As Object, e As CrystalDecisions.Web.NavigateEventArgs) Handles CrystalReportViewer1.Navigate
        SetCurPageNum(e.NewPageNumber)
    End Sub

    Protected Sub btnFirst_Click(sender As Object, e As System.EventArgs) Handles btnFirst.Click
        CrystalReportViewer1.ShowFirstPage()
        GetPageNums()
    End Sub

    Protected Sub btnLast_Click(sender As Object, e As System.EventArgs) Handles btnLast.Click
        CrystalReportViewer1.ShowLastPage()
        GetPageNums()
    End Sub
End Class