Monday, February 28, 2011

A problem with process pipe communication in C#

In C#, the Process component communicates with a child process via a pipe. If a child process writes enough data to the pipe to fill the buffer, the child will block until the parent reads the data from the pipe. This can cause deadlock if your application is reading all output to standard error and standard output, for example, using the following C# code.
              Process p = new Process("...", "...");
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
p.WaitForExit();
string output = p.StandardOutput.ReadToEnd();

In this instance, both the parent and the child processes would be blocked, as the filled pipe prevents the child process from completing, while the parent process is waiting indefinitely for the child process to exit. The pipe buffer, in my code, was 4096 bytes. If the output reaches this limit, it would hang.

This problem can be solved by moving the ReadToEnd() before the WaitForExit(), as follows..
              Process p = new Process("...", "...");
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();

References:
[1] http://bytes.com/topic/c-sharp/answers/459800-redirect-standard-output-size-limit
[2] http://www1.cs.columbia.edu/~lok/csharp/refdocs/System.Diagnostics/types/Process.html

Command line utilities

Many GUI functions have command line counterparts. This makes it possible to write scripts to automate a lot of tasks. The ones here are for the windows platform, specifically here we are talking about windows host script (*.vbs).

1) For SVN, slik svn is a command line version.

2) The sqlcmd command enables running SQL script from command line.

E.g., this runs eg.sql from command line.
sqlcmd -S localhost -d database_name -i eg.sql

This lists all databases on localhost:
sqlcmd -S localhost -i list_databases.sql

list_databases.sql:
select distinct db_name(database_id) AS DATABASE_NAME from sys.master_files group by database_id;
GO

So if you want to do something to certain databases in a server, you can use this to get a list of databases, and check which ones are you need, then construct sql command dynamically.

Just enter sqlcmd, you enter the interactive mode.

3) Build a visual studio solution from command line:

"c:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\devenv.com" c:\projects\Project1\Project1.sln /build Debug /out

Or:

C:\Windows\Microsoft.NET\Framework64\v3.5\MSBuild.exe c:\projects\Project1\Project1.sln

Script can invoke other executables. Executables (e.g. windows service) can invoke scripts. Both can also capture each other's output. This makes rich interaction.

Windows service uses socket in usual. It can use windows remoting (TcpClientChannel) as well.

The following installs a windows service:

C:\Projects\wsService1\wsService1\bin\Debug>InstallUtil /LogToConsole=true wsService1.exe

Adding the /u switch will uninstall it.

When install the windows service under a user account, you will be prompted for the account name and password. The name should be [domain_name]\account_name. If it is a local user (i.e., no domain_name), then it should be .\account_name. Not doing this causes failure.

4) The WMIC command.

The WMIC command is a powerful command line tool to get all kinds of system information, from running processes to OS related. See here for examples.

5) FileSystemObject.copyFolder and XCOPY

FileSystemObject.copyFolder fails when some target files are readonly, this is so even when the OverwriteExisting argument is set to true. The solution is to use XCOPY instead. You may want to use XCOPY this way:

start /WAIT /B XCOPY %1 %2 /R /Y /E /H /Q

/WAIT - wait until this finishes.
/B - do not open another dos window.
For the rest, use XCOPY /? for details. See here for more details.

Thursday, February 17, 2011

C# script to strip comment

This code extracts comment from a line of string. Feeding a stream of lines to it, it concatenates the comment and non-comment parts and return them. Comment is defined by "/**/" or "//" as in C/C++.

/// <summary>
/// Input: line.
/// Output:
/// rStr - The non-comment part is concatenated to rStr.
/// rCmt - The comment part is concatenated to rCmt.
/// </summary>
private void stripComment(string line, ref string rStr, ref string rCmt)
{
bool debug = false;
bool comment_block = false, comment_line = false;
int index, index2;
string NEWLINE = "\r\n";
string s = "";
string cmt = "";

if (line.Trim() == "") return; // ignore empty line.

string subline = line;
while (subline.Length > 0)
{
// if not in both comments mode:
// if find /*, start comment_block from the point on.
// elsif find --, start comment_line from here to end of line.
// Since both "/*" and "--" may exist on this line, find the one occur first.
if (comment_block == false)
{
index = subline.IndexOf("/*");
index2 = subline.IndexOf("--");
if (index != -1 && index2 != -1)
{
if (index < index2) { comment_block = true; }
else { comment_line = true; }
}
else if (index != -1) { comment_block = true; }
else if (index2 != -1) { comment_line = true; }

if (comment_block)
{
// comment_block start found.
// print the substring before "/*".
if (debug) Console.WriteLine(subline.Substring(0, index));
s += subline.Substring(0, index);
// strip the first part.
subline = subline.Substring(index);
continue;
}
else if (comment_line)
{
// comment_line found.
cmt += subline.Substring(index2) + NEWLINE; // comment part.
// print the substring before "--".
subline = subline.Substring(0, index2);
if (subline.Trim() != "")
{
if (debug) Console.WriteLine(subline);
s += subline + NEWLINE;
}
comment_line = false;
break;
}
else
{
// is a normal line.
if (subline.Length > 0)
{
if (debug) Console.WriteLine(subline);
s += subline + NEWLINE;
break;
}
}

}
else
{
// $comment_block == 1. In comment_block,
// search for */, if found, ends comment_block.
// Note that in comment_block mode, "--" has no effect.
index = subline.IndexOf("*/");
if (index != -1)
{
cmt += subline.Substring(0, index + 2); // comment part.
comment_block = false;
if (debug) Console.WriteLine("comment_block end found.");
subline = subline.Substring(index + 2);
continue;
}
else
{
// entire line is in comment_block.
cmt += subline; // comment part.
if (subline != line)
{
if (debug) Console.WriteLine(NEWLINE);
s += NEWLINE;
cmt += NEWLINE; // comment part.
}
break;
}
}

}

rStr += s;
rCmt += cmt;
}

Blog Archive

Followers