Translate

Friday, January 25, 2013

Regular Expression Tester

I "regularly"  (nyuk nyuk!) use regular expressions in my .Net code.  However, I find that I spend no small amount of time designing them, troubleshooting why they aren't working and trying to work out the escape characters correctly.  So much so that I decided to write a Regex testing application.

Regex Tester (zipped .exe)

C# Source Code

This tool allows me to rapidly test regular expressions against sample text with or without options.


It shows me matches, splits and replaced text.

If the Regex phrase is invalid, it will display the error text:



When I'm satisfied with the phrase and options, I click Copy Regex Initializer to copy it to the clipboard:


I can paste the copied text directly into my code after declaring the Regex object:

I hope you find it useful.


Wednesday, June 29, 2011

Win 2008 Terminal Server Network App Crashing

How many Microsoft Developers does it take to change a lightbulb? None. They just make darkness the new standard.

You can no longer safely run applications from a network location in Windows 2008 & R2 terminal server. When a user opens a program across the network and logs off, it will crash the program for all other users running it on the same server.  It appears that Microsoft is no longer supporting functionality that has been present for 20+ years and is not going to fix the bug due to "Architectural Changes" in Windows 2008.

http://support.microsoft.com/kb/2536487

Here is a summary of the issue:
  1. When a user opens any network application in a 2008 or R2 Terminal Server environment, the OS creates a File Control Block (FCB). The FCB is a handle that the OS uses to access the program file loaded into memory.

  2. If another user on the terminal server opens the same program, the OS will give the second user access to the first user’s FCB and access to that part of the original user’s memory space that stores the executable.

  3. When the first user logs off, all their FCB’s are dropped and become inaccessible to other users that were sharing them.

  4. The next action the remaining users perform in the program fails and it crashes the application because it cannot access the program files.

You can see this reported in the Application event log on two entries with the same time stamp:
  • Application Error, Event ID 1000 – “Faulting application xxxxx.exe, version xxxxx…”

  • Application Error, Event ID 1005 – “Windows cannot access the file for one of the following reasons: there is a problem with the network connection, the disk that the file is stored on, or the storage drivers installed on this computer; or the disk is missing. Windows closed the program XXXXX because of this error.”
Microsoft says the "New Standard" is to load applications locally or use a WebDAV share. Local install works ok in one or two-server environments, not so well in 400+ server environments. Applying program updates to multiple servers is an administrative nightmare and a waste of resources as your environment grows. And WebDAV? Really?

We've gotten past Microsoft's Offshore Support Defense Forces and we're finally in discussions with high-level US engineers at Microsoft. We're attempting to convince them of the gravity of the issue and to resolve it. The engineers have acknowledged that they have received multiple reports of the issue but that MS development is refusing to fix it.

They said that the bug is coded deep in the 2008 OS and would require architectural redesign, so they are reluctant to make any changes. It looks like they tried to fix it in 2008 R2, because it now shares the FCB of the last user who opened the program file instead of the first, but it still crashes when they log off before another user grabs the ball.

It seems like they forked in the wrong direction and locked themselves in a faulty design.

Update - 2012.05.01 - Multiple responders have reported that accessing the network files over a DFS share eliminates the FCB errors.  See comments below.

Update - 2011.09.23 - We have had some success running the programs from a UNC path instead of using the mapped network drive.  This is still in testing, but I believe you may also need to remove the mapped drive to eliminate the application crashes. If this works in your environment, please post back in the comments.

Monday, February 1, 2010

Connect to a UNC path with security credentials

I had an ASP.NET site where I wanted to access network resources, but did not have sufficient share permissions because the code ran under the ASP user. I also had a service that copied files every night between file shares in two different domains. I wanted a way to access remote resources without opening up security holes by changing permissions or running as a privileged user. Surprisingly, there is a lack of .Net managed code that will allow you to do this.

I have seen many coders who get around this problem via LogonUser() - programmatically log onto the box as a user who has the necessary privileges and perform the required task. This works fine if you grant the user local logon rights on the box. I'm not comfortable doing that on a public-facing box. I have also encountered code that shells out and performs a net use command. Tsk. So uncivilized. :)

The NetUseAdd() and NetUseDel() commands of NetApi32.dll provide a way to add access to a network resource with network credentials. This will allow you to run the process from whatever user you wish, but still access protected network resources.

The UNCAccessWithCredentials.cs class wraps these functions in an IDisposable class that allows you to encompass your code in a using block. When you encounter the end of the block, the class disconnects from the resource and the connection is closed. If you need a persistent connection, call the NetUseWithCredentials function, but remember to clean up your mess with the NetUseDelete function.

Example usage:

 
using (UNCAccessWithCredentials unc = new UNCAccessWithCredentials())
{
  if (unc.NetUseWithCredentials(UNCPath, UserName, Domain, Password))
  {
      //Code that accesses the remote resource
  }
  else
  {
      MessageBox.Show("Failed to connect to " + UNCPath + "\r\nLastError = " + unc.LastError.ToString(),
                      "Failed to connect",
                      MessageBoxButtons.OK,
                      MessageBoxIcon.Error);
  }

}
 
UNCAccessWithCredentials.cs:
 
using System;
using System.Runtime.InteropServices;
using BOOL = System.Boolean;
using DWORD = System.UInt32;
using LPWSTR = System.String;
using NET_API_STATUS = System.UInt32;

namespace ConnectUNCWithCredentials
{
 public class UNCAccessWithCredentials : IDisposable
 {
     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
     internal struct USE_INFO_2
     {
         internal LPWSTR ui2_local;
         internal LPWSTR ui2_remote;
         internal LPWSTR ui2_password;
         internal DWORD ui2_status;
         internal DWORD ui2_asg_type;
         internal DWORD ui2_refcount;
         internal DWORD ui2_usecount;
         internal LPWSTR ui2_username;
         internal LPWSTR ui2_domainname;
     }

     [DllImport("NetApi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
     internal static extern NET_API_STATUS NetUseAdd(
         LPWSTR UncServerName,
         DWORD Level,
         ref USE_INFO_2 Buf,
         out DWORD ParmError);

     [DllImport("NetApi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
     internal static extern NET_API_STATUS NetUseDel(
         LPWSTR UncServerName,
         LPWSTR UseName,
         DWORD ForceCond);

     private bool disposed = false;

     private string sUNCPath;
     private string sUser;
     private string sPassword;
     private string sDomain;
     private int iLastError;

     /// <summary>
     /// A disposeable class that allows access to a UNC resource with credentials.
     /// </summary>
     public UNCAccessWithCredentials()
     {
     }

     /// <summary>
     /// The last system error code returned from NetUseAdd or NetUseDel.  Success = 0
     /// </summary>
     public int LastError
     {
         get { return iLastError; }
     }

     public void Dispose()
     {
         if (!this.disposed)
         {
             NetUseDelete();
         }
         disposed = true;
         GC.SuppressFinalize(this);
     }

     /// <summary>
     /// Connects to a UNC path using the credentials supplied.
     /// </summary>
     /// <param name="UNCPath">Fully qualified domain name UNC path</param>
     /// <param name="User">A user with sufficient rights to access the path.</param>
     /// <param name="Domain">Domain of User.</param>
     /// <param name="Password">Password of User</param>
     /// <returns>True if mapping succeeds.  Use LastError to get the system error code.</returns>
     public bool NetUseWithCredentials(string UNCPath, string User, string Domain, string Password)
     {
         sUNCPath = UNCPath;
         sUser = User;
         sPassword = Password;
         sDomain = Domain;
         return NetUseWithCredentials();
     }

     private bool NetUseWithCredentials()
     {
         uint returncode;
         try
         {
             USE_INFO_2 useinfo = new USE_INFO_2();

             useinfo.ui2_remote = sUNCPath;
             useinfo.ui2_username = sUser;
             useinfo.ui2_domainname = sDomain;
             useinfo.ui2_password = sPassword;
             useinfo.ui2_asg_type = 0;
             useinfo.ui2_usecount = 1;
             uint paramErrorIndex;
             returncode = NetUseAdd(null, 2, ref useinfo, out paramErrorIndex);
             iLastError = (int)returncode;
             return returncode == 0;
         }
         catch
         {
             iLastError = Marshal.GetLastWin32Error();
             return false;
         }
     }

     /// <summary>
     /// Ends the connection to the remote resource 
     /// </summary>
     /// <returns>True if it succeeds.  Use LastError to get the system error code</returns>
     public bool NetUseDelete()
     {
         uint returncode;
         try
         {
             returncode = NetUseDel(null, sUNCPath, 2);
             iLastError = (int)returncode;
             return (returncode == 0);
         }
         catch
         {
             iLastError = Marshal.GetLastWin32Error();
             return false;
         }
     }

     ~UNCAccessWithCredentials()
     {
         Dispose();
     }

 }
}
 

Saturday, September 6, 2008

Change Height of TextBoxes Using Font Size

Download sample code

I had a c# project that required a single-line text box with adjustable height. I found many examples of how to adjust the width, but nothing on how to dynamically change the height of a text box. I did not want to use multi-line because I wanted to use the auto-complete features of the single-line text box.

Single-line textbox height is set by the size of the font, not the TextBox.Height property. This makes it difficult if you are looking for an exact height. Luckily, the font property uses a float for the font size (emSize). You can use fractions of fonts to fine-tune the textbox height.

The calculation the textbox uses to determine its height is:

Height
= ( Font Size * Font Line Spacing / Font Em Height ) + 7
  • Font Size - It is easiest to measure font in pixels so you do not have to factor in screen dpi.
  • Font Line Spacing - The distance, in design units, between two consecutive lines of text.
  • Font Em Height - height, in design units of the font's widest letter - typically the letter M.
Text boxes have a 3-pixel lower and 4-pixel upper white space around the font height. Therefore, the calculation adjusts the height by 7 pixels.

We can reverse this calculation to obtain the font size needed for a desired height:

Font Size
= ( height - 7 ) * Font Em Height / Font Line Spacing

This method will return a font object that will set the size of your text box:



private Font GetFontForTextBoxHeight(int TextBoxHeight)
{
// What is the target size of the text box?
float desiredheight = (float)TextBoxHeight;

// Set the font from the existing TextBox font.
// We use the fnt = new Font(...) method so we can ensure that
// we're setting the GraphicsUnit to Pixels. This avoids all
// the DPI conversions between point & pixel.
Font fnt = new Font(textBox1.Font.FontFamily,
textBox1.Font.Size,
textBox1.Font.Style,
GraphicsUnit.Pixel);

// TextBoxes never size below 8 pixels. This consists of the
// 4 pixels above & 3 below of whitespace, and 1 pixel line of
// greeked text.
if (desiredheight < 8)
desiredheight = 8;

// Determine the Em sizes of the font and font line spacing
// These values are constant for each font at the given font style.
// and screen DPI.
float FontEmSize = fnt.FontFamily.GetEmHeight(fnt.Style);
float FontLineSpacing = fnt.FontFamily.GetLineSpacing(fnt.Style);

// emSize is the target font size. TextBoxes have a total of
// 7 pixels above and below the FontHeight of the font.
float emSize = (desiredheight - 7) * FontEmSize / FontLineSpacing;

// Create the font, with the proper size to change the TextBox Height to the desired size.
fnt = new Font(fnt.FontFamily, emSize, fnt.Style, GraphicsUnit.Pixel);

return fnt;
}

Whenever you have to set the textbox size, set the font property using the above method:

TextBox.Font = GetFontForTextBoxHeight(int TextBoxHeight)

Monday, April 7, 2008

Good Hex Editor

Every once in a while, I need a hex editor to poke at a file. There are a lot of editors out there, and a lot of chaff among them. I have a few requirements for what constitutes a good hex editor:
  • Free - I don't use hex editors enough to justify a $40 editor.
  • GUI
  • Unicode capabilities
  • byte-level editing
  • Cursor tracking between hex and text frames
  • hex and ASCII/Unicode text searches
  • File comparison - what has changed?
I found one that covers most of these requirements, plus a few more neato features. If you haven't checked out XVI32, give it a try. I particularly like the wildcard search and replace function.

The only thing I would like to see is a file comparison feature. It helps in hacking another program: save a copy of some program's data, then make a change through the program and compare with the reference file. There may be a way to do this through the scripting interface (another neat feature), but it would be nice to see that in the application.

Monday, March 24, 2008

Encheferizer

This is a VB.Net function that you can use to translate your message to Swedish Chef. It's great for error reporting! Paste this function into your VB.Net class. To use it, simply pass the text you want translated and it will return a string of "translated" text.


Sample Translation:
Zeees is a FB.Net fooncshun zeet yuoo cun use tu trunslete yuoor messege tu Svedeesh Cheff. It's greet fur irrur repurteeng! Peste zeees fooncshun intu yuoor FB.Net cless. Tu use it, seemply pess zee text yuoo vunt trunsleted und it veell retoorn a streeng ooff "trunsleted" text, Bork Bork Bork!






Here is a sample application that uses the Encheferizer code. Enjoy!
Encheferizer.zip



Public
Class Encheferizer
'Encheferize
'Adrian Hayes
' http://www.bearnakedcode.com
' You are free to use this code as you wish. It is offered without
' warranty. Use at your own risk - especially if you have a touchy
' manager who would not appreciate funny error reports or easter eggs.
'

' If you publish its source (or any portion thereof), please include
' a reference back to http://www.bearnakedcode.com.
' Translate English text to "Swedish Chef" dialect
' Based on PHP Encheferize from Eric Bakker - http://bork.eamelink.nl
' and original chef.x from John Hagerman
'
'Translation guide:
'an -> un -f -> ff th| -> t
'au -> oo -ir -> ur -tion -> shun
'a- -> e -I -> ee or I -u -> oo
'en -> ee -ow -> oo v -> f
'-ew -> oo |o -> oo w -> v
'
'"|" indicates a word boundary
'"-" indicates in the middle of a word

Public Function Encheferize(ByVal text As String) As String
Dim retval As String = "" ' return value
'separators is a character array used for searching the phrase for
?separating characters
to find word boundaries
Dim separators() As Char = {vbCr, vbLf, " ", ",", ".", ";", ":", "<", ">",
"""", "''", "[", "{", "]", "}", "|", "=", "+",
"-", "_", "!", "@", "#", "$", "%", "^", "&",
"*", "(", ")", "~"}
Dim word As String = ""
Dim rand As New Random

Dim c As Char
For i As Long = 0 To text.Length
c
= ""
If i < text.Length Then
c
= text.Substring(i, 1) ' set c to the i'th character in the text
End If
If Char.IsLetter(c) Then
word
+= c
Continue
For
Else
If Array.IndexOf(separators, c) >= 0 Or i = text.Length Then
' word found - send for "translation"
If word.Length > 0 Then
retval
+= EncheferizeWord(word)
word
= ""
End If
'"25% chance of ending a sentance with "Bork Bork Bork!"
If (c = "." Or c = "!") And rand.Next(0, 4) = 1 Then
retval
+= ", Bork Bork Bork!"
Else
retval
+= c
End If
Else
word
+= c
End If
End If
Next
Return retval
End Function

Public Function EncheferizeWord(ByVal word As String) As String
Dim retval As String = "" 'return value
Dim i As Integer 'tracks character position within word
Dim c As Char 'character currently under evaluation
Dim nc As Cha 'next character - if empty, then c is the
'last character in the word.
Dim biseen As Boolean = False 'used to insure we don't replace
'"i" with "ee" more than once in a word.

If word.ToLower = "bork" Then 'why improve perfection? :)
Return word
End If

i
= 0
' Get the character at position i.
' if not the last character, then get the next character as well
While i < word.Length
c
= word.Substring(i, 1)
If i < word.Length - 1 Then
nc
= word.Substring(i + 1, 1)
Else
nc
= ""
End If

'"The" -> "Zee" "Put the cake in the oven" -> "Poot zee ceke in zee oofee"
If word.ToLower = "the" Then
retval
= MatchCase(c, "z") & "ee"
i
+= 3
Continue
While
End If

'If the first character
If i = 0 Then
Select Case c
Case "E", "e" '"e" -> "i"; "East" -> "Iest"
retval
= MatchCase(c, "i")
i
+= 1
Continue
While
Case "o", "O" '"o" -> "oo"; "Open" -> "Oopin"
retval
= MatchCase(c, "o") & "o"
i
+= 1
Continue
While
End Select
Else ' not the first character
Select Case c
Case "e"
If nc = "w" Then '"ew" -> "oo"; "new" -> "noo"
retval
+= "oo"
i
+= 2
Continue
While
End If
If nc = "" Then '"e" -> "e-a"; "gone" -> "gune-a"
retval
+= "e-a"
i
+= 1
Continue
While
End If
Case "f" '"f" -> "ff"; "of" -> "ooff"
retval
+= "ff"
i
+= 1
Continue
While

Case "i"
If nc = "r" Then '"ir" -> "ur"; "fire" -> "fure-a"
retval
+= "ur"
i
+= 2
Continue
While
End If
'If we've not replaced an "i" before. Prevents too many "ee"'s in a word.
If Not biseen Then
retval
+= "ee" '"i" -> "ee"; "bid" -> "beed"
i
+= 1
biseen
= True
Continue
While
End If

Case "o"
If nc = "w" Then '"ow" -> "oo"; "Owl" -> "Oowl"
retval
+= "oo"
i
+= 2
Continue
While
Else
retval
+= "u" '"o" -> "u"; "tool" -> "tuul"
i
+= 1
Continue
While
End If

Case "t", "s"
If i <= word.Length - 4 Then
'"-sion" or "-tion" -> "-shun"; "compulsion" -> "cumpoolshun"
If word.Substring(i + 1, 3) = "ion" Then
retval
+= "shun" "Action" -> "Ecshun"
i
+= 4
Continue
While
End If
End If
Case "U", "u" '"u" -> "oo"; "bun" -> "boon"
retval
+= MatchCase(c, "o") & "o"
i
+= 1
Continue
While
End Select
End If

' characters that may be replaced anywhere
Select Case c
Case "A", "a"
Select Case nc
Case "n" '"an" -> "un"; "American" -> "Emereecun"
retval
+= MatchCase(c, "u") & "n"
i
+= 2
Continue
While
Case "u"
retval
+= MatchCase(c, "o") & "o" '"au" -> "ao"; "because" -> "becoose"
i
+= 2
Continue
While
Case ""
'do default action if "a" is last character
Case Else ' "a" is not last character and nc <> "n" or "u"
retval
+= MatchCase(c, "e") '"a" -> "e"; "easy" -> "iesy"
i
+= 1
Continue
While
End Select

Case "e"
If nc = "n" And i = word.Length - 2 Then '"en" -> "ee"; "golden" -> "guldee"
retval
+= "ee"
i
+= 2
Continue
While
Else
' do nothing - use existing character
End If

Case "T", "t"
If nc = "h" Then
If i = word.Length - 2 Then ' If "th" at end of word, "th" -> "t"; "worth" -> "vurt"
retval
+= MatchCase(c, "t")
i
+= 2
Continue
While
Else
' If "th" not at end of word, "th" -> "ze"; "this" -> "zeees"
retval
+= MatchCase(c, "z") & "e"
i
+= 2
Continue
While
End If
End If

Case "V", "v" '"v" -> "f"; "fever" -> "fefer"
retval
+= MatchCase(c, "f")
i
+= 1
Continue
While
Case "W", "w" '"w" -> "v"; "worth" -> "vurt"
retval
+= MatchCase(c, "v")
i
+= 1
Continue
While
End Select

' Default behavior is to replace character with character.
retval
+= c
i
+= 1
End While
Return retval
End Function


'MatchCase returns the resultant, in the same case of the determinate
Private Function MatchCase(ByVal determinate As Char, ByVal resultant As Char) As Char
If Char.IsUpper(determinate) Then
Return Char.ToUpper(resultant)
Else
Return Char.ToLower(resultant)
End If
End Function
End Class