Content:
1. Windows Services
   - Create Windows Service (that will just write to a text log)
   - How to specify credentials for WIndows service
2. Generics
   - Generics with classes
   - Create new item with generics
   - Create list based on generic type
3. Dynamic
4. Enums
   - Enum conversion
5. Immutable class
6. Assembly Architektur
7. Mixed mode assembly
8. C#: memory management
   - Garbagge collection
   - Appearance of list in memory
   - GC roots
   - Memory leaks
   - Resources
   - GC modes
   - GC.Collect()
9. Extension methods
10. Tracing/Logging
   - Trace
   - Tracer in app.config
   - Trace with filter
   - Tracing switch
   - Event Log
   - Logging with log4net
   - Output current class and method (for logging)
11. Configuration
   - Get value from default config
   - Set value in default config
   - Get, add and modify values in specific config file
   - Use properties for configuration
   - Custom section in app.config - simple
   - Custom section in app.config - complex
12. Registry
13. Messages
   - Create message queue
   - Read messages synchronously
   - Read messages asynchronously
14. Activator
15. SecureString
16. Events
   - Event aggregator with Caliburn
   - Event aggregator with Prism
17. Lazy



1. Windows Services

Create Windows Service (that will just write to a text log)

Windows Service Applications (MSDN)
Walktrough
In Visual Studio 2010, create new project, Windows group, Windows service
in Service1.cs, OnStart(string[] args) {...} add an action that the service will start. The method has to return in 30 seconds.

C#
Debug.Listeners.Add(new TextWriterTraceListener(System.IO.File.Create(@"c:\_delete\trace.txt")));
Trace.AutoFlush = true;
Trace.WriteLine("starting");
Similar way implement OnStop(), OnShutdown)(), OnPause(), OnContinue() Add installer: in Solution explorer, Doubleclick on Service1.cs in Service1 designer, right click and select "Add Installer" doubleclick on ProjectInstaller.cs Right click on serviceInstaller1, select Properties, set Service Name = "MyService", Start Type="Automatic" if you want to service to start automatically Right click on serviceProcessInstaller1, select Properties, set Account = "LocalSystem" in order to avoid entering user name and password Install: InstallUtil.exe WindowsServiceBasics.exe Start: in start menu type "services.msc", find MyService, right click Start Note: you do not have to install the service again when you recompile the binary. Unistall: InstallUtil.exe /u WindowsServiceBasics.exe Note: You need to close the management console window (service is marked for deletion) Debugging: Start the service (see above) From Visual Studio (running as Administrator), menu Debug, Attach to process Click "Show processes from all users" Select process that belongs to the service, click Attach

How to specify credentials for WIndows service

1. Programatically: Right click on serviceProcessInstaller1, select Properties, set Account = "myAccount"

C#
// ProjectInstaller.Designer.cs 
private void InitializeComponents() {
  ... 
  this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
  this.serviceProcessInstaller1.Password = null;
  this.serviceProcessInstaller1.Username = null;
  ...
}
2. Configuration services.msc right click on the service tab LogOn, enter user and password

2. Generics

Code example:Basics.zip [Misc.cs]

Resource: csharp-online.net

Available in .net 2.0
namespace System.Collections.Generics

Generics with classes

The example below demostrates how generics can be used to write a shared code that populate classes members that belong to base class for any of inherited classes.

C#
private class PersonLite
{
    internal int Id;
    internal string Name;
}

private class Person : PersonLite
{
    internal DateTime BirthDay;
}

internal static void ShowBasics()
{
    PersonLite personLite = new PersonLite();
    Populate(personLite);

    Person person = new Person();
    Populate(person);
    person.BirthDay = DateTime.Parse("1969-01-01");
}

private static void Populate<T>(T p) where T : PersonLite
{
    // read data from external source, e.g. DB
    p.Id = 1;
    p.Name = "John";
}

Create new item with generics

The example below demostrates how create new item with generics

C#
private class PersonLite
{
    internal int Id;
    internal string Name;
}

private class Person : PersonLite
{
    internal DateTime BirthDay;
}

internal static void DemoCreateNewObject()
{
    PersonLite personLite = CreateNew<PersonLite>();
    Person person = CreateNew<Person>();
}

private static T CreateNew<T>() where T : PersonLite, new()
{
    T result = new T();
    result.Name = "new";
    return result;
}

Create list based on generic type

The example below demostrates how load a list of item based on generic type

C#
private class PersonLite
{
    internal int Id;
    internal string Name;
}

private class Person : PersonLite
{
    internal DateTime BirthDay;
}

internal static void DemoLoadDataBasedOnType()
{
    List<PersonLite> list = CreateList<PersonLite>();
    List<Person> list2 = CreateList<Person>();
}

private static List<T> CreateList<T>() where T : PersonLite,  new() // new() is needed New<T>()
{
    List<T> result = new List<T>();
    Type t = typeof(T);
    
    int count = 1;
    switch (t.Name)
    { 
        // this can be used to generate a SQL query
        case "PersonLite":  count = 2; break;
        case "Person": count = 3; break;
    }

    for (int i = 0; i < count; i++)
    {
        T item = new T();
        item.Name = "new";
        result.Add(item);
    }
    return result;
}

3. Dynamic

MSDN If an object is defined as dynammic, it bypasses static type checks at compile time

C#
DateTime date = new DateTime();
// date.AddCentury(1); fails at compile time

dynamic dateDynamic = new DateTime();
dateDynamic.AddCentury(1); // fails at runtime

var obj = date as dynamic;
obj.AddCentury();  // fails at runtime

4. Enums

Enum conversion

C#
public enum Department
{
  IT,
  Finance
}

string text = Enum.GetName(IT.GetType(), IT);

Department d = (Department) Enum.Parse(typeof(Department), "IT");

5. Immutable class

Immutable types are reference types with value type semantics.
Problem: when a class is passed as an argument to a method, it content can be unexpectedly modified.
How to prevent that: Classes can be ensured to be immutable by using readonly keyword for their members.

6. Assembly Architektur

Assembly architektur can be identified with corflags utility.
corflags myassembly.exe
Any CPU: PE = PE32 and 32BIT = 0
x86:     PE = PE32 and 32BIT = 1
64-bit:  PE = PE32+ and 32BIT = 0

7. Mixed mode assembly

Source MSDN : Mixed assemblies are capable of containing both unmanaged machine instructions and MSIL instructions. This allows them to call and be called by .NET components, while retaining compatibility with components that are entirely unmanaged. Using mixed assemblies, developers can author applications using a mixture of managed and unmanaged functionality. This makes mixed assemblies ideal for migrating existing Visual C++ applications to the .NET Platform.

An existing application consisting entirely of unmanaged functions can be brought to the .NET platform by recompiling just one module with the /clr compiler switch

8. C#: memory management

Garbagge collection

Fundamentals of Garbage Collection (MSDN)

Large Object Heap - Objects > 85k or multidimensinonal arrays

There are 3 small Object Heaps: Generation 0, 1 and 2
The idea is that objects created recently are more likely to be released quickly
Small objects are allocated on Gen 0 heap.
When Gen 0 is full (256k), GC runs and objects that cannot be disposed, so called survivors, are moved to Gen 1
When Gen 1 is full (2M), GC runs on Gen 1 and moves survivors to Gen 2 and runs GC on Gen 0.
When Gen 2 is full (10M), full GC runs on Gen1 and then Gen 0. If insuficiant memory, then OutOfMemory exception is thrown.
Objects on Large than 85k are allocated on Object Heap (LOH) and are collected when full (Gen 2) collection takes place. LOH is never compacted, it fragments over time.
If there isn't enough memory to allocate new large objects, OutOfMemory exception is thrown.

The garbage collector calculates whether or not an object can be disposed from one of the heaps by creating a graph, which shows how the object is referenced by other objects.

Appearance of list in memory

List is implented using an array and when new item is added, a new and larger array is allocated and the old one is released. List reaches 85k with about 20.000 items.

Arrays with 10,000 or more end up on LOH.

GC roots

Local variables: are kept alive as long as the method is on call stack (in Release build, JIT can unmark it before the method exits)

Static variables: always considered GC roots

Managed object passed to COM+ through interop: Ends when COM reference counter is set to 0.

Object with finalizer: If it is no longer live, it becomes special kind of GC root until .net called the finalizer (placed in finalizer queue). Require more than one collection GC. Object with finalizers cause delay to be released from memory, use Dispose() whenever possible and GC.SuppressFinalize().

Memory leaks

Holding references to managed objects (e.g. event handlers):
Solution:
- use local rather than global variable
- call Dispose() or use "using(...){ }" concept
- set variable to null when it not needed anymore

Failing to release unmanaged resources
- practises of deallocating unmanaged code apply

Failing to dispose Drawing objects
e.g Bitmaps, brushes, fonts - these are managed objects, but they hold references to unmanaged objects. These resources are cleaned up when the objects are disposed

Solving LOH fragmentation:
- Split list or arrays into smaller objects that remain below 85kB
- allocate the largest and longest-living objects first
- let GC compact the LOH (.net > 4.5.1). If GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce the LOH is compacted during the next full blocking garbage collection, and the property value is reset to default.
- restart program

Generally: do no rely on finalization. 1. It happens unpredictably and may take long time, 2. Finalizer of certain object may not call Dispose() (if it has a bug)

Resources

On 32 bit system a program can use up to 1.5G memory
64 system can page out memory, which decreases performance, and event run out disk space.

GC modes

Concurent (desktop apps): GC will not stop the app, the app will just slow.
Server (ASP.net apps): NET will suspend the running application while the garbage collector is running.

GC.Collect()

Using GC.Collect() will cause newly created objects released later (moved to Gen 2) which increases the likelihood of another expensive full collection in the near future.

9. Extension methods

Code example:Basics.zip [ExtensionMethods.cs]
Extension methods allow to extend classes that are marked as sealed. More detail at MSDN

C#
sealed class SealedClass
{
    internal void DoSomething()
    {
        Console.WriteLine("SealedClass.DoSomething()");
    }

    // cannot be called from extension method
    protected void DoSomethingProtected()
    {
        Console.WriteLine("SealedClass.DoSomethingProtected()");
    }

    internal static void DoSomethingStatic()
    {
        Console.WriteLine("SealedClass.DoSomethingStatic()");
    }
}

static class ExtensionClass
{
    internal static void DoSomething(this SealedClass sealedClass)
    {
        Console.WriteLine("Gets never called");
    }

    internal static void DoSomething(this SealedClass sealedClass, int i)
    {
        sealedClass.DoSomething(); // class method can be called
// sealedClass.DoSomethingProtected(); not available Console.WriteLine("ExtensionClass.DoSomething(int i)"); } internal static void DoSomethingElse(this SealedClass sealedClass) { Console.WriteLine("ExtensionClass.DoSomethingElse()"); } internal static void DoSomethingStatic(this SealedClass sealedClass) { Console.WriteLine("Gets never called"); } } class ExtensionMethodsDemo { internal static void DemoExtensionMethods() { SealedClass sealedClass = new SealedClass(); sealedClass.DoSomething(); // calls SealedClass sealedClass.DoSomething(1); // calls ExtensionClass sealedClass.DoSomethingElse(); // calls ExtensionClass SealedClass.DoSomethingStatic(); // calls SealedClass } }

10. Tracing/Logging

Code example:Basics.zip [Logging.cs]

Trace

Write tracing information to log file, console and event log
Tracing Options

C#
// trace to a text file
TextWriterTraceListener textWriterTraceListener = new TextWriterTraceListener(LOG_FILE_NAME);
textWriterTraceListener.TraceOutputOptions = TraceOptions.DateTime | TraceOptions.ProcessId | TraceOptions.ThreadId;
Trace.Listeners.Add(textWriterTraceListener);

// trace to the console
Trace.Listeners.Add(new ConsoleTraceListener());

// trace to event log
// !! this code must be executed als administrator when the source Basics.exe does not exist
 ventLogTraceListener eventLogTraceListener = new EventLogTraceListener("Basics.exe");

Trace.WriteLine("Written by Trace.WriteLine()");          
Trace.Flush();

Trace.AutoFlush = true;
Trace.TraceInformation("Written by Trace.TraceInformation");   // writes datetime + process id + thread id
Trace.TraceWarning("Written by Trace.TraceWarning");
Trace.TraceError("Written by Trace.TraceError");

Tracer in app.config

Listener can be configured in app.config instead of created in code.

XML
 <configuration>
  <system.diagnostics>
    <trace autoflush="true">
      <listeners>
        <remove name="Default"/>
        <add name="textWriterTraceListenerConfig" 
            type="System.Diagnostics.TextWriterTraceListener" 
            traceOutputOptions="DateTime,Timestamp,Callstack,LogicalOperationStack,ProcessId,ThreadId" 
            initializeData="MyLogFromConfig.log">
        </add>
      </listeners>
    </trace>
  </system.diagnostics>
</configuration>

Trace with filter

Application can assign filter for each listener that will allow only messages with higher importance to be recorded.

C#
TextWriterTraceListener textWriterTraceListener = new TextWriterTraceListener(LOG_FILE_NAME);
            
EventTypeFilter e = new EventTypeFilter(SourceLevels.Error);  // .Error: Verbose, Information, Warning are ignored
textWriterTraceListener.Filter = e;

Trace.Listeners.Add(textWriterTraceListener);
Trace.TraceWarning("DemoEventTraceWithFilter - Trace warning"); // will be ignored (by filter)
Trace.TraceError("DemoEventTraceWithFilter - Trace error");     // will be written
This can be also done in app.config

XML
<add name="textWriterTraceListenerConfig" 
    type="System.Diagnostics.TextWriterTraceListener" 
    initializeData="MyLogFromConfig.log">
  <filter type="System.Diagnostics.EventTypeFilter" initializeData="Warning"/>
</add>

Tracing switch

Another way how to filter messages is to use TraceSwitch by setting its Level

C#
TraceSwitch traceSwitch = new TraceSwitch("textWriterTraceListener", "description" );
traceSwitch.Level = TraceLevel.Warning;  
// Off 0, Error 1, Warning 2  default, Info 3, Verbose 4

TextWriterTraceListener textWriterTraceListener = new TextWriterTraceListener(LOG_FILE_NAME);
Trace.Listeners.Add(textWriterTraceListener);

Trace.WriteLine("switch controlled - no IF");  
Trace.WriteLineIf(traceSwitch.TraceVerbose, "switch controlled verbose");  // will be ignored
Trace.WriteLineIf(traceSwitch.TraceError, "switch controlled error");
Trace.Flush(); 
or in app.config

XML
  <system.diagnostics>
    <switches>
      <add name="textWriterTraceListener" value="3"/>
    </switches>
   ... 

Event Log

Event Log is Windows logging system and cen viewed by Event Viewer.
.Net provides EventLog class that can be used to write to the log.
Windows provides Application, Security and System logs.

C#
string myAppName = "Basics3.exe"; // event source name must be unique accross all logs

// Write to application log
EventLog eventLog = new EventLog("Application", Environment.MachineName, myAppName); EventLog.WriteEntry(myAppName, "Demo error", EventLogEntryType.Error); // !! this code must be executed als administrator when the source does not exist // Write to custom log EventLog customEventLog = new EventLog("ZbynekDemo1", Environment.MachineName, myAppName + "Custom"); customEventLog.WriteEntry("Demo error", EventLogEntryType.Error); // first time you have to close and open Event Viewer to see the new custom event log

Logging with log4net

log4net Logging framework
configuration
Install log4net NuGet package (log4net.dll will be added to project references)

C#
using log4net;
using log4net.Config;

namespace Log4NetBasics
{
    class Program
    {
        private static readonly ILog _logger = LogManager.GetLogger("MyLogger");

        static void Main(string[] args)
        {
            XmlConfigurator.Configure(); // read the configuration from app.config
            // BasicConfigurator.Configure();  // ! does not read configuration for File or Debug appenders

            _logger.Info("Info message");
            _logger.Warn("Warning message");
            _logger.Error("Error message");

            Console.ReadLine();
        }
    }
}

XML
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  </configSections>

  <log4net>
    <appender name="Console" type="log4net.Appender.ColoredConsoleAppender">
      <layout type="log4net.Layout.PatternLayout">
      </layout>
    </appender>

    <root>
      <level value="ALL"/>
      <appender-ref ref="Console"/>
    </root>
  </log4net>

</configuration>
If you want the logger to add information like datetime or thread id, you need to use conversionPattern element

XML
<log4net>
<appender name="Console" type="log4net.Appender.ColoredConsoleAppender">
  <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %-5level %logger %property{myData} (%file:%line): %message %newline"/>
  </layout>
</appender>

...

</log4net>
will produce:
2016-05-22 11:00:05,887 [10] ERROR MyLogger Abc (c:\Log4NetBasics\Program.cs:18): Error message
%property{myData} will pint out content set as below

C#
GlobalContext.Properties["myData"] = "abc";
If you want the messages in console colored, you can define mapping between the severity and the color

XML
<log4net>
<appender name="Console" type="log4net.Appender.ColoredConsoleAppender">
	<mapping>
		<level value="ERROR"/>
		<foreColor value="Red"/>
	</mapping>
	...
</appender>
...
</log4net>
Adding another listeners, called appenders, can be done the same way in app.config

XML
<log4net>
    <appender name="File" type="log4net.Appender.RollingFileAppender">
      <param name="File" value="c:\\temp\\log4netBasics.log"/>
      <param name="AppendToFile" value="true"/>
      <maximumFileSize value="100KB" />
      <maxSizeRollBackups value="2" />

      <layout type="log4net.Layout.PatternLayout"></layout>
    </appender>

    <appender name="DebuggerAppender" type="log4net.Appender.DebugAppender">
      <layout type="log4net.Layout.PatternLayout">
      </layout>
    </appender>

	...

</log4net>

Output current class and method (for logging)

C#
private static void Log(string message)
{
	var frame = new StackFrame(skipFrames: 1); // 1 will get the frame of the caller of the Log method
	System.Reflection.MethodBase callingMethod = frame.GetMethod();
	Console.WriteLine("Class: {0} Method: {1}", callingMethod.DeclaringType.Name, callingMethod);
}

11. Configuration

Code example:Basics.zip [Configuration.cs]

Get value from default config

app.config file contains key-vaulue pair

XML
<configuration>
  <appSettings>
    <add key="key1" value="value1"  />
  </appSettings>
</configuration>
the values can be retrieved as

C#
string settingValue = System.Configuration.ConfigurationManager.AppSettings["key1"];

Set value in default config

Configuration values in default app.config cannot be modified, because the is no setter defined

C#
try
{
    System.Configuration.ConfigurationManager.AppSettings.Add("added", "added value");
}
catch (ConfigurationErrorsException ex)
{
    Console.WriteLine(ex); 
    // "The configuration is write protected" exception. When executed from VS or Basics.exe.
}
but you can open the default app.config as a specific file and then you can modify the values.

Get, add and modify values in specific config file

C#
ExeConfigurationFileMap map = new System.Configuration.ExeConfigurationFileMap();
map.ExeConfigFilename = "app1.config";  // this can be used for Basics.exe.config or Basics.vshost.exe.config as well
Configuration mappedConfiguration = ConfigurationManager.OpenMappedExeConfiguration(map, System.Configuration.ConfigurationUserLevel.None);
AppSettingsSection appSettingsSection = (AppSettingsSection)mappedConfiguration.GetSection("appSettings");

// Get
string settings = appSettingsSection.Settings["key2"].Value;

// Add
appSettingsSection.Settings.Add("addedKey", "addedValue");
mappedConfiguration.Save();

// Remove
appSettingsSection.Settings.Remove("addedKey");
mappedConfiguration.Save();

Use properties for configuration

MSDN
Another way how to store configuration data is to use Properties.Settings class.
- The settings are stored in app.config either in userSettings or applicationSettings sections.

XML
<configuration>
  <configSections>
    <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
      <section name="Basics.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
    </sectionGroup>
    <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
      <section name="Basics.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
    </sectionGroup>
  </configSections>
  <userSettings>
    <Basics.Properties.Settings>
      <setting name="MySettingForUser" serializeAs="String">
        <value>user</value>
      </setting>
    </Basics.Properties.Settings>
  </userSettings>
  <applicationSettings>
    <Basics.Properties.Settings>
      <setting name="MySettingForApp" serializeAs="String">
        <value>app</value>
      </setting>
    </Basics.Properties.Settings>
  </applicationSettings>
</configuration>
- The settings need to be created in the Settings tab in Project properties.
After the creation the Properties\Settings.settings file will be modified.

C#
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
  [global::System.Configuration.UserScopedSettingAttribute()]
  public string MySettingForUser {
      get {
          return ((string)(this["MySettingForUser"]));
      }
      set {
          this["MySettingForUser"] = value;
      }
  }
}
The settings can be accessed with the code below:

C#
// get application scope setting value 
string value = Properties.Settings.Default.MySettingForApp;
// Properties.Settings.Default.SettingApp = value + "x";   does not compile, SettingApp has no setter

// get and set user scope setting value 
string userValue = Properties.Settings.Default.MySettingForUser;
Properties.Settings.Default.MySettingForUser = userValue + "+";
Properties.Settings.Default.Save();

Custom section in app.config - simple

Define class representing the section

C#
class ConfigurationElementSimple : System.Configuration.ConfigurationSection
{
    [System.Configuration.ConfigurationProperty("myValue", IsRequired = true)]
    public string MyValue
    {
        get { return (string)base["myValue"]; }
        set { base["myValue"] = value; }
    }

    [System.Configuration.ConfigurationProperty("myNumber", IsRequired = false)]
    public int MyNumber
    {
        get { return (int)base["myNumber"]; }
        set { base["myNumber"] = value; }
    }
}
Define the section and populate its values in app.config. configSections must be the first section in the app.config. The same section type can be used multiple times.

XML
<configuration>
  <configSections>
    <section name="simpleCustomSection" type="Basics.ConfigurationElementSimple, Basics"/>
    <section name="simpleCustomSection2" type="Basics.ConfigurationElementSimple, Basics"/>
  </configSections>

  <simpleCustomSection myValue="abc" myNumber="1" />
<configuration>
Read content of the section in code.

C#
ConfigurationElementSimple config = (ConfigurationElementSimple)System.Configuration.ConfigurationManager.GetSection("simpleCustomSection");
config = (ConfigurationElementSimple)System.Configuration.ConfigurationManager.GetSection("simpleCustomSection2");

Custom section in app.config - complex

Define class representing the section including the collection with its items. Each item in collection must be identified with unique value.

C#
public class ConfigurationCollectionItem : ConfigurationElement
{
    [ConfigurationProperty("name", IsRequired = true, IsKey = true)]
    public string Name
    {
        get { return (string)base["name"]; }
    }

    [ConfigurationProperty("detail", IsRequired = false, IsKey = true)]
    public string Detail
    {
        get { return (string)base["detail"]; }
    }

    public string UniqueName
    {
        get { return string.Format("{0}{1}", Name, Detail); }
    }
}

public class MyConfigurationCollection : ConfigurationElementCollection
{
    public ConfigurationCollection() { }

    protected override ConfigurationElement CreateNewElement()
    {
        return new ConfigurationCollectionItem();
    }

    protected override Object GetElementKey(ConfigurationElement element)
    {
        return ((ConfigurationCollectionItem)element).UniqueName;
    }
}

public class ConfigurationElementComplex : ConfigurationSection
{
    [ConfigurationProperty("id", IsDefaultCollection = false, IsRequired = true)]
    public string Id
    {
        get { return (string)base["id"]; }
    }

    [ConfigurationProperty("myList", IsDefaultCollection = false, IsRequired = true)]
    public MyConfigurationCollection MyList
    {
        get { return (MyConfigurationCollection)base["myList"]; }
    }
}
Define the section and populate its values in app.config. configSections must be the first section in the app.config.

XML
<configuration>
  <configSections>
    <section name="complexCustomSection" type="Basics.ConfigurationElementComplex, Basics"/>
  </configSections>

  <complexCustomSection id="1">
    <myList>
      <add name="John" detail="abc" />
      <add name="Paul" detail="def" />
    </myList>
  </complexCustomSection>
<configuration>
Read content of the section in code

C#
ConfigurationElementComplex config1 = (ConfigurationElementComplex)System.Configuration.ConfigurationManager.GetSection("complexCustomSection");

// string s = ((ConfigurationCollectionItem)config1.MyList[0]).Name;  // access not allowed (config1.MyList[0])
foreach(var item in config1.MyList)
{
    string name = ((ConfigurationCollectionItem)item).Name;
}

12. Registry

Code example:Basics.zip [Registry.cs]


Registry class is located unter Microsoft.Win32 namespace and allows manupulation records in Windows registry system.

C#
// create or overwrite registry value
Registry.SetValue(Registry.CurrentUser.Name + @"\Software\Zbynek Cernin\Basics", "TestValue", "abc");

// read registry value 
string value = (string)Registry.GetValue(Registry.CurrentUser.Name + @"\Software\Zbynek Cernin\Basics", "TestValue", string.Empty);

// read registry value - using RegistryKey
RegistryKey basicsKey = Registry.CurrentUser.OpenSubKey(@"Software\Zbynek Cernin\Basics");
if (basicsKey != null)
{
    value = (string)basicsKey.GetValue("TestValue"); 
    value = (string)basicsKey.GetValue("TestValue1"); // does not exists - returns null
}

// test if values exists and delete it
basicsKey = Registry.CurrentUser.OpenSubKey(@"Software\Zbynek Cernin\Basics", writable: true); if (basicsKey != null) { if (basicsKey.GetValueNames().Where(s => s == "TestValue").Count() > 0) { basicsKey.DeleteValue("TestValue"); } }

13. Messages

Code example:Basics.zip [Messages.cs]


Windows messages can be used as communication mean to communicate between applications.

Windows messages are not enabled by default, you have to go to Control Panel > Programs and Features > Enable Windows Features and click on "Microsoft Message Queue Server".

Create message queue

C#
// this code has to run as Admin
System.Messages.MessageQueue.Create(@".\Private$\MyQueue");
After succesful execution the queue will be visible in Computer management, under Services and Application, Message Queuing, Private queues

Read messages synchronously

C#
if (MessageQueue.Exists(@".\Private$\MyQueue"))
{
    MessageQueue mq = new MessageQueue(@".\Private$\MyQueue");

    // send
    mq.Send("Hello " + DateTime.Now.ToString(), "Title");

    // receive
    Message msg =  mq.Receive();
};

Read messages asynchronously

C#
static void WriteAndReadMessageAsynchronously()
{
    MessageQueue mq = new MessageQueue(@".\Private$\MyQueue");

    mq.Send("Hello " + DateTime.Now.ToString(), "Title");

    mq.ReceiveCompleted += new ReceiveCompletedEventHandler(MessageReceived);
    mq.BeginReceive();
}

public static void MessageReceived(object source, ReceiveCompletedEventArgs args)
{
    MessageQueue mq = (MessageQueue)source;
    Message msg = mq.EndReceive(args.AsyncResult);
    string body = (string)msg.Body;
}

14. Activator

Code example:Basics.zip [Misc.cs]
Activator class allows creating instance of a class from its type name

C#
internal class Person
{
    internal int ID;
    internal string Name;
}

// arguments: assembly name, full class name with namespace
Person p = new Person() { ID = 1 };
object p1 = Activator.CreateInstance("Basics", "Basics.Person").Unwrap();
p = (Person)p1;

15. SecureString

Represent a string with encrypted content. MSDN Used e.g. WPF PasswordBox.

It is not possible to compare value of 2 SecureStrings directly.

C#
System.Security.SecureString NewPassword;                     
System.Security.SecureString NewPassword2;                     
                    
NewPassword.Clear();
NewPassword.AppendChar('a');

NewPassword2.Clear();
NewPassword2.AppendChar('a');

bool b = NewPassword2.Equals(NewPassword);
// b is FALSE

b = NewPassword.GetHashCode() == NewPassword.GetHashCode();
// b is FALSE
If security allows, you can convert SecureString to string

C#
IntPtr sPtr = IntPtr.Zero;
try
{
    sPtr = Marshal.SecureStringToBSTR(securePassword);

    String s1 = Marshal.PtrToStringBSTR(sPtr);

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < s1.Length; i++)
    {
        sb.Append(s1[i]);
    }
    return sb.ToString();
}
finally
{
    if (ss_bstr1_ptr != IntPtr.Zero)
    {
        Marshal.ZeroFreeBSTR(ss_bstr1_ptr);
    }
}
and compare strings.

16. Events

Event aggregator with Caliburn

Caliburn page
Caliburn EventAggregator simplifies event dispatching and subscribing. Instead of standard types like strings an integer you can send custom classes with parameters.

C#
// add reference to Caliburn.micro.dll
using Caliburn.Micro;  // this is needed in order to see PublishOnUIThread extension method
public partial class MainForm : Form 
{
   internal static readonly Caliburn.Micro.IEventAggregator _eventAggregator = new Caliburn.Micro.EventAggregator();

   public void Publish()
   {
      _eventAggregator.PublishOnUIThread("Hello from Caliburn");
      _eventAggregator.PublishOnUIThread(123);
   }
}

public partial class Form2Calibrun : Form, IHandle<string>, IHandle<int>
{
    public Form2Calibrun()
    {
        ...
        MainForm._eventAggregator.Subscribe(this);
    }

    public new void Handle(string message)
    {
        label1.Text = (string)message;
    }

    public new void Handle(int message)
    {
        label1.Text = Convert.ToString(message);
    }
}

Event aggregator with Prism

Install Prism.Core package <package id="Prism.Core" version="6.1.0" targetFramework="net45" />

It will reference to Prism.dll

C#
// define event object
public class MyStringEvent : PubSubEvent<string> { }  // string event

public class Person
{
	public int Id;
	public string Name;
}
public class MyPersonEvent : PubSubEvent<Person> { }  / 

// create aggregator class
IEventAggregator _aggregator = new EventAggregator();

// subscribe events
_aggregator.GetEvent<MyStringEvent>().Subscribe(StringEventHandler);
_aggregator.GetEvent<MyPersonEvent>().Subscribe(PersonEventHandler);

// subscribe events with filter
SubscriptionToken unsubscribeToken = _aggregator.GetEvent<MyStringEvent>()
    .Subscribe(StringEventHandler, 
	ThreadOption.UIThread, 
	keepSubscriberReferenceAlive: false, 
	filter: s => s.StartsWith("A"));


// handle events
private void StringEventHandler(string s) { ... }
private void PersonEventHandler(Person p) { ... }

// raise event
_aggregator.GetEvent<MyStringEvent>().Publish("Hello");
_aggregator.GetEvent<MyPersonEvent>().Publish(new Person() { Name = "John"} );

// unsubscribe
_aggregator.GetEvent<MyStringEvent>().Unsubscribe(StringEventHandler);
_aggregator.GetEvent<MyStringEvent>().Unsubscribe(unsubscribeToken);

17. Lazy

Lazy class can be used to create a thread safe singleton

C#
public sealed class MySingleton
 {
     private static readonly Lazy<MySingleton> lazy =
         new Lazy<MySingleton>(() => new MySingleton());
     
     public static MySingleton Instance { get { return lazy.Value; } }

     private MySingleton()
     {
     }
 } 
Alternatives without Lazy