Tag Archives: code - Page 4

EventScavenger 4.2

I recently had the ‘pleasure’ of having my EventScavenger tool being used to track the security event logs of a couple of domain controllers for a ‘full’ enterprise company. It may be temporary for us to help a specific issue but in the process I had to re-look at how the collector service (the heart of EventScavenger) performs under really high stress. In case you don’t fully appreciate it, with full auditing for successful and failed events these event logs full up within an hour or so – and they have been set to something like 100MB+! Events literally flood into the logs at (sometimes) 10000 plus events in a single minute.

Now, I’ve never had any performance problems with the collectors until now. I’ve had a single collector running on a plain old pc gathering around 50 logs or more running 24×7 for 4-5 years. In all those cases each individual event log never have more than 20MB of data in them and almost never full of data that gets overwritten within an hour or so. The problem with the domain controller event logs are the sheer volume of events written ‘all the time’ plus the collector is running on a separate pc with the sql database on a remote system that is shared with dozen of other systems. The end result is that events retrieved from the logs cannot (1) be read quickly enough and (2) written quickly enough to the database. The first problem is not something I can do anything about now – other than having a collector installed on each of the domain controllers. This is not feasible as it won’t be allowed and they are Windows Core machines anyway.

To address the second issue I did some research into how to submit batches of records to the database since doing it one by one just isn’t fast enough. I know about the new table variable in newer versions of sql server but unfortunately the server that was provided is only sql 2005 which does not support that. Fortunately it does support the xml data type even as a parameter. I found an article ‘Sending Multiple Rows to Database for Modification‘ on codeproject that specifically deals with sql 2005 that describes 2 ways to accomplish this: using a delimited string or the xml data type. The delimited option won’t work for me as it is limited to about 4000 characters and the amount of data I deal with is orders of magnitude more than that. The xml data type allows for up to 2GB in a single shot!

I did quite a few performance tests and there is a real increase in number of records that can be processed using batches – despite the possible overhead of first converting the rows to xml and then sql server converting it back to a table (in memory). Interestingly, increasing the batch size beyond a certain point (1000 as I tested) does not increase the overall throughput. It more or less stay linear. The problem with that is you increase the chance that a single failure caused by one row can make the whole batch fail. So the best is to have a batch size that is big enough to warrant the benefit but not too big so it can cause failures.

Example code

A basic example of the sql xml handling:

Declare @xml  XML
SET @xml = N’
<rs>
<r cn=”machineName” l=”logName” etc />
…
</rs>’
SELECT
T.Row.value(‘@cn’, ‘VARCHAR(255)’) MachineName,
T.Row.value(‘@l’, ‘varchar(255)’) LogName
FROM   @xml.nodes(‘/rs/r’) AS T(Row)

To generate the xml on client side (simplified code):

System.IO.MemoryStream outstream = new System.IO.MemoryStream();
using (System.Xml.XmlTextWriter wrt = new System.Xml.XmlTextWriter(outstream, Encoding.UTF8))
{

wrt.Formatting = System.Xml.Formatting.Indented;
wrt.Indentation = 1;
wrt.IndentChar = ‘ ‘;
wrt.WriteStartDocument();
wrt.WriteStartElement(“rs”);

foreach (EventLogEntry ele in eventEntries)
{

wrt.WriteStartElement(“r”);
wrt.WriteAttributeString(“cn”, machine);
wrt.WriteAttributeString(“l”, logName);

…
wrt.WriteEndElement(); //r

}

wrt.WriteEndElement(); //rs
wrt.WriteEndDocument();
wrt.Flush();

outstream.Position = 0;
InsertEventLogEntries(outstream);

}

…

public InsertEventLogEntries(System.IO.Stream xml)
{

string sql = “InsertEventLogEntries”;
System.Data.SqlTypes.SqlXml sxl = new System.Data.SqlTypes.SqlXml(xml);
SqlParameter[] parms = new SqlParameter[] {
new SqlParameter(“@xml”, SqlDbType.Xml) { Value = sxl}
};
using (SqlCommand cmnd = new SqlCommand(sql, insertConn))
{
cmnd.CommandType = CommandType.StoredProcedure;
cmnd.CommandTimeout = CommandTimeout;
cmnd.Parameters.AddRange(parms);
try
{

cmnd.ExecuteNoQuery();

}
catch { throw; }

}

So with this change it is possible to handle larger sets of inserts into the database. Now only if there was a way to make reading an event log faster…

Embedding a Form inside another control

This is something I was thinking about at the beginning of my TDI (Tabbed document interface) ‘adventure’ but I didn’t think it would be easy or possible back then. Then I came across a custom tab control (A highly configurable MDI tab control from scratch) that actually allowed you to use plain old WinForms to add to it. The control itself was written in VB and ultimately I abandoned using it but I was intrigued with how it could use a plain WinForm. It actually has some real advantages like.

  • A form you can design easier in the IDE (than a user control).
  • A form can be tested/used separately on it own without being embedded onto something else first.
  • And I had a blank thinking of more reasons…

If you try to just add a Form to another container – like a panel, tabpage etc. and try to run it you would get the error: ‘Top-level control cannot be added to a control.‘. This happens because Forms are marked as Top Level controls. Fortunately, there is a simple way around it. You need to mark the form as not being a ‘Top level control’ like this: (assuming you have a panel called panel1)

Form f = new Form();
f.TopLevel = false;
panel1.Controls.Add(f);
f.Show(); //this is needed to actually have the form display itself inside the container

That will make the form load inside the panel without run-time errors – but it would look ugly if you just leave it like that. To properly ’embed’ it you need to add some more code:

Form f = new Form();
f.TopLevel = false;
f.MdiParent = null;
f.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
f.Dock = DockStyle.Fill;
panel1.Controls.Add(f);
f.Show();

So, there you have it!

Form2 f = new Form2();
            f.TopLevel = false;

Tab control

I’ve come across a tab control that I can customize for my needs in creating a MDI window replacement. It is based on the KRBTabControl article from Karub Yaz?l?m on CodeProject.

I’ve added some built-in context menu that adds a few options for closing a tab, closing all tabs, closing all other tabs and view a list of the available tabs from a modal dialog window. The rest is pretty much the original control except for some enums that I moved around.

My version can be found here.

* Update *

I’ve added an event to the control to fire when a tab page get removed. The code to achieve this looks like this:

public event TabPageRemovedDelegate TabPageRemoved;
private void RaiseTabPageRemoved(TabPageEx tab)
{

if (TabPageRemoved != null)

TabPageRemoved(tab);

}

The ‘RaiseTabPageRemoved’ method then gets called from the main OnMouseClick plus my new context menu events to close tabs.

Multilingual applications

Living in the good old S of A it is natural for us to be bilingual or even multilingual. So what do we do if we have to create software that can/must be used by people of multiple language backgrounds? Traditionally Microsoft had the concept of resource files that you can use to create multiple versions of the same application for different languages or locales. Problem with this is once the executable has been compiled it is also fixed. What happens if you want the application to be multilingual at ‘run-time’?

Well, I’ve had to do something like this for myself. I first looked at resource files but it just did not solve the whole issue on its own. So I started creating something for myself that works for my application. Part of the solution still use .Net resource files for storing xml data that gets use in the language look up procedure. The idea is to load the text of controls and windows at the time the window open since control and window text get loaded at run-time anyway. The following is an explanation of the classes I created to facilitate my solution. It is a fully working solution and already in use inside a proper application. There may be other and even better or smarter solutions ‘out there’ but this one works for me.

LanguageHandler

This is the central class that handles all the language setting/reading etc. functionality. It encapsulate the list of ‘phrases’ used in the application and provides methods to retrieve words or phrases depending on a look-up key. Simply translating words alone is not good enough so the focus is on whole ‘phrases’.

The following is a partial view of the class without methods:

public static class LanguageHandler
{

private static List<Phrase> phraseList = new List<Phrase>();

#region Properties
private static string languageID = “en-za”;
public static Language AppLanguage {

get {

switch (languageID) {
case “af”:
return Language.Afrikaans;
case “en-za”:
return Language.English;
default:
return Language.Afrikaans; }

}
set {

switch (value) {
case Language.Afrikaans:
languageID = “af”;
break;
case Language.English:
languageID = “en-za”;
break;
default:
languageID = “en-za”;
break; }

}

public static string PhraseSource { set; get; }
#endregion

}

PhraseList.xml

The definition of the phrases can probably be stored in any data form but I choose a plain simple xml file to host the raw phrase data. This make it easy to maintain and port. It is also included into the application as a file resource although theoretically it could be located anywhere as long as the application can reach it.

The structure is also very basic as it is simply a serialized version of List<Phrase>

<?xml version=”1.0″ encoding=”utf-16″?>
<ArrayOfPhrase xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=”http://www.w3.org/2001/XMLSchema”>
<phrase id=”someId” default=”Some default”>

<phraseEntry lang=”af”>Afrikaanse waarde</phraseEntry>
<phraseEntry lang=”en-za”>English value</phraseEntry>

</phrase>
<phrase id=”someId2″ default=”Some default 2″>

…

</ArrayOfPhrase>

LoadPhrases method

Loading the data is as simple as deserializing the data. I have a helper class (not shown) to simply take the xml file and deserialize it:

#region LoadPhrases
public static void LoadPhrases()
{

if ((PhraseSource != null) && (PhraseSource.Length > 0))

phraseList = SerializationUtils.DeserializeXML<List<Phrase>>(PhraseSource);

}
#endregion

GetLanguagePhrase method

The main (and only relevant) method for retrieving stuff from the class is GetLanguagePhrase. It simply takes one parameter – the id value of the phrase required.

#region GetLanguagePhrase
public static string GetLanguagePhrase(string languagePhraseID)
{

if (phraseList.Count == 0)

LoadPhrases();

Phrase phrase = (from p in phraseList where p.LanguagePhraseID == languagePhraseID select p).FirstOrDefault();
if (phrase != null)
{

PhraseEntry phraseEntry = (from pe in phrase.Phrases where pe.LangId == languageID select pe).FirstOrDefault();
if (phraseEntry != null)

return phraseEntry.Text;

else

return phrase.DefaultValue;

}
throw new Exception(“Undefined language phrase ID”);

}
#endregion

SetControlTextAndTip methods

To make it a bit easier to set the text (and tooltips if available) of controls (my implementation use WinForms) I created a few helper methods to automatically set the text on the control based on a phrase id specified.

#region SetControlText
public static void SetControlTextAndTip(string languagePhraseID, params ToolStripItem[] tsis)
{

string text = GetLanguagePhrase(languagePhraseID);
foreach (ToolStripItem tsi in tsis)
{

tsi.Text = text;
tsi.ToolTipText = text;

}

}
public static void SetControlTextAndTip(string languagePhraseID, params Control[] crtls)
{

string text = GetLanguagePhrase(languagePhraseID);
foreach (Control crtl in crtls)
{

crtl.Text = text;

}

}
public static void SetControlTextAndTip(string languagePhraseID, params ColumnHeader[] columnHeaders)
{

string text = GetLanguagePhrase(languagePhraseID);
foreach (ColumnHeader ch in columnHeaders)
{

ch.Text = text;

}

}
#endregion

The Phrase and PhraseEntry classes

These two classes are really simple. For the purpose of this article they could be a lot simpler but since I built a separate ‘editor’ they have a bunch of attributes to make editing easier. This editor plus a few method inside LanguageHandler will have to wait for another article.

[Serializable, XmlType(“phrase”)]
public class Phrase : IComparable
{

[XmlAttribute(“id”),
Browsable(true),
CategoryAttribute(“Identifier”),
DefaultValueAttribute(“”),
DescriptionAttribute(“Phrase ID”),
ReadOnly(true)]
public string LanguagePhraseID { get; set; }
[XmlElement(“phraseEntry”),
Browsable(true),
CategoryAttribute(“Value details”),
DefaultValueAttribute(“”),
DescriptionAttribute(“Phrase entries”)]
public List<PhraseEntry> Phrases { get; set; }
[XmlAttribute(“default”),
Browsable(true),
CategoryAttribute(“Value details”),
DefaultValueAttribute(“”),
Editor(typeof(MultilineStringEditor), typeof(UITypeEditor)),
DescriptionAttribute(“Default value. Use ‘\\r\\n’ to indicate crlf.”)]
public string DefaultValue { get; set; }

#region IComparable Members
public int CompareTo(object obj)
{

Phrase otherPhrase = (Phrase)obj;
return LanguagePhraseID.CompareTo(otherPhrase.LanguagePhraseID);

}
#endregion

}

[Serializable, XmlType(“entry”), TypeConverter(typeof(PhraseEntryConverter))]
public class PhraseEntry
{

public PhraseEntry() { }
public PhraseEntry(string phraseEntryDef)
{

if (phraseEntryDef.Contains(“:”))
{

LangId = phraseEntryDef.Substring(0, phraseEntryDef.IndexOf(“:”));
Text = phraseEntryDef.Substring(phraseEntryDef.IndexOf(“:”) + 1);

}

}

[XmlAttribute(“lang”),
Browsable(true),
CategoryAttribute(“Phrase entry”),
DefaultValueAttribute(“”),
DescriptionAttribute(“Language”)]
public string LangId { get; set; }
[XmlText(),
Browsable(true),
CategoryAttribute(“Phrase entry”),
DefaultValueAttribute(“”),
Editor(typeof(MultilineStringEditor), typeof(UITypeEditor)),
DescriptionAttribute(“Text”)]
public string Text { get; set; }

public override string ToString()
{

return LangId + “:” + Text;

}

}

Using it all

Using LanguageHandler is really easy. Typically in a form’s onload event I call a private local method that sets all the texts of the controls that need to be set. There is no real performance issue to worry about since all the phrase data is stored in memory. One disadvantage is that normally the form need to be closed and reopened if the ‘language’ has change. This is not really a problem since changing the language is something that should not happen often. Technically this private LoadLanguageResources method could be ran any number of times whenever you want. The only place where I usually call it multiple times is on the ‘Options’ dialog where you choose the language being used.

If you have to use the same phrase multiple times you can always store the value in a local string variable.

The following is a short example of how to use it:

static class Program
{

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{

…
if (Properties.Settings.Default.LanguageID == “English”)

LanguageHandler.AppLanguage = Language.English;

else

LanguageHandler.AppLanguage = Language.Afrikaans;

LanguageHandler.PhraseSource = Properties.Resources.PhraseList;
LanguageHandler.LoadPhrases();
…
Form mainForm = new MainForm();
…
Application.Run(mainForm);

}

public partial class MainForm : Form
{

….

private void LoadLanguageResources()
{

Text = LanguageHandler.GetLanguagePhrase(“AppName”);
LanguageHandler.SetControlTextAndTip(“File”, fileToolStripMenuItem);
LanguageHandler.SetControlTextAndTip(“NewPerson”, newPersoonToolStripMenuItem);
…

}

private void MainForm_Load(object sender, EventArgs e)
{

LoadLanguageResources();
…

}

…

}

Summary

As you can see the solution is not really complicated. I really like simple solutions that ‘just works’. This one has proven to be working for my needs so far. I have over 340 different phrases, some are very long themselves like message box prompts.

Xml file as a database – part 4

Today’s part will be a slight deviation from what I planned last time. I got side-tracked with trying to save the xml file in a compressed format and actually managed to achieve it.

DotNetZip library

Thanks to this open source library it is possible to output the xml generated by the DataSet straight to a zip file – it is not necessary to first save the xml file and then zip it. To achieve this all you need is the following:

private void WriteToZipFile(string zipFilePath, DataSet ds)
{

MemoryStream mStream = new MemoryStream();
ds.WriteXml(mStream, XmlWriteMode.WriteSchema);
mStream.Seek(0, SeekOrigin.Begin);

if (File.Exists(zipFilePath))

File.Delete(zipFilePath);

using (ZipFile zipFile = new ZipFile(zipFilePath))
{

zipFile.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;
zipFile.AddEntry(ds.DataSetName + “.xml”, mStream);
zipFile.Save();

}

}

private Stream ReadFromZipFile(string zipFilePath, string dsName)
{

MemoryStream mStream = new MemoryStream();
using (ZipFile zipFile = new ZipFile(zipFilePath))
{

if ((from z in zipFile.Entries
where z.FileName == dsName + “.xml”
select z).Count() > 0)
{

ZipEntry ze = (from z in zipFile.Entries
where z.FileName == dsName + “.xml”
select z).First();

ze.Extract(mStream);
mStream.Seek(0, SeekOrigin.Begin);

}

}

return mStream;

}

Using it is as simple as this:

DatabaseTest dbt = new DatabaseTest(); // Typed DataSet
…
//Saving
WriteToZipFile(“SomeFileName.zip”, dbt);

//Loading
DatabaseTest dbt2 = new DatabaseTest();
dbt2.ReadXml(ReadFromZipFile(“SomeFileName.zip”, dbt.DataSetName), XmlReadMode.IgnoreSchema); //Important to ‘exclude’ the schema for a typed DataSet

Really simple hey? Since xml is very ‘compressionable’ (like most text files) you can save quite a bit on disk space.

Find a copy of it here.

WriteToZipFile(saveFileDialogXml.FileName + ".zip", dbt);

Xml file as a database – part 3

Last time I stopped with the sample code how to produce a DataSet in code and then save and load it from a file. This time I’ll just continue with the ‘typed’ DataSet version plus show what the output would look like. Interestingly, the output of both the untyped and typed versions looks (essentially) the same.

First I’ll show how the Typed DataSet version works. Just to refresh the old minds – to create a Typed DataSet you use Visual Studio’s new template for a DataSet xsd and then use the designer to add tables, columns and relationships. Then in code you can do the following (the DataSet is named ‘DatabaseTest’):

DatabaseTest dbt = new DatabaseTest();
DatabaseTest.SurnameRow surname = (DatabaseTest.SurnameRow)dbt.Surname.NewRow();
surname.Surname = “Henning”;
surname.TS = DateTime.Now;
dbt.Surname.AddSurnameRow(surname);
surname = (DatabaseTest.SurnameRow)dbt.Surname.NewRow();
surname.Surname = “Van Der Merwe”;
surname.TS = DateTime.Now;
dbt.Surname.AddSurnameRow(surname);

DatabaseTest.PersonRow pa = (DatabaseTest.PersonRow)dbt.Person.NewRow();
pa.FirstName = “Daddy Koos”;
pa.SurnameId = 1;
pa.TS = DateTime.Now;
pa.YearOfBirth = 1945;
dbt.Person.AddPersonRow(pa);

DatabaseTest.PersonRow ma = (DatabaseTest.PersonRow)dbt.Person.NewRow();
ma.FirstName = “Mommy Johanna”;
ma.SurnameId = 2;
ma.TS = DateTime.Now;
ma.YearOfBirth = 1950;
dbt.Person.AddPersonRow(ma);

DatabaseTest.PersonRow guy = (DatabaseTest.PersonRow)dbt.Person.NewRow();
guy.FirstName = “Little Koos”;
guy.SurnameId = 1;
guy.TS = DateTime.Now;
guy.YearOfBirth = 1971;
guy.FatherID = 1;
guy.MotherID = 2;
guy.ChildNo = 1;
dbt.Person.AddPersonRow(guy);

if (saveFileDialogXml.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{

if (System.IO.File.Exists(saveFileDialogXml.FileName))

System.IO.File.Delete(saveFileDialogXml.FileName);

dbt.WriteXml(saveFileDialogXml.FileName, XmlWriteMode.WriteSchema);

var people = (from p in dbt.Person
join s in dbt.Surname on p.SurnameId equals s.Id
select new
{

PersonId = p.Id,
FirstName = p.FirstName,
Surname = s.Surname

});
StringBuilder sb = new StringBuilder();
foreach (var p in people)
{

sb.AppendLine(string.Format(“{0} {1}”, p.FirstName, p.Surname));

}
MessageBox.Show(sb.ToString());

System.Diagnostics.Process pro = new System.Diagnostics.Process();
pro.StartInfo = new System.Diagnostics.ProcessStartInfo(System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.System), “notepad.exe”));
pro.StartInfo.Arguments = “\”” + saveFileDialogXml.FileName + “\””;
pro.Start();

}

This provides the same functionality as before. The output looks something like this (sorry, I’m a bit lazy to format/indend the output too much now):

<?xml version=”1.0″ standalone=”yes”?>
<DatabaseTest xmlns=”http://tempuri.org/DatabaseTest.xsd”>
<xs:schema id=”DatabaseTest” targetNamespace=”http://tempuri.org/DatabaseTest.xsd” xmlns:mstns=”http://tempuri.org/DatabaseTest.xsd” xmlns=”http://tempuri.org/DatabaseTest.xsd” xmlns:xs=”http://www.w3.org/2001/XMLSchema” xmlns:msdata=”urn:schemas-microsoft-com:xml-msdata” attributeFormDefault=”qualified” elementFormDefault=”qualified”>
<xs:element name=”DatabaseTest” msdata:IsDataSet=”true” msdata:UseCurrentLocale=”true”>
<xs:complexType>
<xs:choice minOccurs=”0″ maxOccurs=”unbounded”>
<xs:element name=”Person“>
<xs:complexType>
<xs:sequence>
<xs:element name=”Id” msdata:AutoIncrement=”true” msdata:AutoIncrementSeed=”1″ type=”xs:int” />
<xs:element name=”FirstName” type=”xs:string” />
<xs:element name=”SurnameId” type=”xs:int” />
<xs:element name=”Description” type=”xs:string” minOccurs=”0″ />
<xs:element name=”YearOfBirth” type=”xs:int” minOccurs=”0″ />
<xs:element name=”MonthOfBirth” type=”xs:unsignedByte” minOccurs=”0″ />
<xs:element name=”DayOfBirth” type=”xs:unsignedByte” minOccurs=”0″ />
<xs:element name=”TS” type=”xs:dateTime” />
<xs:element name=”FatherID” type=”xs:int” minOccurs=”0″ />
<xs:element name=”MotherID” type=”xs:int” minOccurs=”0″ />
<xs:element name=”PlaceOfBirthId” type=”xs:int” minOccurs=”0″ />
<xs:element name=”YearOfDeath” type=”xs:int” minOccurs=”0″ />
<xs:element name=”MonthOfDeath” type=”xs:unsignedByte” minOccurs=”0″ />
<xs:element name=”DayOfDeath” type=”xs:unsignedByte” minOccurs=”0″ />
<xs:element name=”PlaceOfDeathId” type=”xs:int” minOccurs=”0″ />
<xs:element name=”GenealogicalNo” type=”xs:string” minOccurs=”0″ />
<xs:element name=”ChildNo” type=”xs:unsignedByte” default=”0″ minOccurs=”0″ />
<xs:element name=”ProgenitorCode” type=”xs:string” minOccurs=”0″ />
<xs:element name=”IsProgenitor” type=”xs:boolean” minOccurs=”0″ />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name=”Surname“>
<xs:complexType>
<xs:sequence>
<xs:element name=”Id” msdata:AutoIncrement=”true” msdata:AutoIncrementSeed=”1″ type=”xs:int” />
<xs:element name=”Surname” type=”xs:string” />
<xs:element name=”TS” type=”xs:dateTime” />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name=”Place“>
<xs:complexType>
<xs:sequence>
<xs:element name=”Id” msdata:AutoIncrement=”true” msdata:AutoIncrementSeed=”1″ type=”xs:int” />
<xs:element name=”Place” type=”xs:string” />
<xs:element name=”TS” type=”xs:dateTime” />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name=”Marriage“>
<xs:complexType>
<xs:sequence>
<xs:element name=”Id” msdata:AutoIncrement=”true” msdata:AutoIncrementSeed=”1″ type=”xs:int” />
<xs:element name=”No” type=”xs:unsignedByte” default=”0″ minOccurs=”0″ />
<xs:element name=”HusbandId” type=”xs:int” />
<xs:element name=”WifeId” type=”xs:int” />
<xs:element name=”YearOfMarriage” type=”xs:int” minOccurs=”0″ />
<xs:element name=”MonthOfMarriage” type=”xs:unsignedByte” minOccurs=”0″ />
<xs:element name=”DayOfMarriage” type=”xs:unsignedByte” minOccurs=”0″ />
<xs:element name=”PlaceOfMarriageId” type=”xs:int” minOccurs=”0″ />
<xs:element name=”YearOfDivorce” type=”xs:int” minOccurs=”0″ />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
<xs:unique name=”Constraint1″ msdata:PrimaryKey=”true”>
<xs:selector xpath=”.//mstns:Person” />
<xs:field xpath=”mstns:Id” />
</xs:unique>
<xs:unique name=”Surname_Constraint1″ msdata:ConstraintName=”Constraint1″ msdata:PrimaryKey=”true”>
<xs:selector xpath=”.//mstns:Surname” />
<xs:field xpath=”mstns:Id” />
</xs:unique>
<xs:unique name=”Constraint2″>
<xs:selector xpath=”.//mstns:Surname” />
<xs:field xpath=”mstns:Surname” />
</xs:unique>
<xs:unique name=”Place_Constraint1″ msdata:ConstraintName=”Constraint1″ msdata:PrimaryKey=”true”>
<xs:selector xpath=”.//mstns:Place” />
<xs:field xpath=”mstns:Id” />
</xs:unique>
<xs:unique name=”Place_Constraint2″ msdata:ConstraintName=”Constraint2″>
<xs:selector xpath=”.//mstns:Place” />
<xs:field xpath=”mstns:Place” />
</xs:unique>
<xs:unique name=”Marriage_Constraint1″ msdata:ConstraintName=”Constraint1″ msdata:PrimaryKey=”true”>
<xs:selector xpath=”.//mstns:Marriage” />
<xs:field xpath=”mstns:Id” />
</xs:unique>
<xs:keyref name=”FK_Person_Marriage” refer=”Constraint1″>
<xs:selector xpath=”.//mstns:Marriage” />
<xs:field xpath=”mstns:HusbandId” />
</xs:keyref>
<xs:keyref name=”FK_Person_Marriage1″ refer=”Constraint1″>
<xs:selector xpath=”.//mstns:Marriage” />
<xs:field xpath=”mstns:WifeId” />
</xs:keyref>
<xs:keyref name=”FK_Person_Person1″ refer=”Constraint1″>
<xs:selector xpath=”.//mstns:Person” />
<xs:field xpath=”mstns:MotherID” />
</xs:keyref>
<xs:keyref name=”FK_Person_Surname” refer=”Surname_Constraint1″>
<xs:selector xpath=”.//mstns:Person” />
<xs:field xpath=”mstns:SurnameId” />
</xs:keyref>
<xs:keyref name=”FK_Place_Person” refer=”Place_Constraint1″>
<xs:selector xpath=”.//mstns:Person” />
<xs:field xpath=”mstns:PlaceOfBirthId” />
</xs:keyref>
<xs:keyref name=”FK_Person_Person” refer=”Constraint1″>
<xs:selector xpath=”.//mstns:Person” />
<xs:field xpath=”mstns:FatherID” />
</xs:keyref>
</xs:element>
</xs:schema>

<Person>
<Id>1</Id>
<FirstName>Daddy Koos</FirstName>
<SurnameId>1</SurnameId>
<YearOfBirth>1945</YearOfBirth>
<TS>2011-06-06T09:15:58.0021555+02:00</TS>
<ChildNo>0</ChildNo>
</Person>
<Person>
<Id>2</Id>
<FirstName>Mamma Johanna</FirstName>
<SurnameId>2</SurnameId>
<YearOfBirth>1950</YearOfBirth>
<TS>2011-06-06T09:52:51.4151521+02:00</TS>
<ChildNo>0</ChildNo>
</Person>
<Person>
<Id>3</Id>
<FirstName>Little Koos</FirstName>
<SurnameId>1</SurnameId>
<YearOfBirth>1971</YearOfBirth>
<TS>2011-06-06T09:52:51.4151521+02:00</TS>
<FatherID>1</FatherID>
<MotherID>2</MotherID>
<ChildNo>1</ChildNo>
</Person>

<Surname>
<Id>1</Id>
<Surname>Henning</Surname>
<TS>2011-06-06T09:15:57.9771555+02:00</TS>
</Surname>
<Surname>
<Id>2</Id>
<Surname>Van Der Merwe</Surname>
<TS>2011-06-06T09:52:51.4121521+02:00</TS>
</Surname>

</DatabaseTest>

As you can see the xsd is actually embedded inside the xml file. This is helpful to see the structure of the data.

Next step

The next step is to create a Data Access Layer because dealing with a DataSet directly in code is so… old fashioned… and so on. Stay tuned for the next iteration.

Check if user is in AD group

This is an action that often is required when you want to make sure a user account may access some resource only if they are part of some AD (OU) group. There are other ways to do this – if fact it is a lot easier if you use newer versions on the .Net framework (like 3.5 and later) but I had to create something that will still work with the 2.0 framework – thanks to some older 2003 servers that I have to manage.

I’m not going to explain too much details on how it works internally – the simple explanation is that I take the user account and loop through the list of groups the user is a ‘memberOf’. One tricky thing is to also cater for cross domain accounts/groups – like when you have a dev domain that also have groups but the user account is part of the live domain – or some combination like it.

A solution

The solution is a single helper class with one public method – IsUserInGroup(string userName, string groupName). Creating an instance of a class with one method is a bit of a waste so the class is a simple static class.

public static class DirectoryServicesHelper
{

private static bool useDomainName = false;

public static bool IsUserInGroup(string userName, string groupName)
{

useDomainName = userName.Contains(“\\”) || groupName.Contains(“\\”);
List<string> userGroups = GetGroupsForUser(userName);
if (userGroups.Contains(groupName.ToLower()))

return true;

return false;

}

The ‘useDomainName’ variable is there simply to indicate to other methods if they must cater for domain level details or not. Most of the rest of the code is inside the private ‘GetGroupsForUser’ method.

private static List<string> GetGroupsForUser(string pstrUser)
{

List<string> lstGroups = new List<string>();

string domain = System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain().Name;
DirectoryEntry rootEntry;
if (useDomainName && pstrUser.Contains(“\\”))
{

domain = pstrUser.Substring(0, pstrUser.IndexOf(“\\”));
pstrUser = pstrUser.Substring(pstrUser.IndexOf(“\\”) + 1);

}
rootEntry = new DirectoryEntry(“LDAP://” + domain);

using (DirectorySearcher searcher = new DirectorySearcher(rootEntry, “objectCategory=User”))
{

searcher.Filter = string.Format(“(SAMAccountName={0})”, pstrUser);
SearchResult objSR = searcher.FindOne();
if (objSR != null)
{

using (DirectoryEntry objUser = new DirectoryEntry(objSR.Path))
{

System.DirectoryServices.PropertyCollection colProperties = objUser.Properties;
PropertyValueCollection colPropertyValues = colProperties[“memberOf”];
foreach (string strGroup in colPropertyValues)
{

lstGroups.Add(GetSAMAccountName(strGroup).ToLower());

}

}

}

}
return lstGroups;

}

private static string GetDomainNameFromDE(DirectoryEntry entry)
{

if (entry == null)

return “”;

else
{

if (entry.SchemaClassName == “domainDNS”)

return entry.Properties[“Name”].Value.ToString();

else if (entry.Parent != null)

return GetDomainNameFromDE(entry.Parent);

else

return “”;

}

}

private static string GetSAMAccountName(string pstrPath)
{

DirectoryEntry objADEntry = null;
string output = “”;
objADEntry = new DirectoryEntry(“LDAP://” + pstrPath);
if (objADEntry != null)
{

if (useDomainName)

output = GetDomainNameFromDE(objADEntry) + “\\” + objADEntry.Properties[“SAMAccountName”].Value.ToString();

else

output = objADEntry.Properties[“SAMAccountName”].Value.ToString();

}
return output;

}

Summary

I’ve excluded any error checking to make the code simpler to read. For a real utility class you need to add it since AD is an unmanaged resource (as far as .Net goes) and it can throw COM+ errors as well.

With this little helper class you can simply pass it a user and group name and it will return a true or false if the user is part of the group. I’ve tested it with no domain details, partial domain details, mixed domains etc. Hopefully it can help someone else as well.

Scheduled tasks and restarting services

I recently had the ‘pleasure’ to have some of my maintenance scripts that restart some ‘Windows Services‘ stopped working since ‘other’ people installed stuff that creates new dependencies. The problem is that the normal commands like ‘net stop’ or ‘sc start’ are not aware of dependent services and simply fail to perform their task if the dependent service is still running. Additionally, the built in task engine for Windows does not really have much in the line of useful logging and error reporting. Most of the time you have to ‘guess’ what went wrong 😉

.Net to the rescue

Fortunately I knew from past experience that the .Net framework’s ServiceController class has the Stop and Start methods that do take into account the dependencies and also try to stop or start them as needed. So all you need is to create what is essentially a wrapper class to call these methods. Additionally you have full access to errors and can log them in any way you like. As a side bonus it might be useful to have this tool also handle batches of requests since you usually want to stop and start a bunch of related services at the same time.

To keep things simple the tool simple accepts one parameter that – a file name for the file that contain a list of the action ‘service name’s. e.g.

stop .\Winmgmt
start .\Winmgmt

Then it is a simple matter of reading the file line for line using File.ReadAllLines() method and using the first part of the line to see if the service must be stopped or started. I like things that are simple and ‘just works’.

The code also makes provision that it can stop/start services on other machines – provided the account it runs under has the right privileges. The rest of the code are there just for error handling and logging.

The code

static void Main(string[] args)
{

…initial stuff

string[] lines = File.ReadAllLines(args[0]);
foreach (string line in lines)
{

ProcessLine(line);
System.Threading.Thread.Sleep(new TimeSpan(0,0, waitBetweenServiceChangeSec));

}

}

private static void ProcessLine(string line)
{

string action = “”;
string machineName = “.”;
string serviceName = “”;
if (line.Split(‘ ‘).Length > 1) //make sure there is an ‘action’ and service name
{

action = line.Split(‘ ‘)[0].Trim().ToLower();
serviceName = line.Substring(line.IndexOf(‘ ‘)).Trim();
if (serviceName.IndexOf(‘\\’) > -1)
{

machineName = serviceName.Substring(0, serviceName.IndexOf(‘\\’));
serviceName = serviceName.Substring(serviceName.IndexOf(‘\\’) + 1);

}

if (action == “start”)

StartService(serviceName, machineName);

else if (action == “stop”)

StopService(serviceName, machineName);

}

}

private static void StopService(string serviceName, string machineName)
{

ServiceController sc;
ServiceControllerStatus resultingStatus = ServiceControllerStatus.Stopped;
try
{

LogProgress(string.Format(“Stopping the service {0}\\{1}”, machineName, serviceName), EventLogEntryType.Information, 2);
if (machineName == “.”)

sc = new ServiceController(serviceName);

else

sc = new ServiceController(serviceName, machineName);

if (sc.CanStop && sc.Status != ServiceControllerStatus.Stopped)
{

sc.Stop();
sc.WaitForStatus(resultingStatus, new TimeSpan(0, 0, waitTimeSec));
sc.Refresh();
if (sc.Status != resultingStatus)
{

LogProgress(string.Format(“The service {0}\\{1} did not stop properly in the time allowed!”, machineName, serviceName), EventLogEntryType.Warning, 2);

}
#if DEBUG
LogProgress(string.Format(“The service {0}\\{1} stopped sucessfully”, machineName, serviceName), EventLogEntryType.Information, 2);
#endif

}

}
catch (Exception ex)
{

LogProgress(string.Format(“Error stopping service {0}\\{1}: {2}”, machineName, serviceName, ex.Message), EventLogEntryType.Error, 2);

}

}

private static void LogProgress(string message, EventLogEntryType elet, int eventId)
{

Console.WriteLine(message);
EventLog.WriteEntry(eventLogSource, message, elet, eventId);

}

I’m not showing the StartService method as it is basically the same as the StopService method (with one or two extra ‘if’s).

Alternatives

I considered powershell (for 0.834 seconds like Data would have said) but it is only really useful if you run them manually since you have to ‘sign’ them and jump through a million hoops to get it working in a scheduled ‘hands-off’ way. Yes I know this was done to make it more ‘secure’ and avoid malicious stuff but it also makes it useless to solve a simple problem. With the amount of trouble you have to go through to get it working you might as well create a proper app like I did here to solve the issue.

Find a copy here.

Center an html table

Quick tip.

Since css is the more ‘accepted’ way to apply formatting to html I discovered that the normal styles like align:center or text-align:center does not work anymore to center an html table horizontally.

Instead to be able to do it you need to add the following css definition:

table
{

margin-left:auto; margin-right:auto;

}

That will make it work in Firefox but not IE (9 as tested). To get IE to display it properly you need to enclose the whole table inside another tag – like a div:

<div class=”centerme”>

<table>

…

</table>

</div>

plus add css for the div:

div.centerme
{

margin-left:auto; margin-right:auto;text-align:center;

}

Not pretty but it works.

WPF controls and multiple level inheritance

I’m still working on my MDI replacement idea and was trying to extend the ideas to include the WPF tabcontrol as well. Most things ported easily enough but there is one problem that came up trying to create a base class for the UserControl class that inherited from the Interface I created.

If you try to create a new WPF UserControl that inherits from the base class I created – which inherits from both WPF’s UserControl and my interface, then I get a compiler error:
“Missing partial modifier on declaration of type ‘xxx’; another partial declaration of this type exists”.

After some searching on the net (ya that old Internet thingy) I found a post on social.msdn.microsoft.com forums where the guy had similar problems. One of the responses basically explains that WPF does not support multiple inheritance. Not exactly useful. This means you cannot create base classes for WPF Windows or controls.

Thanks for this Microsoft. WPF just scored another negative point with me and a reason to stay away from it even longer. As a platform to develop applications from WPF is still too immature to be taken seriously.

ChildWindowBaseWPF