How to SAFELY invoke java keytool from C# code [closed] - c#

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed last year.
Improve this question
I want to create a GUI in C# that will be used to run keytool on cmd.exe behind the scenes to create a keystore, including a key, and certificate data.
Input data then requires
Keystore path
Password
Key alias
Key password
Validity
Certificate info (cn, ou, o, l, st and c)
Unfortunately people may type special characters in their passwords and also space is allowed in the certificate info.
Overall I am worried someone may input some information somewhere that can result in a disastrous command running behind the scenes once this is called (like rm -rf *).
Is there a way to pass a java properties file with the input information to keytool or is there any way that I can safely escape all the data that is passed as string parameters to keytool?
I could not find any type of file that keytool could take, even in separate steps, that would eliminate this issue.
here's the unsafe code (warning: IT'S UNSAFE!!):
using System;
using System.IO;
using System.Diagnostics;
using System.Collections.Generic;
using System.Text.RegularExpressions;
public class AndroidKeystoreCertificateData
{
public string FirstAndLastName;
public string OrganizationalUnit;
public string OrganizationName;
public string CityOrLocality;
public string StateOrProvince;
public string CountryCode;
}
public class AndroidKeystoreData : AndroidKeystoreCertificateData
{
public string KeystorePath;
public string Password;
public string KeyAlias;
public string KeyPassword;
public int ValidityInYears;
}
internal class AndroidUtils
{
private static bool RunCommand(string command, string working_dir, bool show_window = true)
{
using (Process proc = new Process
{
StartInfo =
{
UseShellExecute = false,
FileName = "cmd.exe",
Arguments = command,
CreateNoWindow = !show_window,
WorkingDirectory = working_dir
}
})
{
try
{
proc.Start();
proc.WaitForExit();
return true;
}
catch
{
return false;
}
}
return false;
}
private static string FilterString(string st)
{
return Regex.Replace(st, #"[^\w\d _]", "").Trim();
}
public static string GetKeystoreCertificateInputString(AndroidKeystoreCertificateData data)
{
string strCN = FilterString(data.FirstAndLastName);
string strOU = FilterString(data.OrganizationalUnit);
string strO = FilterString(data.OrganizationName);
string strL = FilterString(data.CityOrLocality);
string cnST = FilterString(data.StateOrProvince);
string cnC = FilterString(data.CountryCode);
string cert = "\"";
if (!string.IsNullOrEmpty(strCN)) cert += "cn=" + strCN + ", ";
if (!string.IsNullOrEmpty(strOU)) cert += "ou=" + strOU + ", ";
if (!string.IsNullOrEmpty(strO)) cert += "o=" + strO + ", ";
if (!string.IsNullOrEmpty(strL)) cert += "l=" + strL + ", ";
if (!string.IsNullOrEmpty(cnST)) cert += "st=" + cnST + ", ";
if (!string.IsNullOrEmpty(cnC)) cert += "c=" + cnC + "\"";
if (cert.Length > 2) return cert;
return string.Empty;
}
private static string GetKeytoolPath()
{
string javaHome = Environment.GetEnvironmentVariable("JAVA_HOME", EnvironmentVariableTarget.User);
return Path.Combine(javaHome, "bin\\keytool");
}
private static string GetKeystoreGenerationCommand(AndroidKeystoreData d)
{
string cert = GetKeystoreCertificateInputString(d);
string keytool = GetKeytoolPath();
string days = (d.ValidityInYears * 365).ToString();
string dname = "-dname \"cn=" + d.KeyAlias + "\"";
if (!string.IsNullOrEmpty(cert)) dname = "-dname " + cert;
string cmd = "echo y | " + keytool + " -genkeypair " + dname +
" -alias " + d.KeyAlias + " -keypass " + d.KeyPassword +
" -keystore " + d.KeystorePath + " -storepass " + d.Password + " -validity " + days;
return cmd;
}
public static bool RunGenerateKeystore(AndroidKeystoreData d)
{
string cmd = GetKeystoreGenerationCommand(d);
string wdir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
return RunCommand(cmd, wdir, false);
}
}
An example usage would be:
using System;
class MainClass
{
static void Main(string[] args)
{
AndroidKeystoreData d = new AndroidKeystoreData();
d.KeystorePath = "keystorepath";
d.Password = "pass";
d.KeyAlias = "key0";
d.KeyPassword = "pass";
d.ValidityInYears = 25*365;
d.FirstAndLastName = "self";
d.OrganizationalUnit = "my ou";
d.OrganizationName = "my o";
d.CityOrLocality = "my city";
d.StateOrProvince = "my state";
d.CountryCode = "cc";
AndroidUtils.RunGenerateKeystore(d);
}
}
repository | zip file
Additional information on things I tried:
I am in .NET 4.6.2, and I know about CommandLineBuilderExtension, but it's docs starts saying to not use it: This API supports the product infrastructure and is not intended to be used directly from your code.
Xamarin related codebase seems to rely on whatever commandlinebuilderextension does
Looking the CommandLineBuilder.cs source code, I can't tell how well it escapes (it includes a comment on code injection) and what a minimum version of it for only the purpose of the code above would look like.
For now I have a very restrictive regex going on but I don't know if this may be problematic: people with non A-Za-z0-9 characters in their names, if someone wants to use special characters in their passwords and so on. Ideally if there's a way to pass parameters safely through a file, I would prefer. Or alternatively, some way to generate an Android compatible keystore in pure C# without relying in Java keytool.
Bluntely stealing code from MSBuild above I managed to cut things out and come up with something like below, which seems like about right as minimum with a similar enough functionality to be useful.
using System;
using System.Text;
using System.Text.RegularExpressions;
namespace AndroidSignTool
{
public class CommandArgumentsBuilder
{
private StringBuilder Cmd { get; } = new StringBuilder();
private readonly Regex DefinitelyNeedQuotes = new Regex(#"^[a-z\\/:0-9\._\-+=]*$", RegexOptions.None);
private readonly Regex AllowedUnquoted = new Regex(#"[|><\s,;""]+", RegexOptions.IgnoreCase);
private bool IsQuotingRequired(string parameter)
{
bool isQuotingRequired = false;
if (parameter != null)
{
bool hasAllUnquotedCharacters = AllowedUnquoted.IsMatch(parameter);
bool hasSomeQuotedCharacters = DefinitelyNeedQuotes.IsMatch(parameter);
isQuotingRequired = !hasAllUnquotedCharacters;
isQuotingRequired = isQuotingRequired || hasSomeQuotedCharacters;
}
return isQuotingRequired;
}
private void AppendTextWithQuoting(string unquotedTextToAppend)
{
if (string.IsNullOrEmpty(unquotedTextToAppend))
return;
bool addQuotes = IsQuotingRequired(unquotedTextToAppend);
if (addQuotes)
{
Cmd.Append('"');
}
// Count the number of quotes
int literalQuotes = 0;
for (int i = 0; i < unquotedTextToAppend.Length; i++)
{
if (unquotedTextToAppend[i] == '"')
{
literalQuotes++;
}
}
if (literalQuotes > 0)
{
// Replace any \" sequences with \\"
unquotedTextToAppend = unquotedTextToAppend.Replace("\\\"", "\\\\\"");
// Now replace any " with \"
unquotedTextToAppend = unquotedTextToAppend.Replace("\"", "\\\"");
}
Cmd.Append(unquotedTextToAppend);
// Be careful any trailing slash doesn't escape the quote we're about to add
if (addQuotes && unquotedTextToAppend.EndsWith("\\", StringComparison.Ordinal))
{
Cmd.Append('\\');
}
if (addQuotes)
{
Cmd.Append('"');
}
}
public CommandArgumentsBuilder()
{
}
public void AppendSwitch(string switchName)
{
if (string.IsNullOrEmpty(switchName))
return;
if (Cmd.Length != 0 && Cmd[Cmd.Length - 1] != ' ')
{
Cmd.Append(' ');
}
Cmd.Append(switchName);
}
public void AppendSwitchIfNotNull(string switchName, string parameter)
{
if (string.IsNullOrEmpty(switchName) || string.IsNullOrEmpty(parameter))
return;
AppendSwitch(switchName);
AppendTextWithQuoting(parameter);
}
public override string ToString() => Cmd.ToString();
}
}
then the rewritten GetKeystoreGenerationCommand becomes this
public static string GetKeystoreGenerationCommand(AndroidKeystoreData d)
{
string cert = GetKeystoreCertificateInputString(d);
string keytool = "%JAVA_HOME%\\bin\\keytool" ;// GetKeytoolPath();
string days = (d.ValidityInYears * 365).ToString();
if (!string.IsNullOrEmpty(cert)) cert = d.KeyAlias;
var cmd = new CommandArgumentsBuilder();
cmd.AppendSwitch("echo y | " + keytool);
cmd.AppendSwitch("-genkeypair");
cmd.AppendSwitchIfNotNull("-dname", cert);
cmd.AppendSwitchIfNotNull("-alias", d.KeyAlias);
cmd.AppendSwitchIfNotNull("-keypass", d.KeyPassword);
cmd.AppendSwitchIfNotNull("-storepass", d.Password);
cmd.AppendSwitchIfNotNull("-keystore", d.KeystorePath);
cmd.AppendSwitchIfNotNull("-validity", days);
return cmd.ToString();
}

I believe that invoking the keytool binary directly instead of cmd.exe would do the trick if you don't want the user to inject shell commands.

Related

Getting a System.StackOverflowException' error

I am Getting this error An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll I know you are not supposed to have an infinite loop but its not an infinate loop because it just has too go till it gets a file number that has not been made yet. How can i go about this a better way?
private int x = 0;
public string clients = #"F:\Internal Jobs\Therm-Air Files\Program\P-1-2.0\Clients\";
public string tdate = DateTime.Today.ToString("MM-dd-yy");
public void saveloop()
{
string path = LoadPO.Text.Substring(0, LoadPO.Text.LastIndexOf("\\"));
string name = Path.GetFileName(path);
string t = Convert.ToString(x);
if (!File.Exists(path + #"\" + name + ".xlsx")) // This Line throws error
{
oSheet.SaveAs(path + #"\" + name + "-" + t + ".xlsx");
string prop = /* snipped for space reasons, just string concats */
string Combine = string.Empty;
int b = 0;
int c = cf.cPanel.Controls.Count;
string[] items = new string[c];
foreach (WProduct ewp in cf.cPanel.Controls)
{
string item = /* snipped for space reasons, just string concats */
items[b] = item;
b += 1;
}
Combine = prop + "^<";
foreach (var strings in items)
{
Combine += strings + "<";
}
File.WriteAllText(path + #"\" + name + ".txt", Combine);
}
else
{
x += 1;
saveloop();
}
The reason the code above is failing is because you do not use i in the name of the file so you can increment all you want it does not change the name.
You need to abstract the creation of the name of the file from the code that does the writing. Think of it as writing code in blocks of functionality.
public static string GetFileName(string path, string name)
{
var fileName = $#"{path}\{name}.xlsx";
int i = 0;
while (System.IO.File.Exists(fileName))
{
i++;
fileName = $#"{path}\{name}({i}).xlsx";
}
return fileName;
}
public void saveloop()
{
var fileName = GetFileName(path, name);
// use fileName from this point on
}

Serializing a manually written code

I am having a problem receiving files from the client. Someone suggested that I should use binary serialization to send and receive messages in stream. Can you give me ideas on how I should serialize this? I just learned about serialization not long ago so I am quite confused on how I should associate it with my program.
This is the client that 'should' be serialize
public void sendthedata()
{
if (!_timer.Enabled) // If timer is not running send data and start refresh interval
{
SendData();
_timer.Enabled = true;
}
else // Stop timer to prevent further refreshing
{
_timer.Enabled = false;
}
}
private List<int> listedProcesses = new List<int>();
private void SendData()
{
String processID = "";
String processName = "";
String processPath = "";
String processFileName = "";
String processMachinename = "";
listBox1.BeginUpdate();
try
{
piis = GetAllProcessInfos();
for (int i = 0; i < piis.Count; i++)
{
try
{
if (!listedProcesses.Contains(piis[i].Id)) //placed this on a list to avoid redundancy
{
listedProcesses.Add(piis[i].Id);
processID = piis[i].Id.ToString();
processName = piis[i].Name.ToString();
processPath = piis[i].Path.ToString();
processFileName = piis[i].FileName.ToString();
processMachinename = piis[i].Machinename.ToString();
output.Text += "\n\nSENT DATA : \n\t" + processFileName + "\n\t" + processMachinename + "\n\t" + processID + "\n\t" + processName + "\n\t" + processPath + "\n";
}
}
catch (Exception ex)
{
wait.Abort();
output.Text += "Error..... " + ex.StackTrace;
}
NetworkStream ns = tcpclnt.GetStream();
String data = "";
data = "--++" + processFileName + " " + processMachinename + " " + processID + " " + processPath;
if (ns.CanWrite)
{
byte[] bf = new ASCIIEncoding().GetBytes(data);
ns.Write(bf, 0, bf.Length);
ns.Flush();
}
}
}
finally
{
listBox1.EndUpdate();
}
}
And deserializing in the server
private void recieveData()
{
NetworkStream nStream = tcpClient.GetStream();
ASCIIEncoding ascii = null;
while (!stopRecieving)
{
if (nStream.CanRead)
{
byte[] buffer = new byte[1024];
nStream.Read(buffer, 0, buffer.Length);
ascii = new ASCIIEncoding();
recvDt = ascii.GetString(buffer);
/*Received message checks if it has +##+ then the ip is disconnected*/
bool f = false;
f = recvDt.Contains("+##+");
if (f)
{
string d = "+##+";
recvDt = recvDt.TrimStart(d.ToCharArray());
clientDis();
stopRecieving = true;
}
//else if (recvDt.Contains("^^"))
//{
// new Transmit_File().transfer_file(file, ipselected);
//}
/* ++-- shutsdown/restrt/logoff/abort*/
else if (recvDt.Contains("++--"))
{
string d = "++--";
recvDt = recvDt.TrimStart(d.ToCharArray());
this.Invoke(new rcvData(addToOutput));
clientDis();
}
/*--++ Normal msg*/
else if (recvDt.Contains("--++"))
{
string d = "--++";
recvDt = recvDt.TrimStart(d.ToCharArray());
this.Invoke(new rcvData(addToOutput));
}
}
Thread.Sleep(1000);
}
}
public void addToOutput()
{
if (recvDt != null && recvDt != "")
{
output.Text += "\n Received Data : " + recvDt;
recvDt = null;
}
}
Thank you.
There are a couple of rules to follow when serialising a piece of data.
It's easy to convert data to bytes, but consider how to reconstruct the data on the other side. Assume that the server can't have any knowledge on what you sended.
In your serialiser you just convert a couple of strings into a byte[] and send it over. Example:
string x = "abcdef";
string y = "ghijk";
var bytes = Encoding.Ascii.GetBytes(x + y);
the server receives: "abcdefghijk";
Is it possible for the server to determine and reconstruct strings x and y?
Since the server has no knowledge of the length of either x and y: no.
There are ways to solve this:
Use fixed length fields. In my example x should always be 6 chars and y should always be 5 chars in length. decoding on the server then becomes as trivial as
string x = data.Substring(0, 6)
string y = data.Substring(6, 5)
Use delimiters between the fields. If you are familiar with cvs, the ',' splits the fields. This however has it drawbacks, how to handle a ',' somewhere in a string? The data send over would be like "abcdef,ghijk"
Send the size of each field before the content of the field.
A naive approach just to clarify: string x would be send as '6abcdef' and y as '5ghijk'
Doing all this things by hand can get really hairy and is something that I would consider only if really needed.
I would resort to existing frameworks that do an excellent job on this subject:
Json.net
protobuf ported by Jon skeet
In this case I would first create a class to define the data send to the server instead of a bunch of strings:
class ProcessInfo{
public string ProcessID {get;set;}
public string ProcessName {get;set;}
public string ProcessPath {get;set;}
public string ProcessFileName {get;set;}
public string ProcessMachinename {get;set;}
};
the using Json to serialise this:
var procinfo = new ProcessInfo{
ProcessId = "1",
...
};
var serialised = JsonConvert.SerializeObject(procinfo);
var bytes = Encoding.Utf8.GetBytes(serialised);
ns.Write(bytes, 0, bytes.Length);
And restore it on the server just by:
var procInfo = JsonConvert.DeserializeObject<ProcessInfo>(json);

Unknown error - cmdlet Invoke-SCScript

So this is what I have (full add-in code):
using Microsoft.SystemCenter.VirtualMachineManager;
using Microsoft.SystemCenter.VirtualMachineManager.UIAddIns;
using Microsoft.SystemCenter.VirtualMachineManager.UIAddIns.ContextTypes;
using Microsoft.VirtualManager.Remoting;
using System;
using System.AddIn;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Forms;
namespace Microsoft.VirtualManager.UI.AddIns.BackupAddIn
{
[AddIn("Make Backup")]
public class BackupAddIn : ActionAddInBase
{
protected const string PowershellPath = "%WINDIR%\\System32\\WindowsPowershell\\v1.0\\powershell.exe";
protected const string DefaultDirectory = "C:\\ClusterStorage\\Volume2";
protected const string WildcardVMName = "{{VMName}}";
protected const string WildcardError = "{{Error}}";
protected const string BackupDirectoryBase = "{{Base}}";
protected const string BackupDirectoryName = "{{Name}}";
protected const string BackupDirectoryDate = "{{Date}}";
public enum JobState { Initialized, Success, Fail };
protected const string BackupDirectoryTemplate = BackupDirectoryBase + "\\" + BackupDirectoryName + "\\" + BackupDirectoryDate + "\\";
protected static readonly ReadOnlyCollection<string> AllowedBackupStates = new ReadOnlyCollection<string>(new string[] { "PowerOff", "Paused"/*, "Saved"*/});
public override bool CheckIfEnabledFor(IList<ContextObject> contextObjects)
{
if (contextObjects != null && contextObjects.Count > 0)
{
foreach (var host in contextObjects.OfType<HostContext>())
{
if (host.ComputerState != ComputerState.Responding)
{
return false;
}
}
return true;
}
return false;
}
public override void PerformAction(IList<ContextObject> contextObjects)
{
if (contextObjects != null)
{
// check if we have VMs selected
var VMs = contextObjects.OfType<VMContext>();
if (VMs != null && VMs.Count() > 0)
{
// check if VMs are in a good state
var badVMs = VMs.Where(vm => AllowedBackupStates.Contains(vm.Status.ToString()) == false).ToArray();
if (badVMs != null && badVMs.Length > 0)
{
MessageBox.Show("Backup not possible!\r\nThe following VMs are still running:\r\n\r\n" + string.Join(", ", badVMs.Select(vm => vm.Name)));
}
else
{
// ask for backup directory
string backupDir = Microsoft.VisualBasic.Interaction.InputBox("Enter a path on the host to export the selected virtual machine(s) to.", "Export path", DefaultDirectory);
if (string.IsNullOrEmpty(backupDir) == false)
{
if (backupDir.EndsWith("\\"))
{
backupDir = backupDir.Substring(0, backupDir.Length - 1);
}
// go
/*foreach (var vm in VMs)
{
exportVM(vm, backupDir);
}*/
// testing to export multiple vms in one invoke
exportVMs(VMs, backupDir);
}
}
}
}
}
public string getDate()
{
var date = DateTime.Now;
return date.Year.ToString()
+ (date.Month < 10 ? "0" : "") + date.Month.ToString()
+ (date.Day < 10 ? "0" : "") + date.Day.ToString()
+ "_"
+ (date.Hour < 10 ? "0" : "") + date.Hour.ToString()
+ (date.Minute < 10 ? "0" : "") + date.Minute.ToString();
}
public void ManageJob(string name, JobState state, string message = null)
{
string command;
if (state == JobState.Initialized)
{
command = string.Format("New-SCExternalJob -Name \"{0}\"", name);
}
else if (state == JobState.Success)
{
command = string.Format("Set-SCExternalJob -Job (Get-SCJob -Name \"{0}\")[0] -Complete -InfoMessage \"" + (string.IsNullOrEmpty(message) ? "Backup successfully started." : message.Replace("\"", "'")) + "\"", name);
}
else
{
command = string.Format("Set-SCExternalJob -Job (Get-SCJob -Name \"{0}\")[0] -Failed -InfoMessage \"" + (string.IsNullOrEmpty(message) ? "Backup FAILED." : message.Replace("\"", "'")) + "\"", name);
}
//MessageBox.Show(command);
PowerShellContext.ExecuteScript<Host>(
command,
(profiles, error) =>
{
if (error != null)
{
MessageBox.Show("Cannot modify job state\r\nError: " + error.Problem);
}
}
);
}
public void exportVMs(IEnumerable<VMContext> VMs, string backupDir)
{
string date = getDate();
string VMS = "";
string fullBackupDirS = BackupDirectoryTemplate.Replace(BackupDirectoryBase, backupDir).Replace(BackupDirectoryName, "_VMBackups").Replace(BackupDirectoryDate, date);
VMS = "'" + string.Join("', '", VMs.Select(vm => vm.Name).ToArray()) + "'";
string command = string.Format("Export-VM -Name {0} -Path '{1}'", VMS, fullBackupDirS);
MessageBox.Show(command);
// We need to manager jobs in another thread probably --------------------------------------------------------------!!!
string jobname = "Starting_backup_of_multiple_machines";
mkShortcuts(backupDir, date, VMs.Select(vm => vm.Name).ToArray(), VMs.First());
//! execPSScript(jobname, scvmmPsCommand(command, VMs.First()), VMs.First(), WildcardVMName + ": Backup successful.", WildcardVMName + ": Backup FAILED!\r\nError: " + WildcardError, backupDir, date, VMs.Select(vm => vm.Name).ToArray());
}
public String scvmmPsCommand(string command, VMContext vm, string appPath = PowershellPath)
{
return string.Format("Invoke-SCScriptCommand -Executable {0} -VMHost (Get-SCVMHost -ID \"{1}\") -CommandParameters \"{2}\" -RunAsynchronous -TimeoutSeconds 360000", appPath, vm.VMHostId.ToString(), command);
}
// Make a shortcut from the machines backup directory to the backup in the "_VMBackups"-folder
public void mkShortcuts(string path, string date, string[] names, VMContext vm)
{
string command = "$shell = New-Object -ComObject WScript.Shell;";
foreach (var n in names)
{
command = command + string.Format(" $shortc = $shell.CreateShortcut('{0}\\{1}\\{2}.lnk'); $shortc.TargetPath = '{0}\\_VMBackup\\{2}\\{1}'; $shortc.Save();", path, n, date);
}
string fullCommand = scvmmPsCommand(command, vm);
MessageBox.Show(fullCommand);
execPSScript("Create_ShortcutS", fullCommand, vm, "Shortcut(s) created.", "FAILED to create Shortcut(s)!");
}
public void execPSScript(string jobname, string command, VMContext vm, string successMessage, string errorMessage, string path = "", string date = "", string[] names = null)
{
ManageJob(jobname, JobState.Initialized);
PowerShellContext.ExecuteScript<Host>(
command,
(vms, error) =>
{
if (error != null)
{
ManageJob(jobname, JobState.Fail, errorMessage.Replace(WildcardVMName, vm.Name).Replace(WildcardError, error.Problem));
}
else
{
ManageJob(jobname, JobState.Success, successMessage.Replace(WildcardVMName, vm.Name));
if (string.IsNullOrEmpty(path) == false)
{
//mkShortcuts(path, date, names, vm);
}
}
}
);
}
}
}
When I run the plugin I get an error Box that says sth like: "Unknown script-error. Expression not closed - ")" missing.
+ ... andard'; .Save();
+ ~
An expression was expected after '('.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordEx
ception
+ FullyQualifiedErrorId : ExpectedExpression". Weitere Informationen finden Sie im Standardfehlerprotokoll "C:\Windows\TEMP\gce_stderrord07b04547c74493caa6bdba9087df444.log".
But I can't find the error in my code since it worked when typed manually into the powershell on the host and .Save() takes no arguments. Do you have any idea?
OK, Instead of
command2 = command2 + string.Format(" ${1} = $shell.CreateShortcut(\"C:\\ClusterStorage\\Volume2\\test.lnk\"); ${1}.TargetPath = \"{0}\\_VMBackup\\{2}\\{1}\"; ${1}.Save();", backupDir, vm.Name, date);
Try
command2 = command2 + string.Format(" $shortc = $shell.CreateShortcut(\"C:\\ClusterStorage\\Volume2\\test.lnk\"); $shortc.TargetPath = \"{0}\\_VMBackup\\{2}\\{1}\"; $shortc.Save();", backupDir, vm.Name, date);
command2 = "'" + command2 + "'"
This did not work and neither did a lot of other things.
Basically, the problem is the way the command string is passed to PowerShell to execute.
PowerShell will examine the sting and attempt to enumerate all variables. This is not the desired behaviour. Tried to enclose the entire string in single quotes, however this did not help.
In the end the solution was to use the PowerShell escape character “`” to mask all variable names.
command2 = command2 + string.Format(" `$shortc = `$shell.CreateShortcut(\"C:\\ClusterStorage\\Volume2\\test.lnk\"); `$shortc.TargetPath = \"{0}\\_VMBackup\\{2}\\{1}\"; `$shortc.Save();", backupDir, vm.Name, date);

Check for System DSN and create System DSN if NOT Existing (iSeries Access ODBC Driver)

can someone please help me with this?
i need to check through the System DSN for my ODBC connection to the AS400 servier and create a System DSN if a particular one does not exist.
i've tried googling and have not been able to find anything good for me.
btw, i am quite new to programming. any help will be much appreciated.
thank you
After going through the few less complicated examples available online, this is what i managed to come up with (and it works fine for me).
using System;
using System.Runtime.InteropServices;
public class ODBC_Manager
{
[DllImport("ODBCCP32.dll")]
public static extern bool SQLConfigDataSource(IntPtr parent, int request, string driver, string attributes);
[DllImport("ODBCCP32.dll")]
public static extern int SQLGetPrivateProfileString(string lpszSection, string lpszEntry, string lpszDefault, string #RetBuffer, int cbRetBuffer, string lpszFilename);
private const short ODBC_ADD_DSN = 1;
private const short ODBC_CONFIG_DSN = 2;
private const short ODBC_REMOVE_DSN = 3;
private const short ODBC_ADD_SYS_DSN = 4;
private const short ODBC_CONFIG_SYS_DSN = 5;
private const short ODBC_REMOVE_SYS_DSN = 6;
private const int vbAPINull = 0;
public void CreateDSN(string strDSNName)
{
string strDriver;
string strAttributes;
try
{
string strDSN = "";
string _server = //ip address of the server
string _user = //username
string _pass = //password
string _description = //not required. give a description if you want to
strDriver = "iSeries Access ODBC Driver";
strAttributes = "DSN=" + strDSNName + "\0";
strAttributes += "SYSTEM=" + _server + "\0";
strAttributes += "UID=" + _user + "\0";
strAttributes += "PWD=" + _pass + "\0";
strDSN = strDSN + "System = " + _server + "\n";
strDSN = strDSN + "Description = " + _description + "\n";
if (SQLConfigDataSource((IntPtr)vbAPINull, ODBC_ADD_SYS_DSN, strDriver, strAttributes))
{
Console.WriteLine("DSN was created successfully");
}
else
{
Console.WriteLine("DSN creation failed...");
}
}
catch (Exception ex)
{
if (ex.InnerException != null)
{
Console.WriteLine(ex.InnerException.ToString());
}
else
{
Console.WriteLine(ex.Message.ToString());
}
}
}
public int CheckForDSN(string strDSNName)
{
int iData;
string strRetBuff = "";
iData = SQLGetPrivateProfileString("ODBC Data Sources", strDSNName, "", strRetBuff, 200, "odbc.ini");
return iData;
}
}
... and then call the methods from your application.
static void Main(string[] args)
{
ODBC_Manager odbc = new ODBC_Manager();
string dsnName = //Name of the DSN connection here
if (odbc.CheckForDSN(dsnName) > 0)
{
Console.WriteLine("\n\nODBC Connection " + dsnName + " already exists on the system");
}
else
{
Console.WriteLine("\n\nODBC Connection " + dsnName + " does not exist on the system");
Console.WriteLine("\n\nPress 'Y' to create the connection?");
string cont = Console.ReadLine();
if (cont == "Y" || cont == "y")
{
odbc.CreateDSN(dsnName);
Environment.Exit(1);
}
else
{
Environment.Exit(1);
}
}
}

How to keep logs in C#?

This is a WinForm written in C#.
Lets say I'm generating a random named text file in my selected directory. When the button is clicked teh first time, i write the data contained in the textboxes into that text file. If the user wants to do the same thing with different data in the textboxes then the click on the button should write the new data into the text file without losing the old data. It's like keeping logs, is this possible?
My code is like:
private readonly Random setere = new Random();
private const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private string RandomString()
{
char[] buffer = new char[5];
for (int i = 0; i < 5; i++)
{
buffer[i] = chars[setere.Next(chars.Length)];
}
return new string(buffer);
}
private void button1_Click(object sender, EventArgs e)
{
DialogResult dia = MessageBox.Show("Wanna continue?", "Question", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (dia == DialogResult.Yes)
{
StreamWriter wFile = new StreamWriter("C:\\Users\\Ece\\Documents\\Testings\\" + RandomString() + ".txt");
wFile.WriteLine("Name Surname:" + text1.Text + text2.Text);
wFile.WriteLine("Other:" + text3.Text + text4.Text);
wFile.WriteLine("Money:" + textBox1.Text + " TL.");
wFile.WriteLine("*************************************");
wFile.Close();
}
else
{
return;
}
}
You can append to the text in the file.
See
File.AppendText
using (StreamWriter sw = File.AppendText(pathofFile))
{
sw.WriteLine("This");
sw.WriteLine("is Extra");
sw.WriteLine("Text");
}
where pathofFile is the path to the file to append to.
Have a look at using something like this:
StreamWriter fw = new StreamWriter(#"C:\Logs\MyFile.txt",true);
fw.WriteLine("Some Message" + Environment.Newline);
fw.Flush();
fw.Close();
Hope that helps. See MSDN StreamWriter for more information
Updated: Removed old example
Also if you are trying to create a unique file you can use Path.GetRandomFileName()
Again from the MSDN Books:
The GetRandomFileName method returns a
cryptographically strong, random
string that can be used as either a
folder name or a file name.
UPDATED: Added a Logger class example below
Add a new class to your project and add the following lines (this is 3.0 type syntax so you may have to adjust if creating a 2.0 version)
using System;
using System.IO;
namespace LogProvider
{
//
// Example Logger Class
//
public class Logging
{
public static string LogDir { get; set; }
public static string LogFile { get; set; }
private static readonly Random setere = new Random();
private const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
public Logging() {
LogDir = null;
LogFile = null;
}
public static string RandomFileName()
{
char[] buffer = new char[5];
for (int i = 0; i < 5; i++)
{
buffer[i] = chars[setere.Next(chars.Length)];
}
return new string(buffer);
}
public static void AddLog(String msg)
{
String tstamp = Convert.ToString(DateTime.Now.Day) + "/" +
Convert.ToString(DateTime.Now.Month) + "/" +
Convert.ToString(DateTime.Now.Year) + " " +
Convert.ToString(DateTime.Now.Hour) + ":" +
Convert.ToString(DateTime.Now.Minute) + ":" +
Convert.ToString(DateTime.Now.Second);
if(LogDir == null || LogFile == null)
{
throw new ArgumentException("Null arguments supplied");
}
String logFile = LogDir + "\\" + LogFile;
String rmsg = tstamp + "," + msg;
StreamWriter sw = new StreamWriter(logFile, true);
sw.WriteLine(rmsg);
sw.Flush();
sw.Close();
}
}
}
Add this to your forms onload event
LogProvider.Logging.LogDir = "C:\\Users\\Ece\\Documents\\Testings";
LogProvider.Logging.LogFile = LogProvider.Logging.RandomFileName();
Now adjust your button click event to be like the following:
DialogResult dia = MessageBox.Show("Wanna continue?", "Question", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (dia == DialogResult.Yes)
{
StringBuilder logMsg = new StringBuilder();
logMsg.Append("Name Surname:" + text1.Text + text2.Text + Environment.NewLine);
logMsg.Append("Other:" + text3.Text + text4.Text + Environment.NewLine);
logMsg.Append("Money:" + textBox1.Text + " TL." + Environment.NewLine);
logMsg.Append("*************************************" + Environment.NewLine);
LogProvider.Logging.AddLog(logMsg.ToString());
} else
{
return;
}
Now you should only create one file for the entire time that application is running and will log to that one file every time you click your button.
You might want to take a look at log4net and the RollingFileAppender
Sure. Just open the file for appending with something like System.IO.File.AppendText

Categories