Friday, October 23, 2009

Mind Your Trailing Backslashes

When you are testing in the Visual C# IDE, and your test setup includes command line arguments that contain backshlashes, take caution.

The Problem

Take a fairly typical (for me) command argument string such as the following.

"C:\Documents and Settings\David\My Documents\_CLIENTS\EMCERT\"

Enter that into the Command Line Arguments text box on the Debug tab of the Project Properties page of a Console program project, put a breakpoint anywhere in its Main() function (in Program.cs). Press F5, make the Locals window visible, and display the first element of the Args array.

  [0] "C:\\Documents and Settings\\David\\My Documents\\Visual Studio 2005\\Projects\\wwBldNbrMgr\\wwBldNbrMgr\"" string

Do you see the extra quote at the end?

Now, press Shift-F5 to stop the the debugger, and change the Command Line Arguments string as shown below.

"C:\Documents and Settings\David\My Documents\Visual Studio 2005\Projects\wwBldNbrMgr\wwBldNbrMgr\ "

Not the space that I inserted between the backslash and the quote at the end of the string.

Repeat the debug session, and display the argument.

  [0] "C:\\Documents and Settings\\David\\My Documents\\Visual Studio 2005\\Projects\\wwBldNbrMgr\\wwBldNbrMgr\\ " string

I'll bet that is the string you expected to see the first time.

The Explanation

If you've never written and debugged console mode programs in C, using a tool such as Microsoft Visual C++, you might be scratching your head. So was I, for an embarrassingly long time.

After several hours of beating my head against the wall, I realized why the debugger was behaving in this seemingly bizarre way. Under the hood, the Microsoft .NET runtime must be using the scanf() function, or a derivative of it, to read the command line. Like its cousins, printf(), fprintf(), sprintf(), and others on the output side of the standard C library, scanf() interprets escape sequences, such as \", \r, \n, \t, and a host of others, in addition to the infamous printf() format strings.

The Solution

Once I understood what was happening under the hood, the solution is straightforward.

  1. If the last character in your Command Line Arguments string is a backslash, enter two of them.
  2. If the last two characters in your Command Line Arguments string are a backslash followed by a quotation mark, put a space between them.
  3. If you realize that your Command Line Arguments string contains an escape sequence, such as \", \r, \n, \t, take appropriate action to prevent it from being interpreted as an escape sequence. Usually, all that's needed is to double the backslash, so that \t becomes \t, for example.
  4. If the string you see in the debugger is missing a character or two that you entered into Command Line Arguments, check that part of the string for an escape sequence. Pay close attention to lower case characters that immediately follow a backslash, because all escape sequences involve lower case letters and numbers.

Escape sequences are covered in the help topics for the C# programming language in MSDN library, because you can use them in regular string literals. This topic closes with a handful of references, the first of which is to the definition of a regular string literal, as that term applies to the C# programming language.

References

Wednesday, August 12, 2009

64 Bit Integer Literals and Microsoft Visual C++ 6.0

In the course of porting an implementation of the SHA-x series of message digest algorithms (SHA-224, SHA-256, SHA-384, and SHA-512) from Unix C to Microsoft C, using the Visual C++ 6.0 compiler, I needed to convert several hard coded initialization vectors, each composed of 64 bit integer literals, to a format acceptable to the Visual C++ compiler. This was my first serious foray into 64 bit integer math, which is essential to the higher order SHA-x algorithms.

In the Unix C code, the literals look like this.

0xcbbb9d5dc1059ed8ULL

The Microsoft Visual C++ compiler needs something like this.

0xcbbb9d5dc1059ed8

Following is the shortest of the initialization vectors.

#if defined(_MSC_VER) defined(__BORLANDC__)
uint64 sha384_h0[8] =
{0xcbbb9d5dc1059ed8, 0x629a292a367cd507,
0x9159015a3070dd17, 0x152fecd8f70e5939,
0x67332667ffc00b31, 0x8eb44a8768581511,
0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4};
#else
uint64 sha384_h0[8] =
{0xcbbb9d5dc1059ed8ULL, 0x629a292a367cd507ULL,
0x9159015a3070dd17ULL, 0x152fecd8f70e5939ULL,
0x67332667ffc00b31ULL, 0x8eb44a8768581511ULL,
0xdb0c2e0d64f98fa7ULL, 0x47b5481dbefa4fa4ULL};
#endif

The second block, between #else and #endif, contains the original definition, which probably works fine on any C compiler that runs on Unix or any of its many variations, especially if the target CPU has a 64 bit word, but a half-dozen or so of these made Visual C++ hurl.

I suspected that I needed only to lose the ULL size suffixes, and almost tried the change without doing any research. Amazingly, the following query, fed into Google, promptly returned exactly one link, MatLab® 7: External Interfaces. When I saw the title, I felt certain that I had hit pay dirt.

"64 bit integer literal" "visual c++ 6.0"

Repeating the first of the two search tokens, "64 bit integer literal," within the returned "page," which is a massive Adobe PDF document, instantly took me to the following section, on page 186

Specifying Constant Literal Values

To assign signed and unsigned 64-bit integer literal values, use typedefinitions int64_T and uint64_T.

On UNIX systems, to assign a literal value to an integer variable where thevalue to be assigned is greater than 2 31-1 signed, you must suffix the valuewith LL. If the value is greater than 2 32-1 unsigned, then use LLU as thesuffix. These suffixes apply only to UNIX systems and are considered invalidon the Microsoft Windows systems.

Note The LL and LLU suffixes are not required for hardcoded (literal) valuesless than 2 G (2 31-1), even if they are assigned to a 64-bit int type.

The following example declares a 64-bit integer variable initialized with alarge literal int value, and two 64-bit integer variables:

void mexFunction(int nlhs, mxArray *plhs[], int nrhs,
const mxArray *prhs[])
#if defined(_MSC_VER) defined(__BORLANDC__) /* Windows */
int64_T large_offset_example = 9000222000;
#else /* UNIX */
int64_T large_offset_example = 9000222000LL;
#endif

int64_T offset = 0;
int64_T position = 0;

This was exactly the information that I needed. Application of the same technique to my source code quickly yielded code that compiles cleanly, without errors or warnings.

I've used hundreds of similar queries with Google and other search engines, followed by hours of wasted time reading irrelevant material. More often than not, they have yielded either nothing, or a hopelessly long list of mostly useless articles.

Persistence pays, and, sometimes, you really do get lucky.

Monday, August 10, 2009

File.WriteAllText Quirks

Background

Today, in the course of adding a couple of features to a new tool, which I created last week, for internal use, I started to put the code behind its Save button through its paces, only to discover that the code displayed the following error message.

Access to the path d:\Path\FileName is denied.

Since I had recently opened the file in my text editor (UltraEdit) which I know, for certain, acquires exclusive access to files that you open for editing, I restarted the computer.

Alas, the problem persisted.

Analysis

The only difference in the environment between last week, when I wrote and tested the initial version of the program, and today, is that its output file is now marked Read Only, System, and Hidden. Since a companion Visual Basic script reads the very same file, with the same three flags set on it, I didn't expect trouble from the C# program that I wrote last week.

After wasting a couple of hours reading articles returned by several Google searches, I concluded that I might be the first person to identify this problem and write about it.

After tinkering with the file attributes, I discovered that the only way to get the File.WriteAllText() method to work was to temporarily remove all three attributes, although I suspect it's actually the Hidden attribute that is the offender.

Fortunately, this task was made much easier by the fact that I already had a FileInfoExtension class, which extends the standard System.IO.FileInfo class by adding a set of simple methods for setting, clearing, and restoring the Read Only file attribute. Leveraging my latest general purpose programming editor, UltraEdit, and the Visual Studio editor, it took only a few minutes to extend the class to cover the System, and Hidden attributes. For good measure, I also extended it to cover the Archive attribute. I decided to leave the remaining attributes for another day, since I don't have an immediate need for them.

In the C# program, the change required just seven new lines of code, highlighted in blue.

 FileInfoExtension fix = new FileInfoExtension ( strFQFN );
 /*
  --------------------------------------------------------
  The File.WriteAllText() method cannot see files unless
  their Hidden attribute is removed.
  --------------------------------------------------------
 */
 fix.SystemClear ( );
 fix.HiddenClear ( );
 fix.ReadOnlyClear ( );
 File.WriteAllText (
  strFQFN ,
  strData ,
  Encoding.ASCII );
 fix.ReadOnlySet ( );
 fix.SystemSet ( );
 fix.HiddenSet ( );

The first step is to create a new FileInfoExtension object, passing the name of the file, strFQFN, to its constructor. Next, the three attributes are cleared. The Archive attribute is ignored, because the call to File.WriteAllText() sets it to True. Finally, the three attributes are reset to to True. Since this is a tiny data file, this happens so fast that no user is likely to see the brief change.

I'll save a discussion of the FileInfoExtension class for another day. I'll just say that, if your code also needs a System.IO.FileInfo object to go with the same file, the FileInfoExtension class exposes one as a property.

 

Wednesday, July 29, 2009

Amber Screens

Raise your hand if you remember when computer displays came in three colors: green, white, or amber text, always on a black background. Since the beginning, the Windows shortcut to a command prompt came configured to display text in either white or green, and always on a black background. The Colors tab allows you to choose foreground and background colors from a palette of a paltry 16 colors, which includes some decent colors, none of which is amber.

Among the three basic color schemes, I favored amber text on a black background from the first time I saw a an amber monitor. I liked it because the contrast was much better than green letters on a black background. Indeed, it might be surprising to some, but I think the contrast is at least as good as white on black, and, under certain circumstances, it is better.

Figure 1 - The Original Green Screen

Figure 1 - The Original Green Screen

Figure 2 - White Text on Black

Figure 2 - White Text on Black

In those days, there was considerable variation in the actual colors displayed; some of the green screens were brighter than the one shown in Figure 1, above, and some of the white phosphor screens were less bright than the one shown in Figure 2, above.

I promptly replaced my green screen with an amber one, which I used until I started buying VGA color screens to run Microsoft Windows 3.0, around 1990. The occupational health authorities in some European countries came to the same conclusion, and began requiring amber screens in work places, beginning in about 1983.

Beyond the 16 Color Palette

One day recently, having grown tired of yellow text on a blue background, I paid a visit to the Colors tab on the Command Prompt shortcut property sheet, and discovered something that I had scarcely noticed. In the upper right corner of the page, there is a group of three spinner controls, labeled Red, Green, and Blue (See Figure 3, below.). Their values change to reflect the selected color from the sixteen color tiles spread across the middle of the property sheet, and it didn't take long for me to realize that they are the RGB color codes that map to the selected color.

Figure 3 - Colors Dialog Box, showing settings for Amber text

Figure 3 - Colors Dialog Box, showing settings for Amber text

Using the preview controls just above the three push buttons, I began playing with the RGB color values. It didn't take long to see that I could replace any of the sixteen colors with the color of my choice, and assign it to any of the four color properties of a Command Prompt window. Hence, as is the case with the colors in the Display control panel applet, you have a choice of 16,581,375 colors!

Figure 4, below, illustrates my new amber on black Command Prompt window.

Figure 4 - My Amber on Black Command Prompt window

Figure 4 - My Amber on Black Command Prompt window

I Want to Start in My Documents

While I was at it, I decided to change the Startup directory, which is, by default, %HOMEDRIVE%%HOMEPATH%, and resolves to C:\Documents and Settings\David in my user profile. As shown in Figure 5, below, I replaced the default Start In string with %USERPROFILE%\My Documents. Since %USERPROFILE% resolves to C:\Documents and Settings\David, the whole string resolves to C:\Documents and Settings\David\My Documents. Since the path string may contain embedded spaces, it is enclosed in quotation marks.

Figure 5 - Startup Directory

Figure 5 - Generic Startup Directory, pointing to My Documents

Because all paths are specified as environment variable substitution tokens, and the remaining settings are at their default values, this shortcut works on any installation of Microsoft Windows.

http://www.wizardwrx.com/FREEBIES/AmberCommandPrompt.ZIP is a complete command prompt shortcut, ready for use. The Command Prompt shortcut, Command Prompt.lnk, in AmberCommandPrompt.ZIP reflects the settings shown in Figure 3, for colors, and Figure 5, for startup directory.

Long live the Amber Screen!

Friday, January 23, 2009

TrayProdder is for Programmers, too!

I just wrote about my latest software acquisition, TrayProdder, in aonther of my blogs, Wired Wisdom.  To ensure that the information reaches programmers who might follow this blog, but not that one, I decided to post a cross link here.