Batch file v PowerShell script - windows

Is there any advantage to using a PowerShell script over a batch file?
By which I mean, if I have something which will run in either, is there anything to warrant detecting if PowerShell is installed and using it over a batch file if it is?
SCHTASKS works in exactly the same manner on both. Given that, is there any differentiator between them?

The following code executes the same code via both processes:
internal class Program
{
private static void Main()
{
const string COMMAND = #"SCHTASKS /QUERY /XML ONE";
Collection<PSObject> psObjects;
using (var ps = PowerShell.Create())
{
ps.AddScript(COMMAND);
psObjects = ps.Invoke();
}
var process = new Process
{
StartInfo = new ProcessStartInfo
{
UseShellExecute = false,
RedirectStandardOutput = true,
FileName = "cmd.exe",
Arguments = "/C " + COMMAND
}
};
process.Start();
string cmdTasks;
using (var reader = process.StandardOutput)
{
cmdTasks = reader.ReadToEnd();
}
}
}
The response object from the two approaches differs. The Process.Start() approach returns a string, which I would have to parse whereas invoking PowerShell gives me a collection of PSObject objects.

Related

Why does my for loop increment past where it should stop?

I am attempting to increase the speed at which files in my application download by downloading them in parallel. Previously I was downloading them sequentially and it worked fine but when I attempted to download them in parallel I ran into unexplained issues.
Here is my method in which I downloaded the files in sequence:
public IActionResult DownloadPartFiles([FromBody] FileRequestParameters parameters)
{
List<InMemoryFile> files = new List<InMemoryFile>();
for (int i = 0; i < parameters.FileNames.Length; i++)
{
InMemoryFile inMemoryFile = GetInMemoryFile(parameters.FileLocations[i], parameters.FileNames[i]).Result;
files.Add(inMemoryFile);
}
byte[] archiveFile = null;
using (MemoryStream archiveStream = new MemoryStream())
{
using (ZipArchive archive = new ZipArchive(archiveStream, ZipArchiveMode.Create, true))
{
foreach (InMemoryFile file in files)
{
ZipArchiveEntry zipArchiveEntry = archive.CreateEntry(file.FileName, CompressionLevel.Optimal);
using (MemoryStream originalFileStream = new MemoryStream(file.Content))
using (Stream zipStream = zipArchiveEntry.Open())
{
originalFileStream.CopyTo(zipStream);
}
}
}
archiveFile = archiveStream.ToArray();
}
return File(archiveFile, "application/octet-stream");
}
Here is the method changed to download the files in parallel:
public async Task<IActionResult> DownloadPartFiles([FromBody] FileRequestParameters parameters)
{
List<Task<InMemoryFile>> fileTasks = new List<Task<InMemoryFile>>();
for (int i = 0; i < parameters.FileNames.Length; i++)
{
if(i == parameters.FileNames.Length - 1)
{
int breakpoint = 0;
}
if(i == parameters.FileNames.Length)
{
int breakpoint = 0;
}
fileTasks.Add(Task.Run(() => GetInMemoryFile(parameters.FileLocations[i], parameters.FileNames[i])));
}
InMemoryFile[] fileResults = await Task.WhenAll(fileTasks);
byte[] archiveFile = null;
using (MemoryStream archiveStream = new MemoryStream())
{
using (ZipArchive archive = new ZipArchive(archiveStream, ZipArchiveMode.Create, true))
{
foreach (InMemoryFile file in fileResults)
{
ZipArchiveEntry zipArchiveEntry = archive.CreateEntry(file.FileName, CompressionLevel.Optimal);
using (MemoryStream originalFileStream = new MemoryStream(file.Content))
using (Stream zipStream = zipArchiveEntry.Open())
{
originalFileStream.CopyTo(zipStream);
}
}
}
archiveFile = archiveStream.ToArray();
}
return File(archiveFile, "application/octet-stream");
}
Here is the method that does the actual downloading:
private async Task<InMemoryFile> GetInMemoryFile(string fileLocation, string fileName)
{
InMemoryFile file;
using (HttpClient client = new HttpClient())
using (HttpResponseMessage response = await client.GetAsync(fileLocation))
{
byte[] fileContent = await response.Content.ReadAsByteArrayAsync();
file = new InMemoryFile(fileName, fileContent);
}
return file;
}
Now the issues I run into is after I changed DownloadPartFiles to get all the files in parallel my for loop is now seeming to go past its stop condition. For example, if parameters.FileNames.Length returns 12 the for loop should not run when i = 12 and it should exit the loop. However, in my testing it will continue to run when i = 12 and as one might expect I run into an out of bounds error. I tried to set breakpoints in my code to make sure that it was actually running past the stop condition and more weird behavior arose. In my for loop I included two if statements with breakpoint variables to break on. It will always break when i should be on its last loop but will never break when i is one after its expected last loop. It seems to skip that breakpoint when i is one past the expected stop condition. It will run fine if I step through the code while debugging but will out of bounds error when I let it run normally.
I'm not sure why this is happening but I am still new to asynchronous programming so maybe its just an oversight somewhere. Let me know if I need to explain anything further.
I make a critical mistake in that I tried to wrap an asynchronous method (my GetInMemoryFile method) in the Task.Run() method which is used to wrap synchronous methods to make them run asynchronously. This caused the weird behavior.
So in short I changed
fileTasks.Add(Task.Run(() => GetInMemoryFile(parameters.FileLocations[i], parameters.FileNames[i])));
To
fileTasks.Add(GetInMemoryFile(parameters.FileLocations[i], parameters.FileNames[i]));

process.WaitForExit() is not waiting in Mac OS X - ASP.NET Core 2.2

I have an ASP.NET Core 2.2 application. When I call the below Test Method (from Chrome browser), based on the OS Type, a new Incognito instance of Chrome browser will be opened.
In Windows and Linux (running in Virtual Box), the process.WaitForExit() is waiting till the Incognito Chrome Browser is closed.
But, in Mac OS X (running in Virtual Box), the process.WaitForExit() is not waiting. It's immediately called after process.Start().
How to ensure the process is Waiting till the browser is closed?
public IActionResult Test()
{
string fileName = "";
string arguments = "";
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
fileName = #"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe";
arguments = #"-incognito --user-data-dir=""C:\Users\bala_sakthis\AppData\Local\Temp\ChromeData"" data:,";
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
fileName = "google-chrome";
arguments = #"-incognito --user-data-dir=""/home/osboxes/ChromeData"" data:,";
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
fileName = "open";
arguments = #"-a ""Google Chrome"" -n --args --incognito --user-data-dir=""/Users/bala/ChromeData"" data:,";
}
var process = new Process();
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = fileName;
process.StartInfo.Arguments = arguments;
using (process)
{
try
{
process.Start();
process.WaitForExit();
Console.WriteLine("Process exited...");
Console.WriteLine(process.HasExited);
}
catch (Exception exp)
{
Console.WriteLine(exp);
}
}
return new OkResult();
}

Unexpected Behavior with Parallel.For() - file not found

I am trying to use Parallel.For() to load n # of flat files into a database. Each iteration needs to create 2 config files in a folder and then run another process that uses the config files to know how to load the flat file into Oracle. Before I decided to use a parallel option, I tested the performance benefits by iterating 1000 times, each time creating the config files. I had better than a 2x increase in speed.
I then added the part where I call the other process, and I am getting periodic errors that some of the config files I am trying to create per iteration aren't there.
Here is my stripped-down code:
public static void FindFilesToLoad(int monitorId)
{
//left out code here that calls a stored procedure to get a list of ids
fileCount = idList.Count;
if (fileCount != 0)
{
Parallel.For(0, fileCount,
i =>
{
fileId = Convert.ToInt32(idList[i]);
//do the work here...
LoadFileIntoDatabase(monitorId, fileId);
});
}
}
public static void LoadFileIntoDatabase(int monitorId, int fileId)
{
stamp = Guid.NewGuid().ToString();
controlFile = CreateSqlLoaderControlFile(monitorId,stamp);
controlFileName = Path.GetFileName(controlFile);
parFile = CreateParFile(monitorId, controlFileName, stamp);
myCommand = #"CMD.EXE";
ProcessStartInfo startInfo = new ProcessStartInfo(myCommand)
{
WorkingDirectory = #"c:\temp\",
Arguments = #"/c SQLLDR CONTROL=" + controlFile + " PARFILE=" + parFile ,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false
};
Process process = new Process();
process.StartInfo = startInfo;
process.Start();
process.WaitForExit();
exitCode = process.ExitCode;
}
Error example:
SQLLoader-500: Unable to open file (6f46ccfd-986e-427e-ab63-b7ce2396488e.ctl)
SQLLoader-553: file not found
SQL*Loader-509: System error: The system cannot find the file specified

Win service to install another win service (programmatically in C#?)

I have a win service running under the system session. I want this main service to control (install/remove) another winservice to be installed under another user (we main service have the user/password).
How to do that?
What you are intending to do sounds a bit hackish, but if you really want to do it then you can by using the sc.exe utility that is bundled with Windows. All you have to do is make sure the correct service files are in place on the file system (for example under %PROGRAMFILES%\[CompanyName]\[ServiceName]), then use Process.Start to invoke sc.exe with the right command line arguments.
To specify the name and password for the account the service should run under, use the obj= <account name> and password= <password> options. Note the space between the option and its value - without that space the command will fail.
Another option is to use Process.Start() to invoke installutil.exe (which is part of the .Net framework). A quick example of this is:
var installutil = Environment.GetFolderPath(Environment.SpecialFolder.Windows)
+ "\\Microsoft.Net\\Framework\\v4.0.30319\\installutil.exe";
var arguments = string.Format( " /ServiceName=\"{0}\" /DisplayName=\"{1}\" \"{2}\" ",
serviceName,
displayName,
servicePath);
var psi = new ProcessStartInfo(installutil, arguments) {
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardInput = false,
RedirectStandardError = false,
ErrorDialog = false,
UseShellExecute = false
};
var p = new Process { StartInfo = psi, EnableRaisingEvents = true };
p.Start();
p.WaitForExit();
Seems like all I need in my main win service to call:
//Installs and starts the service
ServiceInstaller.InstallAndStart("MyServiceName", "MyServiceDisplayName", "C:\PathToServiceFile.exe");
And make sure inside the target (the one I want to control/install) Service code (ServiceInstaller) the right Installer:
public class MyServiceInstaller : Installer
{
private ServiceInstaller serviceInstaller;
private ServiceProcessInstaller processInstaller;
public MyServiceInstaller()
{
// instantiate installers for process and service
processInstaller = new ServiceProcessInstaller();
serviceInstaller = new ServiceInstaller();
// run under the system service account
processInstaller.Account = ServiceAccount.User;
processInstaller.Username = ".\\UserName";
processInstaller.Password = "password";
// service to start automatically.
serviceInstaller.StartType = ServiceStartMode.Automatic;
serviceInstaller.Description = "MyServiceDescription";
// name
string SvcName = "MyServiceNae";
// ServiceName must equal those on ServiceBase derived classes
serviceInstaller.ServiceName = SvcName;
// displayed in list
serviceInstaller.DisplayName = "My Service DisplayName ";
// add installers to collection
Installers.Add(serviceInstaller);
Installers.Add(processInstaller);
Committed += new InstallEventHandler((sender, args) =>
{
var controller = new System.ServiceProcess.ServiceController(SvcName);
controller.Start();
});
}
}
The installandstart:
public static void InstallAndStart(
string serviceName,
string displayName,
string fileName,
string username=null,
string password = null)
{
IntPtr scm = OpenSCManager(null, null, ScmAccessRights.AllAccess);
try
{
IntPtr service = OpenService(
scm,
serviceName,
ServiceAccessRights.AllAccess);
if (service == IntPtr.Zero)
{
service = CreateService(
scm,
serviceName,
displayName,
ServiceAccessRights.AllAccess,
SERVICE_WIN32_OWN_PROCESS,
ServiceBootFlag.AutoStart,
ServiceError.Normal,
fileName,
null,
IntPtr.Zero,
null,
username,
password);
if (service == IntPtr.Zero)
throw new ApplicationException("Failed to install service.");
try
{
StartService(service, 0, 0);
}
finally
{
CloseServiceHandle(service);
}
}
}
finally
{
CloseServiceHandle(scm);
}
}

How to get EnvDTE.Command from CommandBarButton in Visual Studio 2012

I'm trying to find all enabled resharper Commands via the menus. I can find all the menu items using the following code. However I cannot find how to get the actual command from the DTE commands collection.
var resharper = ((CommandBars)_dte.Application.CommandBars)["RESHARPER"];
var refactor = (CommandBarPopup)resharper.Controls["&Refactor"].Control;
foreach (var c in refactor.Controls)
{
var cbb = c as CommandBarButtonClass;
if (cbb != null)
{
yield return new VoiceCommand
{
Command = _dte.Commands.Item(???),
Key = cbb.accName,
};
}
}
What property should I use to look up the command (??? above)?
Thank you,
Erick
You want to use the CommandInfo method, like this:
Guid guid;
int id;
_dte.Commands.CommandInfo(cbb, out guid, out id);
yield return new VoiceCommand
{
Command = _dte.Commands.Item(guid, id),
Key = cbb.accName,
};

Resources