How to run multiple SSIS packages programmatically by C#? - c#

guy, I used this code snippet, I execute multiple SSIS packages, when the first one finished, it takes a so long time to execute the another one, but I run the script command line on the CMD, it execute quickly. So I think it's the problem of code, do you know why? This is my code below:
SSISHelper.ExecuteSSISPackage("/F \"C:\\Users\\v-nashi\\Documents\\visual studio 2010\\projects\\ImportExcel\\ImportExcel\\LYO_DailyLogin.dtsx\"");
SSISHelper.ExecuteSSISPackage("/F \"C:\\Users\\v-nashi\\Documents\\visual studio 2010\\projects\\ImportExcel\\ImportExcel\\LYO_COSMOS_Activities.dtsx\"");
/// <summary>
/// Excuete SQL Server Integration Services packages with parameter.
/// </summary>
/// <param name="para">parameter</param>
/// <returns>bool</returns>
public static bool ExecutePackage(string parameter)
{
if (File.Exists(DTExec_Path) == false)
throw new Exception("The file DTExec.exe is not found, or the file is not exist.");
Process process = new Process();
process.StartInfo.FileName = DTExec_Path;
process.StartInfo.Arguments = parameter;
// True if the shell should be used when starting the process; false if the process should be created directly
// from the executable file.
process.StartInfo.UseShellExecute = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
Console.WriteLine("{0} {1}", DTExec_Path, parameter);
process.Start();
process.WaitForExit();
string[] results = process.StandardOutput.ReadToEnd().Split('\n');
foreach (string item in results)
{
if (item.Contains("DTExec: The package execution returned DTSER_SUCCESS (0)."))
return true;
}
return false;
}
I just want to run the SSIS package programmatically, or the better way?

Another way can be like this.
Microsoft.SqlServer.Dts.Runtime.Application app = new Microsoft.SqlServer.Dts.Runtime.Application();
string packagePath = "Path of your SSIS package";
Package package = app.LoadPackage(packagePath, null);
//Assign your variables here.
Variables vars = package.Variables;
vars["FileName"].Value = variables.FileName;
Microsoft.SqlServer.Dts.Runtime.DTSExecResult results = package.Execute();
if (results == DTSExecResult.Success)
{
//Do what u want after success.
}
For this U have to use this Microsoft.SqlServer.ManagedDTS library from microsoft. Try to find it in GAC or Something else site.
This is for single SSIS service in same way U can execute multiple one by one.

Related

Execute DTExec file via Command Line

I've got a dotnet core 6 project where I need to execute SSIS packages via dtexec.
So far I have the following code:
private void ExecutePackage()
{
var processOutput = string.Empty;
var processErrorOutput = string.Empty;
var command = #"/C dtexec /file ""C:\git\star\tests\Star.Shared.UnitTests\test-artifacts\TestPackage.dtsx""";
var process = new Process();
process.StartInfo.FileName = "cmd.exe";
process.StartInfo.Arguments = command;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
processOutput = process.StandardOutput.ReadToEndAsync().Result;
processErrorOutput = process.StandardError.ReadToEndAsync().Result;
if (processOutput != string.Empty)
{
_logger.LogInformation("{output}", processOutput);
}
if (processErrorOutput != string.Empty)
_logger.LogError("{errors}", processErrorOutput);
}
When running this via my unit test, it just seems to hang and I'm unsure as to why.
On one of my previous attempts to get this file to run I got the following message:
An error occurred trying to start process 'dtexec /file "C:\git\star\tests\Star.Shared.UnitTests\test-artifacts\Test_Package.dtsx' with working directory 'C:\git\star\tests\Star.Shared.UnitTests\bin\Debug\net6.0'. The system cannot find the file specified.
Which is telling me that the last time I ran this, it was looking in my tests bin folder for the package instead of where the package is stored.
Is there a setting that I'm missing / set wrong?

Process.Start - struggling with arguments to TFS command line

I'm trying to execute TFS via Process.Start but am having some difficulty and I can't understand why. Here's my code snippet:
/// <summary>
/// Get the entire history for a project
/// </summary>
public void GetHistory(String project)
{
ProcessStartInfo info = new ProcessStartInfo();
String fileName = Path.GetTempFileName();
info.Arguments = String.Format("history \"{0}\" /recursive /format:Detailed /noprompt > {1}", "c:\\source\\ " + project, fileName);
info.FileName = "\"C:\\Program Files (x86)\\Microsoft Visual Studio 11.0\\Common7\\IDE\\tf.exe\"";
info.RedirectStandardError = true;
info.UseShellExecute = false;
Process process = new Process();
process.StartInfo = info;
process.Start();
process.WaitForExit();
Console.WriteLine(process.StandardError.ReadToEnd());
Console.WriteLine("History written to " + fileName);
Console.ReadKey();
}
This is resulting in a set of arguments like so (I've just removed the full project name):
I then get the following error:
The history command takes exactly one item.
If I piece the string together and execute in a normal command line however then it works:
Can anyone tell me what I'm missing?
You can't redirect output to a file in the Process.Start arguments. File redirection is a function of the shell.
If you want to put the history into a file, you will need to File.Open the file yourself, read the output of the tf history command and write it to the file.
Or you could use a command script or PowerShell script.

How to resync time using w32tm in C# code?

Please give me a working code for achieving the time synchronization using w32tm.exe in C#.net. I already tried. Code shown below.
System.Diagnostics.Process p;
string output;
p = new System.Diagnostics.Process();
p.StartInfo = procStartInfo;
p.StartInfo.FileName = "w32tm";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.Arguments = " /resync /computer:xxxxx977";
p.Start();
p.WaitForExit();
output = p.StandardOutput.ReadLine().ToString();
MessageBox.Show(output);
But i am getting the following error
The specified module could not be found. (0x8007007E).
and my requirement also wants to redirect the standardoutput for the success message.
you can try following C# code to enable date time sync from NTP server.
by the way i guess which is /resync command number so that i wouldn't have to launch that dirty external process
/// <summary>Synchronizes the date time to ntp server using w32time service</summary>
/// <returns><c>true</c> if [command succeed]; otherwise, <c>false</c>.</returns>
public static bool SyncDateTime()
{
try
{
ServiceController serviceController = new ServiceController("w32time");
if (serviceController.Status != ServiceControllerStatus.Running)
{
serviceController.Start();
}
Logger.TraceInformation("w32time service is running");
Process processTime = new Process();
processTime.StartInfo.FileName = "w32tm";
processTime.StartInfo.Arguments = "/resync";
processTime.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
processTime.Start();
processTime.WaitForExit();
Logger.TraceInformation("w32time service has sync local dateTime from NTP server");
return true;
}
catch (Exception exception)
{
Logger.LogError("unable to sync date time from NTP server", exception);
return false;
}
}
detailled explanation :
windows have a service, called w32time, which can sync time on your computer, first i check that the service is running, using ServiceController class, then, because i don't know which is the resync command number so that i can use ServiceController launch command method, i use a ProcessStart to launch a dos command on that services : w32tm /resync
The error is occurring when the .Net runtime JITs the method you're about to step into, because it couldn't find one of the types used by the method.
What exactly does the method that you can't step into do, and what types / methods does it use?
Refer this Link
So check whether any item you tried to load is in the folder or not.

Execute multiple commands in same environment from C#

I'm developing a small C# GUI tool which is supposed to fetch some C++ code and compile it after going through some wizard. This works all nice if I run it from a command prompt after running the famous vcvarsall.bat. Now I would like the user not to go to a command prompt first but have the program call vcvars followed by nmake and other tools I need. For that to work the environment variables set by vcvars should obviously be kept.
How can I do that?
The best solution I could find yet was to create a temporary cmd/bat script which will call the other tools, but I wonder if there is a better way.
Update: I meanwhile experimented with batch files and cmd. When using batch files vcvars will terminate the complete batch execution so my second command (i.e. nmake) won't be executed. My current workaround is like this (shortened):
string command = "nmake";
string args = "";
string vcvars = "...vcvarsall.bat";
ProcessStartInfo info = new ProcessStartInfo();
info.WorkingDirectory = workingdir;
info.FileName = "cmd";
info.Arguments = "/c \"" + vcvars + " x86 && " + command + " " + args + "\"";
info.CreateNoWindow = true;
info.UseShellExecute = false;
info.RedirectStandardOutput = true;
Process p = Process.Start(info);
This works, but the output from the cmd call is not captured. Still looking for something better
I have a couple of different suggestions
You may want to research using MSBuild instead of NMake
It's more complex, but it can be controlled directly from .Net, and it is the format of VS project files for all projects starting with VS 2010, and for C#/VB/etc. projects earlier than that
You could capture the environment using a small helper program and inject it into your processes
This is probably a bit overkill, but it would work. vsvarsall.bat doesn't do anything more magical than set a few environment variables, so all you have to do is record the result of running it, and then replay that into the environment of processes you create.
The helper program (envcapture.exe) is trivial. It just lists all the variables in its environment and prints them to standard output. This is the entire program code; stick it in Main():
XElement documentElement = new XElement("Environment");
foreach (DictionaryEntry envVariable in Environment.GetEnvironmentVariables())
{
documentElement.Add(new XElement(
"Variable",
new XAttribute("Name", envVariable.Key),
envVariable.Value
));
}
Console.WriteLine(documentElement);
You might be able to get away with just calling set instead of this program and parsing that output, but that would likely break if any environment variables contained newlines.
In your main program:
First, the environment initialized by vcvarsall.bat must be captured. To do that, we'll use a command line that looks like cmd.exe /s /c " "...\vcvarsall.bat" x86 && "...\envcapture.exe" ". vcvarsall.bat modifies the environment, and then envcapture.exe prints it out. Then, the main program captures that output and parses it into a dictionary. (note: vsVersion here would be something like 90 or 100 or 110)
private static Dictionary<string, string> CaptureBuildEnvironment(
int vsVersion,
string architectureName
)
{
// assume the helper is in the same directory as this exe
string myExeDir = Path.GetDirectoryName(
Assembly.GetExecutingAssembly().Location
);
string envCaptureExe = Path.Combine(myExeDir, "envcapture.exe");
string vsToolsVariableName = String.Format("VS{0}COMNTOOLS", vsVersion);
string envSetupScript = Path.Combine(
Environment.GetEnvironmentVariable(vsToolsVariableName),
#"..\..\VC\vcvarsall.bat"
);
using (Process envCaptureProcess = new Process())
{
envCaptureProcess.StartInfo.FileName = "cmd.exe";
// the /s and the extra quotes make sure that paths with
// spaces in the names are handled properly
envCaptureProcess.StartInfo.Arguments = String.Format(
"/s /c \" \"{0}\" {1} && \"{2}\" \"",
envSetupScript,
architectureName,
envCaptureExe
);
envCaptureProcess.StartInfo.RedirectStandardOutput = true;
envCaptureProcess.StartInfo.RedirectStandardError = true;
envCaptureProcess.StartInfo.UseShellExecute = false;
envCaptureProcess.StartInfo.CreateNoWindow = true;
envCaptureProcess.Start();
// read and discard standard error, or else we won't get output from
// envcapture.exe at all
envCaptureProcess.ErrorDataReceived += (sender, e) => { };
envCaptureProcess.BeginErrorReadLine();
string outputString = envCaptureProcess.StandardOutput.ReadToEnd();
// vsVersion < 110 prints out a line in vcvars*.bat. Ignore
// everything before the first '<'.
int xmlStartIndex = outputString.IndexOf('<');
if (xmlStartIndex == -1)
{
throw new Exception("No environment block was captured");
}
XElement documentElement = XElement.Parse(
outputString.Substring(xmlStartIndex)
);
Dictionary<string, string> capturedVars
= new Dictionary<string, string>();
foreach (XElement variable in documentElement.Elements("Variable"))
{
capturedVars.Add(
(string)variable.Attribute("Name"),
(string)variable
);
}
return capturedVars;
}
}
Later, when you want to run a command in the build environment, you just have to replace the environment variables in the new process with the environment variables captured earlier. You should only need to call CaptureBuildEnvironment once per argument combination, each time your program is run. Don't try to save it between runs though or it'll get stale.
static void Main()
{
string command = "nmake";
string args = "";
Dictionary<string, string> buildEnvironment =
CaptureBuildEnvironment(100, "x86");
ProcessStartInfo info = new ProcessStartInfo();
// the search path from the adjusted environment doesn't seem
// to get used in Process.Start, but cmd will use it.
info.FileName = "cmd.exe";
info.Arguments = String.Format(
"/s /c \" \"{0}\" {1} \"",
command,
args
);
info.CreateNoWindow = true;
info.UseShellExecute = false;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
foreach (var i in buildEnvironment)
{
info.EnvironmentVariables[(string)i.Key] = (string)i.Value;
}
using (Process p = Process.Start(info))
{
// do something with your process. If you're capturing standard output,
// you'll also need to capture standard error. Be careful to avoid the
// deadlock bug mentioned in the docs for
// ProcessStartInfo.RedirectStandardOutput.
}
}
If you use this, be aware that it will probably die horribly if vcvarsall.bat is missing or fails, and there may be problems with systems with locales other than en-US.
There is probably no better way than collect all the data you need, generate bat file and run it using Process class.
As you wrote, you are redirecting output, which means you must set UseShellExecute = false; so I think there is no way to set your variables other then calling SET from the bat file.
EDIT: adding a specific use case for nmake calling
I've needed to get various "build path stuff" in the past, and this is what I've used - you may need to tweak things here or there to suit, but basically, the only thing that vcvars does is set up a bunch of paths; these helper methods go fetch those path names, you'd just need to pass them into your start info:
public static string GetFrameworkPath()
{
var frameworkVersion = string.Format("v{0}.{1}.{2}", Environment.Version.Major, Environment.Version.Minor, Environment.Version.Build);
var is64BitProcess = Environment.Is64BitProcess;
var windowsPath = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
return Path.Combine(windowsPath, "Microsoft.NET", is64BitProcess ? "Framework64" : "Framework", frameworkVersion);
}
public static string GetPathToVisualStudio(string version)
{
var is64BitProcess = Environment.Is64BitProcess;
var registryKeyName = string.Format(#"Software\{0}Microsoft\VisualStudio\SxS\VC7", is64BitProcess ? #"Wow6432Node\" : string.Empty);
var vsKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(registryKeyName);
var versionExists = vsKey.GetValueNames().Any(valueName => valueName.Equals(version));
if(versionExists)
{
return vsKey.GetValue(version).ToString();
}
else
{
return null;
}
}
And you'd take advantage of this stuff via something like:
var paths = new[]
{
GetFrameworkPath(),
GetPathToVisualStudio("10.0"),
Path.Combine(GetPathToVisualStudio("10.0"), "bin"),
};
var previousPaths = Environment.GetEnvironmentVariable("PATH").ToString();
var newPaths = string.Join(";", previousPaths.Split(';').Concat(paths));
Environment.SetEnvironmentVariable("PATH", newPaths);
var startInfo = new ProcessStartInfo()
{
FileName = "nmake",
Arguments = "whatever you'd pass in here",
};
var process = Process.Start(startInfo);

Trouble Starting Selenium Process from a Console Application

Let me be clear:
- I have Java.exe in my path environment variable
- So if I want to run a "selenium-server" I will do :
1. Start cmd.exe
Microsoft Windows [Version 5.2.3790]
(C) Copyright 1985-2003 Microsoft Corp.
C:\Documents and Settings\cnguyen>
2. Then:
C:\Documents and Settings\cnguyen>cd C:\Selenium RC 0.9.2\selenium-server-0.9.2
3. Next, I'm in the directory that I want so I run:
C:\Documents and Settings\cnguyen>cd C:\Selenium RC 0.9.2\selenium-server-0.9.2
C:\Selenium RC 0.9.2\selenium-server-0.9.2>java -jar selenium-server.jar
09:26:18.586 INFO - Java: Sun Microsystems Inc. 16.3-b01
09:26:18.586 INFO - OS: Windows 2003 5.2 x86
09:26:18.586 INFO - v0.9.2 [2006], with Core v0.8.3 [1879]
09:26:18.633 INFO - Version Jetty/5.1.x
09:26:18.633 INFO - Started HttpContext[/selenium-server/driver,/selenium-server
/driver]
09:26:18.633 INFO - Started HttpContext[/selenium-server,/selenium-server]
09:26:18.633 INFO - Started HttpContext[/,/]
09:26:18.648 INFO - Started SocketListener on 0.0.0.0:4444
09:26:18.648 INFO - Started org.mortbay.jetty.Server#16a55fa
And here is what I got so far, it compiled but not showing anything :(
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace SeleniumProcessExample
{
public class SeleniumProcess
{
private Process pro;
public SeleniumProcess()
{
pro = new Process();
Directory.SetCurrentDirectory( #"C:\Selenium RC 0.9.2\selenium-server-0.9.2" );
pro.StartInfo.FileName = "java";
pro.StartInfo.Arguments = " -jar selenium-server.jar";
pro.StartInfo.RedirectStandardOutput = true;
pro.StartInfo.RedirectStandardError = true;
pro.StartInfo.UseShellExecute = false;
pro.Start();
string strOutput = pro.StandardOutput.ReadToEnd();
string strError = pro.StandardError.ReadToEnd();
Console.WriteLine( strOutput );
Console.WriteLine( strError );
Console.Out.Flush();
pro.CloseMainWindow();
}
}
}
EDIT: if you intent is to hide the
selenium-server output window, you're
going to have to make some
asynchronous calls. I can go into the
details if this is indeed your intent.
I would love to see this. Would you mind showing me how to do this? Thanks a lot for your suggestion ;)
This works for me...
/// <summary>
/// Creates new process to run and executable file, and return the output
/// </summary>
/// <param name="program">The name of the executable to run</param>
/// <param name="arguments">Any parameters that are required by the executable</param>
/// <param name="silent">Determines whether or not we output execution details</param>
/// <param name="workingDirectory">The directory to run the application process from</param>
/// <param name="standardErr">The standard error from the executable. String.Empty if none returned.</param>
/// <param name="standardOut">The standard output from the executable. String.Empty if none returned, or silent = true</param>
/// <returns>The application's exit code.</returns>
public static int Execute(string program, string arguments, bool silent, string workingDirectory, out string standardOut, out string standardErr)
{
standardErr = String.Empty;
standardOut = String.Empty;
//sometimes it is not advisable to output the arguments e.g. passwords etc
if (!silent)
{
Console.WriteLine(program + " " + arguments);
}
Process proc = Process.GetCurrentProcess();
if (!string.IsNullOrEmpty(workingDirectory))
{
//execute from the specific working directory if specified
proc.StartInfo.WorkingDirectory = workingDirectory;
}
proc.EnableRaisingEvents = true;
proc.StartInfo.FileName = program;
proc.StartInfo.Arguments = arguments;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardError = true;
proc.Start();
proc.WaitForExit();
//only display the console output if not operating silently
if (!silent)
{
if (proc.StandardOutput != null)
{
standardOut = proc.StandardOutput.ReadToEnd();
Console.WriteLine(standardOut);
}
}
if (proc.StandardError != null)
{
standardErr = proc.StandardError.ReadToEnd();
Console.WriteLine(standardErr);
}
proc.StandardOutput.Close();
proc.StandardError.Close();
return proc.ExitCode;
}
Your pro.StandardOutput.ReadToEnd() call will block until the executable terminates. Since you're starting a server that will launch and wait for output, you'll never get anything.
If you just want to see the output of the server, set UseShellExecute to true and RedirectStandardOutput and RedirectStandardError to false. (or just delete those three lines) This will cause a new console window to open and show the output from selenium-server.
EDIT: if you intent is to hide the selenium-server output window, you're going to have to make some asynchronous calls. I can go into the details if this is indeed your intent.
I would start by changing the code for the process to this to see if it starts java.exe
pro = new Process();
pro.StartInfo.FileName = #"C:\Selenium RC 0.9.2\selenium-server-0.9.2\java.exe";
pro.StartInfo.Arguments = " -jar selenium-server.jar";
pro.StartInfo.RedirectStandardOutput = true;
pro.StartInfo.RedirectStandardError = true;
pro.StartInfo.UseShellExecute = false;
pro.Start();
Chances are that your program is blocking on the call to pro.StandardOutput.ReadToEnd(). Consider using the non-blocking BeginOutputReadLine() method (more at http://msdn.microsoft.com/en-us/library/system.diagnostics.process.beginoutputreadline.aspx and http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput.aspx).
class LaunchJava
{
private static Process myProcessProcess;
private static StreamWriter myProcessStandardInput;
private static Thread thist = Thread.CurrentThread;
public static void DoJava()
{
// Initialize the process and its StartInfo properties.
// The sort command is a console application that
// reads and sorts text input.
myProcess= new Process();
myProcess.StartInfo.Arguments = "-jar selenium-server.jar";
myProcess.StartInfo.FileName = #"C:\Documents and Settings\cnguyen\java.exe";
// Set UseShellExecute to false for redirection.
myProcess.StartInfo.UseShellExecute = false;
// Redirect the standard output of the sort command.
// This stream is read asynchronously using an event handler.
myProcess.StartInfo.RedirectStandardOutput = true;
myProcessOutput = new StringBuilder("");
// Set our event handler to asynchronously read the sort output.
myProcess.OutputDataReceived += new DataReceivedEventHandler(myProcessOutputHandler);
// Redirect standard input as well. This stream
// is used synchronously.
myProcess.StartInfo.RedirectStandardInput = true;
Console.WriteLine("Start.");
// Start the process.
myProcess.Start();
// Use a stream writer to synchronously write the sort input.
myProcessStandardInput = myProcess.StandardInput;
// Start the asynchronous read of the sort output stream.
myProcess.BeginOutputReadLine();
// Wait for the process to end on its own.
// as an alternative issue some kind of quit command in myProcessOutputHandler
myProcess.WaitForExit();
// End the input stream to the sort command.
myProcessInput.Close();
myProcessProcess.Close();
}
private static void myProcessOutputHandler(object sendingProcess, DataReceivedEventArgs Output)
{
// do interactive work here if needed...
if (!String.IsNullOrEmpty(Output.Data))
{ myProcess.StandardInput.BaseStream.Write(bytee,0,bytee.GetLength);
}
}

Categories

Resources