November 29, 2010

AssemblyVersion and AssemblyFileVersion

See here
and here

  • AssemblyVersion is .NET version info whilst 'AssemblyFileVersion' is Windows version number, ie., what you see when you do view the file properties of an assembly in windows explorer. AssemblyVersion defines the version other assemblies are compiled against in their manifest.
  • If 'AssemblyFileVersion' attribute is not present in 'AssemblyInfo.cs' then AssemblyFileVesrion takes the same value as AssemblyVersion.

November 4, 2010

Proxy, Decorator, Adapter, Bridge and Facade Patterns

Found the following explanantion here. It is a useful description of the differences between these patterns as they are very similar.

Proxy, Decorator, Adapter, and Bridge are all variations on "wrapping" a class. But their uses are different:
  • Proxy could be used when you want to lazy-instantiate an object, or hide the fact that you're calling a remote service, or control access to the object.
  • Decorator is also called "Smart Proxy." This is used when you want to add functionality to an object, but not by extending that object's type. This allows you to do so at runtime.
  • Adapter is used when you have an abstract interface, and you want to map that interface to another object which has similar functional role, but a different interface.
  • Bridge is very similar to Adapter, but we call it Bridge when you define both the abstract interface and the underlying implementation. I.e. you're not adapting to some legacy or third-party code, you're the designer of all the code but you need to be able to swap out different implementations.
  • Facade is a higher-level (read: simpler) interface to a subsystem of one or more classes. Think of Facade as a sort of container for other objects, as opposed to simply a wrapper.

November 2, 2010

Deleting a Read-Only File

See also the FileAttributesExtender class
and the PathCombine class
string fullPath = PathCombine(pathRoot, relativePath);
if (File.Exists(fullPath))
{
    FileAttributes fas = File.GetAttributes(fullPath);
    if (fas.Contains(FileAttributes.ReadOnly))
        File.SetAttributes(fullPath, FileAttributes.Normal);
    File.Delete(fullPath);
}

October 22, 2010

String Conversion C++ C#

C++/CLi to C#
std::string str = ...; 
System::String^ mgdstr = gcnew System::String(str.c_str();
and the other way
C# to C++
System::String^ value = ...;
CString cstr(value);
const std::string converted(cstr);

October 13, 2010

Static Property Reflection Sample

This sample enumerates over the "TargetClass" looking for all static properties that return the specified "ReturnType"
public static IEnumerable 
EnumerateReturnTypeProperties()
{
    Type type = typeof(TargetClass);
    PropertyInfo[] props = type.GetProperties();
    foreach (PropertyInfo prop in props)
    {
        // null as 1st GetValue parameter implies
        // only static properties enumerated
        ReturnType xxx = prop.GetValue(null, null)
            as ReturnType;
        if (xxx == null)
            continue;
        yield return xxx;
    }
}

Path.Combine

See here for a description of the behaviour of this method.
Following method resolves some weaknesses in the static method "Path.Combine" found in the System.IO namespace.
public static string PathCombine(string rootPath, string relativePath)
{
    Debug.Assert(rootPath != null);
    Debug.Assert(relativePath != null);

    string res = string.Empty;
    if (rootPath.EndsWith(":"))
    {
        rootPath += @"\";
    }
    // remove initial single '\' or '/' from relative path
    if (relativePath.StartsWith(@"\") || 
        relativePath.StartsWith(@"/"))
    {
        if (! (relativePath.StartsWith(@"\\") || 
               relativePath.StartsWith(@"//")))
        {
           relativePath = relativePath.Substring(1); 
        }
    }
    res = Path.Combine(rootPath, relativePath);
    res = res.Replace('/', '\\'); // replace '/' with '\'
    return res;
}

October 6, 2010

More Complex Progress Dialog

This sample does a file search asynchronously. At the end of the search the progress dialog pauses and closes itself. Internally a timer is used to initiate this automatic close of the progress dialog. AsynchFileFinder is the BackgroundWorker derived object. Here is th Xaml for the dialog:
<Window x:Class="FindingFilesDlg"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Finding modified files" Height="167" Width="514" Closing="Window_Closing" Loaded="Window_Loaded">
    <Grid>
        <Button Height="23" HorizontalAlignment="Right" Margin="0,0,12,12" Name="butCancel" VerticalAlignment="Bottom" Width="91" Click="butCancelDone_Click">_Cancel</Button>
        <TextBlock Height="21" Margin="12,15,0,0" Name="tbActivity" VerticalAlignment="Top"  Text="Searching for modified files:" HorizontalAlignment="Left" Width="Auto" />
        <ProgressBar Margin="12,61,0,52" Name="progressBar" HorizontalAlignment="Left" Width="427" />
        <TextBlock Margin="0,61,16,52" Name="tbPercentage" Text="0%" HorizontalAlignment="Right" Width="31" TextAlignment="Right" />
    </Grid>
</Window>
Here is the code for the dialog:
public partial class FindingFilesDlg : Window
{
    AsyncFileFinder m_AsyncFileFinder = null;
    DispatcherTimer m_Timer = new DispatcherTimer();
    IEnumerable<FileSystemInfo> m_FilesFound = 
               new FileSystemInfo[0];
    int countDown = 1;
    const int SECOND = 10000000;
    bool m_Completed = false;

    public FindingFilesDlg(string rootDir, FileSearchRules fsr)
    {
        InitializeComponent();
        tbActivity.Text = "Preparing to search \'" +
            rootDir + "\' for modified files:";

        InitialiseTimer();
        InitialiseAsyncFileFinder(rootDir, fsr);
    }

    private void InitialiseAsyncFileFinder(
        string rootDir, 
        FileSearchRules fsr)
    {
        // The BackgroundWorker derived object
        m_AsyncFileFinder = new AsyncFileFinder(rootDir, fsr);
        m_AsyncFileFinder.ProgressChanged +=
            new ProgressChangedEventHandler(
                AsynchFileFinder_ProgressChanged);
        m_AsyncFileFinder.RunWorkerCompleted += new
            RunWorkerCompletedEventHandler(
            AsynchFileFinder_RunWorkerCompleted);
    }

    private void InitialiseTimer()
    {
        // Disable timer
        m_Timer.IsEnabled = false;
        // Set timer event interval
        m_Timer.Interval = new TimeSpan((long)(1 * SECOND));
        // Timer events
        m_Timer.Tick += new EventHandler(timer_Tick);
    }

    public IEnumerable<FileSystemInfo> FilesFound
    {
        get { return m_FilesFound; }
    }

    void AsynchFileFinder_RunWorkerCompleted(
        object sender, 
        RunWorkerCompletedEventArgs e)
    {
        if (!e.Cancelled)
        {
            m_Completed = true;
            m_FilesFound = m_AsyncFileFinder.Files;
            tbActivity.Text = "Finished searching \'" + 
                m_AsyncFileFinder.SearchDirectory + 
                "\', " + m_FilesFound.Count().ToString() + 
                " files found";
            progressBar.Value = progressBar.Maximum;
            tbPercentage.Text = "100%";
            UpdateDoneButton();
            // wait several seconds before closing the dialog
            m_Timer.Start(); 
        }
    }

    void UpdateDoneButton()
    {
        string str = "_Done (" + 
              countDown.ToString() + ")";
        butCancel.Content = str;
    }
    
    bool textUpdated = false;
    void AsynchFileFinder_ProgressChanged(object sender, 
        ProgressChangedEventArgs e)
    {
        Debug.Assert((e.ProgressPercentage >= 0) && 
            (e.ProgressPercentage <= 100));
        progressBar.Value = e.ProgressPercentage;
        tbPercentage.Text = e.ProgressPercentage.ToString() +
                "%";
        if ((e.ProgressPercentage > 1) && !textUpdated)
        {
            textUpdated = true;
            tbActivity.Text = "Searching \'" + 
                m_AsyncFileFinder.SearchDirectory + 
                "\' for modified files (" + 
                m_AsyncFileFinder.NumFilesToSearch.ToString() + 
                " files to search) :";
        }
    }

    private void Window_Closing(object sender, CancelEventArgs e)
    {
        CancelAsyncFileFinder();
        if (m_Timer.IsEnabled)
            StopTimer();
        m_AsyncFileFinder.Dispose();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        m_AsyncFileFinder.RunWorkerAsync();
        tbActivity.Text = "Searching \'" + 
            m_AsyncFileFinder.SearchDirectory + 
            "\' for modified files:";
    }

    // wait several seconds before closing the dialog
    void timer_Tick(object sender, EventArgs e)
    {
        --countDown;
        UpdateDoneButton();
        if (countDown == 0)
        {
            StopTimer();
            DialogResult = m_Completed;
        }
    }

    private void StopTimer()
    {
        m_Timer.Stop();
    }

    private void butCancelDone_Click(object sender, 
          RoutedEventArgs e)
    {
        if (!m_Completed)
        {
            CancelAsyncFileFinder();
            if (m_Timer.IsEnabled)
                StopTimer();
        }
        DialogResult = m_Completed;
    }

    private void CancelAsyncFileFinder()
    {
        if (m_AsyncFileFinder.IsBusy)
            m_AsyncFileFinder.CancelAsync();
    }
}

Here is the code for the background worker object:
internal class AsyncFileFinder : BackgroundWorker
{
    private FileSearchRules FileSearchRules { get; set; }
    string m_RootDir = string.Empty;
    List<System.IO.FileSystemInfo> files = new 
        List<System.IO.FileSystemInfo>();
    int m_NumberFilesToSearch = 0;

    // Pass required info in through constructor, 
    // cant change it whilst a search is underway
    public AsyncFileFinder(string rootDir, 
        FileSearchRules fsr) : base()
    {
        this.WorkerReportsProgress = true;
        this.WorkerSupportsCancellation = true;
        m_RootDir = rootDir;
        FileSearchRules = new FileSearchRules(fsr);
    }

    public string SearchDirectory
    {
        get { return m_RootDir; }
    }

    public IEnumerable<FileSystemInfo> Files
    {
        get
        {
            if (IsBusy) // No access while thread active
                return new List<FileSystemInfo>();
            else
                return files.AsReadOnly();
        }
    }

    // Total number of files that will be checked
    public int NumFilesToSearch
    {
        get
        {
            return m_NumberFilesToSearch;
        }
    }

    protected override void OnDoWork(DoWorkEventArgs e)
    {
        bool cancelled = false;
        double progress = 0;
        double prevProgress = 0;
        files = new List<FileSystemInfo>();
        DirectoryInfo targ = new DirectoryInfo(m_RootDir);
        // Count the number of files to process first
        foreach (System.IO.FileSystemInfo fsi in
            targ.IterateFiles(true))
        {
            if (CancellationPending)
            {
                cancelled = true;
                break;
            }
            
            Interlocked.Increment(ref 
                m_NumberFilesToSearch);
        }
        int count = 0;
        if (!cancelled)
        {
            foreach (FileSystemInfo fsi in 
                targ.IterateFiles(true))
            {
                if (CancellationPending)
                {
                    cancelled = true;
                    break;
                }

                ++count;
                // progress as a percentage
                progress = (100.0 * (double)count /
                    (double)m_NumberFilesToSearch) + 0.5;
                // Report every 1% of progress
                if ((progress - prevProgress) > 1.0)
                {
                    ReportProgress((int)(progress));
                    prevProgress = progress;
                }

                if (FileSearchRules.PassesRules(fsi))
                {
                    files.Add(fsi);
                }
            }
        }
        e.Cancel = cancelled;
    }
 
}