Verifying Chilean RUT code (tax number) in C#

In one of the projects at my work, I got task to verify some data (in migration process from old system to new one); one of the fields was Chilean RUT (Rol Único Tributario) or Tax Identification Number.

The RUT has 8 digits plus a validation number or letter (xx.xxx.xxx-z) and algorithm for verification (as per my knowledge) is as follows:

  1. Remove all characters except digits and last char which can be digit or letter “K”
  2. Pad with zeros (“0”) string until it is exactly 9 symbols long
  3. Multiply, from left to right, first 8 digits with following factors: 3, 2, 7, 6, 5, 4, 3, 2 and sum those results (let’s call it total)
  4. Find difference: 11 – (total % 11) (let’s call it rest)
  5. Determine control char by:
      5.1 If rest = 11, than control char “0” (zero)
      5.2 If rest = 10, than control char “K”
      5.3 Otherwise, rest is control char
  6. If the original control char (9th char) is equal to calculated control char, RUT is valid.

C# code for this could be like this:

/// <summary>
/// Routine for checking of RUT correctness
/// </summary>
/// <param name="rut">RUT to check</param>
/// <returns>true if RUT is valid</returns>
/// <remarks>Only numbers and optional "K" at the end of string are expected
/// </remarks>
public static bool IsRutOk(string rut)
{
    const string RutRegex = "[0-9]+K?";
    Regex RegExRut = new Regex(RutRegex, RegexOptions.Compiled | 
    RegexOptions.IgnoreCase);
    int[] coefs = {3, 2, 7, 6, 5, 4, 3, 2};

    // In case that rut is padded with spaces
    rut = rut.Trim().ToUpperInvariant();

    if (!RegExRut.IsMatch(rut)) { return false; }

    if (rut.Length > 9) { return false; }

    // If shorter than 9 characters (8 + control char) ...
    while (rut.Length < 9) { rut = "0" + rut; }

    int total = 0;

    for (int index = 0; index < rut.Length - 1; index++)
    {
         char curr = rut.Substring(index, 1).ToCharArray()[0];
         total += coefs[index]*(curr - '0');
    }

    int rest = 11 - (total%11);

    if (rest == 11) rest = 0;

    if ((rest == 10) && rut.EndsWith("K")) { return true; }

    if (rut.Substring(rut.Length - 1, 1).ToCharArray()[0] == ('0' + rest))
    { 
        return true; 
    }

    return false;
}

Transliteration easy way – Microsoft Transliteration Utility

If you are lucky enough 🙂 to have not one, but two alphabets in daily use, your regular task in programming will be transliteration – transformation of text from one script (alphabet) to another.

In Serbia, we are using Latin as well as Cyrillic alphabet (and that is not same Cyrillic as Russian one) and common task is conversion from one to another and vice-versa.

This is not too complicated request; you can easily create necessary procedures; however, there is a better way:

Microsoft Transliteration Utility (MTU) is not widely known, but very useful tool for just that purpose: transliteration. It can easily transliterate text either typed in a text box or from one file to another.

There is set of predefined translations:

  • Serbian Cyrillic to Latin / Serbian Latin to Cyrillic
  • Bosnian Cyrillic to Latin / Bosnian Latin to Cyrillic
  • Hangul to Romanization
  • Inuktitut to Romanization / Romanization to Inuktitut
  • Malayalam to Romanization / Romanization to Malayalam

You are not limited to above set; you can easily create your own translations, using Module Development Console:

Microsoft Transliteration Utility - Module Development Console
(click on image for larger version)

Creating simple textual file, you can use full power of MTU’s parsing engine: definitions of input and output characters, rules for transliteration including definitions of new states for translation state machine.

This is not the end – you can even use MTU programmatically (although please check EULA for commercial usage):

  • Add reference to MSTranslitTools.DLL (it can be found in %programfiles%Microsoft Transliteration Utility)
  • Add using System.NaturalLanguage.Tools;
  • Current translation files (.tms) can be found in %CommonProgramFiles%TransliterationModulesMicrosoft
  • Here is simple code fragment to demonstrate:
TransliteratorSpecification specification =
   TransliteratorSpecification.FromSpecificationFile("Serbian Latin to Cyrillic.tms");

Transliterator transliterator = Transliterator.FromSpecification(specification);
string rezultat = transliterator.Transliterate("Vesic.Org");

Console.WriteLine(rezultat);

Upgrade of Application/User Settings between application versions

One of the polished areas of .Net Framework 2.0 is manipulation with Application/User settings in WinForms applications. You can store almost anything in appropriate app.config or user.config file, and to manipulate with those settings with ease – wrapper class created around Settings allows you to use settings as just another field in your code.

But, what about of upgrade existing settings once when you create new version of the application?

That is supported via Upgrade() method:


	 Settings.Default.Upgrade();
	 Settings.Default.Save();

You can add one user bool setting (for example UpgradeSettings) with default value True, which will be a flag if upgrade is carried out; code can be:


	if (Settings.Default.UpgradeSettings)
	{
		 Settings.Default.Upgrade();
		 Settings.Default.UpgradeSettings = false;
		 Settings.Default.Save();
	}

Suppose that your application is simple little application, not a Click-Once or even distributed with installer – just archive which users can download, unpack and start using.

If you new version picks user settings folder from previous one, all is fine – Upgrade() method will find previous version and upgrade them:

Application versions - as expected

Suppose that this is true:

  • both versions are either signed or not (if you do signing of assembly between versions, Upgrade will fail)
  • both versions are either Click-Once applications or not (for the same reasons)

In theory, all is fine and Upgrade should do the job. But, what if all above is fulfilled and that you still have different user settings folder:

Application versions - mismatch

What can be reason that same application creates completely different user settings folder? This situation happened to me. After couple of hours, I could not determine reason, so I pulled out heavy tools – Reflector.

If you start tracking .Upgrade(), it goes to: ApplicationSettingsBase.Upgrade which simply calls IApplicationSettingsProvider.Upgrade for each provider present. For simple WinForms application, appropriate provider is LocalFileSettingsProvider. After some more thorough analysis, internal method for determining folder of settings store is:

System.Configuration.ClientConfigPaths

One of elements in determining local store for user settings was hashed value derived from Evidences for given assembly.

It turns out that one of Evidence elements for non-signed assemblies is path from where assembly/application is launched!.

To summarize: in order for Upgrade() of settings succeed, your new version of application should be deployed in the exact same folder where previous version is found; otherwise, Upgrade() will fail (or at least, it won’t do what you expected 🙂 )

How to make Windows Form app truly Full Screen (and to hide Taskbar) in C#?

One of sound-like-simple questions is “how to make your application truly Full Screen” i.e. not showing Taskbar or anything like that.

Initial approach is obvious:


    targetForm.WindowState = FormWindowState.Maximized;
    targetForm.FormBorderStyle = FormBorderStyle.None;
    targetForm.TopMost = true;

Does it work? Well, sort of. If your Taskbar have default setting unchecked for “Keep the taskbar on top of other windows”, this will present your application in all it’s glory all over screen estate.

However, if the Taskbar is set to appear on top of all others, this won’t help – your application won’t cover it.

Let’s go further – next step is to use P/Invoke and to engage Win32 API services. There is easy way to hide particular window. So, find the Taskbar and hide it:


    private const int SW_HIDE = 0;
    private const int SW_SHOW = 1;

    [DllImport("user32.dll")]
    private static extern int FindWindow(string className, string windowText);
    [DllImport("user32.dll")]
    private static extern int ShowWindow(int hwnd, int command);

    int hWnd = FindWindow("Shell_TrayWnd", "");
    ShowWindow(hWnd, SW_HIDE);

    targetForm.WindowState = FormWindowState.Maximized;
    targetForm.FormBorderStyle = FormBorderStyle.None;
    targetForm.TopMost = true;

(you need to add using System.Runtime.InteropServices;)

Is this better? In theory yes – Taskbar is hidden, but your application still does not occupy whole screen – place where Taskbar was is not used.

Real and proven solution is to make request to WinAPI that your form take whole screen estate – Taskbar will hide itself in that case. Full information about that can be found in KB Article Q179363: How To Cover the Task Bar with a Window and here is the code:


/// <summary>
/// Selected Win AI Function Calls
/// </summary>

public class WinApi
{
    [DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
    public static extern int GetSystemMetrics(int which);

    [DllImport("user32.dll")]
    public static extern void 
        SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter,
                     int X, int Y, int width, int height, uint flags);        
    
    private const int SM_CXSCREEN = 0;
    private const int SM_CYSCREEN = 1;
    private static IntPtr HWND_TOP = IntPtr.Zero;
    private const int SWP_SHOWWINDOW = 64; // 0x0040
    
    public static int ScreenX
    {
        get { return GetSystemMetrics(SM_CXSCREEN);}
    }
    
    public static int ScreenY
    {
        get { return GetSystemMetrics(SM_CYSCREEN);}
    }
    
    public static void SetWinFullScreen(IntPtr hwnd)
    {
        SetWindowPos(hwnd, HWND_TOP, 0, 0, ScreenX, ScreenY, SWP_SHOWWINDOW);
    }
}

/// <summary>
/// Class used to preserve / restore state of the form
/// </summary>
public class FormState
{
    private FormWindowState winState;
    private FormBorderStyle brdStyle;
    private bool topMost;
    private Rectangle bounds;

    private bool IsMaximized = false;

    public void Maximize(Form targetForm)
    {
        if (!IsMaximized)
        {
            IsMaximized = true;
            Save(targetForm);
            targetForm.WindowState = FormWindowState.Maximized;
            targetForm.FormBorderStyle = FormBorderStyle.None;
            targetForm.TopMost = true;
            WinApi.SetWinFullScreen(targetForm.Handle);
        }
    }
    
    public void Save(Form targetForm)
    {
        winState = targetForm.WindowState;
        brdStyle = targetForm.FormBorderStyle;
        topMost = targetForm.TopMost;
        bounds = targetForm.Bounds;
    }

    public void Restore(Form targetForm)
    {
        targetForm.WindowState = winState;
        targetForm.FormBorderStyle = brdStyle;
        targetForm.TopMost = topMost;
        targetForm.Bounds = bounds;
        IsMaximized = false;
    }
}

Code for example application is here: MaxWinForm.zip

How to find out Volume Serial Number / CPU info

One of the techniques used when you plan to protect your valuable intellectual property 🙂 (your code) is reading some kind of hardware signature of machine where program is installed.

Usual initial approach is to read Volume serial number (bear in mind that this number can be easily changed) or similar hardware information. Here is where WMI – Windows Management Instrumentation comes in play – you can find enormous amount of information using WMI.

Let’s give small example – find out Volume Serial Number:

– add reference to System.Management.dll
– here is the code:


using System;
using System.Collections.Generic;
using System.Text;
using System.Management;

namespace Org.Vesic.WMI.Example
{
    class Program
    {
        static void Main(string[] args)
        {
            string targetVolume = "C";
            
            if((args != null) && args.Length > 0)
            {
                targetVolume = args[0];
            }

            string mngObject = String.Format("Win32_LogicalDisk.DeviceID="{0}:"", 
                                             targetVolume);
            try 
            {
                ManagementObject myDisk = new ManagementObject(mngObject);
                PropertyData myDiskProp = myDisk.Properties["VolumeSerialNumber"];
                
                Console.WriteLine("HDD Serial for Volume {0}: is {1}", 
                                  targetVolume, myDiskProp.Value);
            }
    
            catch(ManagementException ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }
    }
}

Simple and very effective. Of course, you can read load of other data types, NIC info, even CPU info:


ManagementObjectSearcher mos = new ManagementObjectSearcher
	 ("SELECT Name, L2CacheSize, L2CacheSpeed FROM  Win32_Processor");

ManagementObjectCollection moc = mos.Get();

int procCount = -1;

foreach (ManagementObject mob in moc)
{
	 procCount++;
	 Console.WriteLine("Processor No. {0}: {1}, L2 Cache size/speed: {2} / {3}",
		  procCount,
		  mob.Properties["Name"].Value,
		  mob.Properties["L2CacheSize"].Value,
		  mob.Properties["L2CacheSpeed"].Value
		  );
}            

Validation of input elements and Form Closing

One of very often questions ask by WinForms developers is “How to make sure that if Form is Closing, no validation occurs in contained controls”?

For reference, validating input is rather straightforward:

1. Add ErrorProvider to Form
2. For control in question, add handlers for Validating and Validated events:


        private void tbFixPath_Validating(object sender, CancelEventArgs e)
        {
            if (!ControlValidated(tbFixPath.Text))
            {
                // Cancel the event and select the text to be corrected by the user.
                e.Cancel = true;
                tbFixPath.Select(0, tbFixPath.Text.Length);

                // Set the ErrorProvider error with the text to display. 
                this.errorProvider1.SetError(tbFixPath, 
                   "Sorry, something went wrong during validation. Please check.");
            }
        }

        private void tbFixPath_Validated(object sender, EventArgs e)
        {
                // After successful validation, clear error message.
                errorProvider1.SetError(tbFixPath, "");
        }

3. Everything is fine, until user tries to close form where some input elements are not validated – closing will fail. In order to prevent this, if you are absolutely sure that you want to close such form, just add:


        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
                e.Cancel = false;
        }

so that no cancellation of closing event will occur.

I know that this is basic 🙂 but after I was ask for 10th time in 6 months, I figured that is better to note this somewhere 🙂