Introduction
If there is a need for a setup component that can read an XML setting file (SetupScripts.xml) and run methods or scripts defined in it asynchronously, then this Setup Grid component is what you are looking for. To use this component, the methods in an assembly, a batch file, or a path to a T-SQL file can be defined in SetupScripts.xml and we can call StartSetup()
to execute them asynchronously. That means, when the setup is in progress, other operations are not suspended.
Solution
SetupGridUC
is a UserControl that contains a DataGridView
to show the scripts and the progress status when the setup is running. A typical scenario of using the SetupGridUC
component is as follows:
- Initialize
SetupGridUC
from the SetupScripts.xml file using the Load()
method. - Start setup using the
StartSetup()
method of SetupGridUC
and also StopSetup(false)
when it is started. - In order to do some actions (e.g., disable buttons) when the setup is being started or finished, use the following events:
void stpGrid_ProgressFinished(object sender, EventArgs e)
is called when the setup finishes.void stpGrid_ProgressStarted(object sender, EventArgs e)
is called when the setup starts.
Implementation
The StartSetup()
method starts the setup process asynchronously by calling the ExecuteScript()
method of the SetupGridUC
component using BeginInvoke()
.
private MethodDelegate methodDelegate;
...
asyncResult = methodDelegate.BeginInvoke(null, null);
Using visual components, like refreshing the DataGridView
from another thread of execution, can cause a Cross-thread operation not valid exception. To bypass this exception, the Invoke()
method of the component should be used. SafeInvoke()
uses the Invoke()
method of a component to call a parameter-less method (the void MethodName()
delegate) in the component:
private delegate void VoidDelegate();
...
private void SafeInvoke(Control ctrl, VoidDelegate delgt)
{
if (ctrl.InvokeRequired)
{
VoidDelegate d = new VoidDelegate(delgt);
ctrl.Invoke(d, new object[] { });
}
}
To refresh asyncSetupGrid
in another thread, use the SafeInvoke()
method as follow:
private void command_Progress(object sender, ProgressEventArgs e)
{
SafeInvoke(asyncSetupGrid, Refresh);
}
Note: The void command_Progress
event is called from another thread, so the visual components should use Invoke()
to call a method.
The scripts or methods can be defined in SetupScripts.xml as follows:
<tblSetupScripts>
<ID>2</ID>
<Name>S3</Name>
<Title>Execute SQLQuery.sql against Database</Title>
<Script>SQLQuery.sql</Script>
<Kind>SQLScript</Kind>
</tblSetupScripts>
Tag meanings:
<Name>:
Name tag should be a unique name that user can assign to the script to be run
<Title>:
Title tag is an explanation for the script
<Script>:
Script tag meanings depend on <Kind> tag that can be set as follows:
<Kind>Method</Kind>
<Script>
{Assembly file name}, {Namespace. class name}, {Method name}
/// <summary>
/// This method is being called using reflection.
/// It is defined in SetupScripts.xml file.
/// Developer should pass following two delegate parameters
/// to it that are doing some actions on calling SetupGrid object.
/// </summary>
/// <param name="doProgress"> This method sets progress bar
/// status in the subject row of the SetupGrid </param>
/// <param name="doHalt"> This method returns true
/// if user stops the setup process </param>
public void Mehtod(SetupGrid.ScriptCommands.ProgressDelegate doProgress,
SetupGrid.ScriptCommands.HaltDelegate doHalt)
{
...
}
</Script>
<Kind>SQLScript</Kind>
<Script> {Path of SQL script to be executed}
The T-SQL commands are separated using 'Go'
command from each other. For example:
...
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
...
</Script>
<Kind>BatchFile</Kind>
<Script> { The path and file name of the Batch file to be executed}
The batch file execution will open a Command Prompt
window and waits the command is finished.
</Script>
Deployment
This sample contains two assemblies:
- Main.exe as the executable to host the SetupGrid component.
- SetupGrid.dll as the library that contains the SetupGridUC component.
In order to test the SQL scripts, create a database in SQL Server and add proper connection settings to the database in Properties/Settings.settings file.