Tag Archives: .Net - Page 6

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.

Xml file as a database part 2

Been a but busy lately but I didn’t give up on the idea of an xml database yet. Actually, I started with some code even before the first post – that is in principle working but it still needs a lot of work. Unfortunately I’ve been side-tracked on other things in the last couple of days so the whole idea is just sitting idle for the moment.

First let me cover some (old) basics. The following examples are nothing new and functionality available for quite a number of years using plain old DataSets.

The first concept is how to create define or create a DataSet in code or run-time. Look at the following example:

DataSet myDb = new DataSet(“XmlDb”);
DataTable personTable = new DataTable(“Person”);

DataColumn personId = new DataColumn(“Id”, typeof(int)) { AutoIncrement = true, AutoIncrementSeed = 1, Unique = true, AllowDBNull = false };
DataColumn surnameId = new DataColumn(“Id”, typeof(int)) { AutoIncrement = true, AutoIncrementSeed = 1, Unique = true, AllowDBNull = false };
DataColumn personSurnameId = new DataColumn(“SurnameId”, typeof(int)) { AllowDBNull = false };
DataColumn fatherId = new DataColumn(“FatherID”, typeof(int));
DataColumn motherId = new DataColumn(“MotherID”, typeof(int));

personTable.Columns.AddRange(new DataColumn[]
{

personId,
new DataColumn(“Name”, typeof(string)) { AllowDBNull = false},
personSurnameId,
new DataColumn(“YearOfBirth”, typeof(int)),
new DataColumn(“MonthOfBirth”, typeof(int)),
new DataColumn(“DayOfBirth”, typeof(int)),
new DataColumn(“Description”, typeof(string)),
fatherId,
motherId

});
personTable.PrimaryKey = new DataColumn[] { personId };
myDb.Tables.Add(personTable);

DataTable surnameTable = new DataTable(“Surname”);
surnameTable.Columns.AddRange(new DataColumn[]
{

surnameId,
new DataColumn(“Surname”, typeof(string)) { Unique=true, AllowDBNull=false }

});
surnameTable.PrimaryKey = new DataColumn[] { surnameId };
myDb.Tables.Add(surnameTable);
DataRelation personSurname = new DataRelation(“PersonSurname”, surnameId, personSurnameId);
myDb.Relations.Add(personSurname);
DataRelation personFather = new DataRelation(“PersonFather”, personId, fatherId);
myDb.Relations.Add(personFather);
DataRelation personMother = new DataRelation(“PersonMother”, personId, motherId);
myDb.Relations.Add(personMother);

The piece of code above shows a couple of things (which probably includes bad programming habits as well… but it is only an example 😉 ). First it creates a DataSet with two tables. The two tables have a relationship between them that will enforce some referential integrity. Both tables have an id field that is an autonumber/identity column. The ‘Person’ table also adds two internal references (parent-child relationship). No rocket science here (yet).

The next block demonstrates how to add raw data to the DataSet.

DataRow newSurname = surnameTable.NewRow();
newSurname[“Surname”] = “Van Der Merwe”;
surnameTable.Rows.Add(newSurname);

newSurname = surnameTable.NewRow();
newSurname[“Surname”] = “Botha”;
surnameTable.Rows.Add(newSurname);

DataRow newRow = personTable.NewRow();
newRow[“Name”] = “Piet”;
newRow[“SurnameId”] = 1;
newRow[“Description”] = “Testing 123”;
personTable.Rows.Add(newRow);

newRow = personTable.NewRow();
newRow[“Name”] = “Sannie”;
newRow[“SurnameId”] = 2;
newRow[“YearOfBirth”] = 1950;
personTable.Rows.Add(newRow);

newRow = personTable.NewRow();
newRow[“Name”] = “Koos”;
newRow[“SurnameId”] = 1;
newRow[“YearOfBirth”] = 1980;
newRow[“FatherID”] = 1;
newRow[“MotherID”] = 2;
personTable.Rows.Add(newRow);

Again, no rocket science here. Two surnames are added and then 3 people. The third person reference the first two (parents).

Then if you want  to ‘persist’ the DataSet:

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

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

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

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

…

Then lastly you might want to reload it back into another DataSet to add some more data etc.

DataSet myDb2 = new DataSet();
myDb2.ReadXml(saveFileDialogXml.FileName);

personTable = myDb2.Tables[“Person”];
newRow = personTable.NewRow();
newRow[“Name”] = “Gert”;
newRow[“SurnameId”] = 1;
personTable.Rows.Add(newRow);

So there you have the basics how a simple .Net DataSet can be used to create, load, persist, reload and change data to and from an Xml file. Personally I’d like to add another step to compress (zip) the data while on disk – to make it easier to transport if needed. This will not impact functionality or performance of the running application.

One concern is of course what happens when the ‘database’ grows big because if you haven’t noticed, the entire database must be loaded/saved when you interact with the Xml file. This concept will not allow you to use extremely large databases – but then it is not intended for mission critical enterprise database systems! On the positive side – the database is ‘in memory’ so it should be very fast. Splitting the file and loading only sections will make it very tricky and possibly mean you have to wrote a lot of custom code.

It is also possible to use a Typed DataSet in exactly the same manner. The advantage then is you don’t have to define the ‘database structure’ in code. The rest works basically the same.

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.

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

MDI interface alternative

As some developers might remember there was a time when Microsoft created/used the MDI type of user interface – MDI stands for Multiple Document Interface in case you forgot. It was widely in use during the Windows 3 and 95 areas and applications like Word, Excel and even Visual Studio used it. And then Microsoft move on… apparently abandoning the use for MDI in their own applications.

Fortunately (sort of) they still support developers creating MDI interface applications in Visual Studio 2010, but for WinForms only. WPF has no native support for it. The real problem is that since Windows 7/Vista came around they don’t actually support the MDI type of interface. Easiest way to see this is that MDI child windows are not rendered with the current Windows theme but instead shown with the ugly ‘default’ Windows theme. This is really a shame. There are still many applications or scenarios where this type of user interface is useful – I’ll admit there are many cases where alternative UI’s are better – like the tabs in web browsers like Firefox and lately IE. However, there is no ‘easy’ way to create applications using a tabbed interface in Visual Studio with all the supported things like Firefox or IE has. For one – dragging, moving and dropping tabs are not supported by default (as in WinForms – not sure about WPF). You cannot ’embed’ child Forms inside tabs (well, actually it might theoretically work as both ‘Forms’ and ‘Controls’ are internally derived from the same classes but it is going to cost some hacking…). Instead, you have to develop your entire Form as a UserControl, which is not a train smash but still…

So I’ve been digging through the Internet (electron by electron it feels like) and saw many others complain and suggesting alternatives. All the complaints aside, there are ways to do things differently but it is going to cost some work! (rolling up sleeves).

I started playing with some code to see what I can do with some trial and error. The basic approach I’m following is to use a plain old WinForm with a tab control on it. Creating tabs dynamically is easy enough in .Net. What is not so easy is the part where you must keep track of the child ‘windows’ and what is where at what time. Basically, you have to rewrite your own mini ‘Window manager’. I’ve got something working that kind of works but it doesn’t look pretty yet. It has the potential to develop into a full new MDI like framework. Keep in mind this is still work in progress and it is not complete.

IChildWindow

In order for the main or parent form to ‘communicate’ with the children and vice versa you need some standard interface. I created IChildWindow for this.

public interface IChildWindow
{

/// <summary>
/// Unique Id if used
/// </summary>
int Id { get; set; }
/// <summary>
/// Type description
/// </summary>
string Type { get; }
/// <summary>
/// Is the tab/Window pinned
/// </summary>
bool Pinned { get; set; }
/// <summary>
/// Start clean up process before closing tab/window
/// </summary>
void CloseAndCleanup();
string GetWindowTitle();
event StartCleanupDelegate StartCloseAndCleanup;
event PinnedStatusChangedDelegate PinnedStatusChanged;

}

This interface is really simple and small. The ‘Id’ and ‘Type’ properties are used to uniquely identify a ‘child window’. Actually, the ‘Id’ is only used for cases where the child can have multiple instances – say you have an editing window for persons then the ‘Id’ would be used for the person id field. In other cases you might want a window that is unique by itself in the application or reused for everything.

Pinned is used to ‘pin’ the window – if you choose ‘Close All tabs’ on the parent window it will not close the pinned tabs.

The CloseAndCleanup method is simply a way to to ensure any resources created/used on the child window gets cleaned up. GetWindowTitle is called to set the display name or text of the tab/window.

UserControlIdentifier

In order to ‘map’ type of ‘windows’ used to a name I created this simple class.

public class UserControlIdentifier
{

public string TypeName { get; set; }
public Type UserControl { get; set; }

}

It identifies the type of ‘window’ or in this case the UserControl.

WindowManager

This is a base class that manages the list of opened ‘windows’. It has methods for finding, creating, opening, and closing ‘windows’. It is generic and does not have a direct link to the type of ‘control’ used to host the new ‘window’ system. Instead, it only reference the IChildWindow interface (and UserControlIdentifier for identifying and creating windows).

The following is just a partial view of the class.

public abstract class WindowManager
{

private List<IChildWindow> windowList = new List<IChildWindow>();
protected abstract void CreateNewWindow(IChildWindow cw);
protected abstract void CloseWindow(IChildWindow cw);
protected abstract void SetWindowFocus(IChildWindow cw);
protected abstract void OnPinnedStatusChanged(IChildWindow cw);

#region Open window

public void OpenChildWindow(UserControlIdentifier uci, int id, bool reUseSimilarType)
{

IChildWindow c = FindChildWindowByTypeAndId(uci, id);
if (c != null)
{

SetWindowFocus(c);

}
else
{

if (reUseSimilarType)
{

c = FindFirstChildWindowOfType(uci);
if (c != null)
{

c.Id = id;
SetWindowFocus(c);
return;

}

}
//The create new window
IChildWindow newChild = CreateChildWindow(uci, id);

}

}
private IChildWindow CreateChildWindow(UserControlIdentifier uci, int id)
{

IChildWindow newChild = (IChildWindow)Activator.CreateInstance(uci.UserControl);
newChild.Id = id;
CreateNewWindow(newChild);
SetWindowFocus(newChild);
newChild.PinnedStatusChanged += new PinnedStatusChangedDelegate(OnPinnedStatusChanged);
newChild.Pinned = false; //and display the unpinned icon
windowList.Add(newChild);
return newChild;

}

#endregion

#region Close window

public void CloseChildWindow(IChildWindow cw)
{

cw.CloseAndCleanup();
windowList.Remove(cw);
CloseWindow(cw);

}

public void CloseAllChildWindows(bool forced)
{

for (int i = windowList.Count – 1; i >= 0; i–)
{

IChildWindow cw = windowList[i];
if (forced || (!cw.Pinned))
{

CloseChildWindow(cw);

}

}

}

#endregion

#region Find window
public IChildWindow FindFirstChildWindowOfType(UserControlIdentifier uci)
{

foreach (IChildWindow c in windowList)
{

if (c.Type == uci.TypeName)
{

return c;

}

}
return null;

}
public IChildWindow FindChildWindowByTypeAndId(UserControlIdentifier uci, int id)
{

foreach (IChildWindow c in windowList)
{

if (c.Type == uci.TypeName && c.Id == id)
{

return c;

}

}
return null;

}
#endregion

#region Pinned status
public void SetChildWindowPinnedStatus(IChildWindow c)
{

c.Pinned = !c.Pinned;

}
#endregion

}

WindowManagerForTabControl

This is the custom implementation of the WindowManager base class. It is created to handle the System.Windows.Forms.TabControl as ‘host’ to the ‘windows’ specifically.

/// <summary>
/// WindowManager implementation for System.Windows.Forms.TabControl
/// Note: the tab control must use an image list control with 3 images
///   image 0: fixed tab
///   image 1: unpinned tab
///   image 2: pinned tab
/// </summary>
public class WindowManagerForTabControl : WindowManager
{

private TabControl hostTabControl;
public WindowManagerForTabControl(TabControl hostTabControl)
{

this.hostTabControl = hostTabControl;

}

protected override void CreateNewWindow(IChildWindow cw)
{

TabPage tp = new TabPage(cw.GetWindowTitle());
((Control)cw).Dock = DockStyle.Fill;
tp.Controls.Add(((Control)cw));
tp.Tag = cw; //set inverse reference
hostTabControl.TabPages.Add(tp);
hostTabControl.SelectTab(tp); //and switch to it

}

protected override void CloseWindow(IChildWindow cw)
{

TabPage tp = null;
foreach (TabPage t in hostTabControl.TabPages)
{

if (t.Tag is IChildWindow && (IChildWindow)t.Tag == cw)
{

tp = t;

}

}
if (tp != null)

hostTabControl.TabPages.Remove(tp);

}

protected override void SetWindowFocus(IChildWindow cw)
{

TabPage tp = null;
foreach (TabPage t in hostTabControl.TabPages)
{

if (t.Tag is IChildWindow && (IChildWindow)t.Tag == cw)
{

tp = t;

}

}
if (tp != null)
{

hostTabControl.SelectTab(tp);
tp.Text = cw.GetWindowTitle();

}

}

protected override void OnPinnedStatusChanged(IChildWindow cw)
{

TabPage tp = null;
foreach (TabPage t in hostTabControl.TabPages)
{

if (t.Tag is IChildWindow && (IChildWindow)t.Tag == cw)
{

tp = t;

}

}
if (tp != null)
{

if (cw.Pinned)

tp.ImageIndex = 1;

else

tp.ImageIndex = 2;

}

}

public IChildWindow GetChildWindowFromTabPage(TabPage tabPage)
{

if (tabPage.Tag is IChildWindow)

return (IChildWindow)tabPage.Tag;

else

return null;

}

}

If you want to use another control or something like WPF’s tab control you would create a similar class for those types.

GlobalChildWindowController

To use all of this you need a singular place to reference the window manager implementation. In order to make it possible to reference this functionality globally in the application it is hosted in a single static class – GlobalChildWindowController.

This is really the ‘run-time’ control center of the implementation. Here the different types of windows get defined and used. The ‘hosting’ main form where the tab control is initialize this class.

public static class GlobalChildWindowController
{

private static WindowManagerForTabControl cwm;
public static void SetHostTabControl(TabControl hostTabControl)
{

cwm = new WindowManagerForTabControl(hostTabControl);

}

//Example window type
public static string PersonWindowTypeName = “Person”;
public static UserControlIdentifier PersonWindowType = new UserControlIdentifier()
{

TypeName = PersonWindowTypeName,
UserControl = typeof(PersonEditControl)

};

public static void OpenPersonWindow(int id, bool reUseCurrent)
{

cwm.OpenChildWindow(GlobalChildWindowController.PersonWindowType, id, reUseCurrent);

}

…

internal static void CloseChildWindow(TabPage tabPage)
{

if (tabPage.Tag != null && tabPage.Tag is IChildWindow)
{

cwm.CloseChildWindow((IChildWindow)tabPage.Tag);

}

}

}

With this class you can make the following call any place in the application:

GlobalChildWindowController.OpenPersonWindow(123);

If there is no existing ‘PersonEdit’ window open one will be opened with Id 123. If you need to have a window reused you the overload OpenPersonWindow(123,true).

ChildWindowBase

Now, to develop one of the ‘client windows’ or in this case a UserControl, I created a base class that inherits from IChildWindow and System.Windows.Forms.UserControl to make it easier to implement (otherwise you’d have to redo all the code for all controls you create.

public class ChildWindowBase : UserControl, IChildWindow
{

#region IChildWindow Members
[System.ComponentModel.Browsable(false)]
public int Id { get; set; }
[System.ComponentModel.Browsable(true),
System.ComponentModel.ReadOnly(true)]
public virtual string Type { get { return “Remember to set this value in derived class!”; } set { } }

public virtual string GetWindowTitle()
{

return string.Format(“{0}”, Type);

}
private bool pinned = false;
[System.ComponentModel.Browsable(true)]
public bool Pinned
{

get { return pinned; }
set
{

pinned = value;
RaisePinnedStatusChanged();

}

}

public event PinnedStatusChangedDelegate PinnedStatusChanged;
private void RaisePinnedStatusChanged()
{

if (PinnedStatusChanged != null)

PinnedStatusChanged(this);

}

public event StartCleanupDelegate StartCloseAndCleanup;
private void RaiseWindowClosing()
{

if (StartCloseAndCleanup != null)
{

StartCloseAndCleanup(this);

}

}
public virtual void CloseAndCleanup()
{

RaiseWindowClosing();

}
#endregion

}

To create a user control you do this:

public partial class SomeChild : ChildWindowBase
{

public SomeChild()
{

InitializeComponent();

}

public override string Type
{

get
{

return GlobalChildWindowController.SomeChildTypeName;

}
set
{

base.Type = value;

}

}

}

Conclusion

As mentioned before, this is a work-in-progress development and may not be perfect – if ever 🙂

Try it out. You can get a preview of it here.

The example shows a person view/edit window that can have multiple instances plus some others that only have a single instance. There is also a dialog box example simulating a search function. Additionally I played with a WPF example that is hosted inside an ElementHost container.

string GetWindowTitle();

HTMLWriter on CodePlex

I’ve decided to move the source code to Codeplex to have it online. This helps with me not having to keep the latest copy on my web site plus it is version controlled.

The Codeplex project is: http://htmlwriter.codeplex.com

But keep on reading my blog… 😉

HTMLWriter 1.3

This is another iteration of my HTMLWriter library. A few new methods  have been added and a whole lot of code comments added for those that like documentation 😉

A new constructor has been added for those that want to enable auto formatting from the beginning.

public HTMLWriter(string documentTitle, bool enableAutoFormatting) : base(documentTitle)
{

AutoFormatting = enableAutoFormatting;
AutoIndentation = enableAutoFormatting;

}

Two new variants of the AppendTagEnd method have been added to make life easier.

public HTMLWriter AppendTagEnd(int tagCountToClose)
{

for (int i = 0; i < tagCountToClose; i++)
{

AppendTagEnd();

}
return this;

}

public HTMLWriter AppendAllEndTagsUntil(string tagName)
{

while (tags.Count > 0)
{

string tagNameToPop = tags.Pop();
AppendTagEndInternal(tagNameToPop, false);
if (tagNameToPop == tagName)

break;

}
return this;

}

Additionally I added the EscapeText method to help format html text properly for characters that might need ‘escaping’.

Find version 1.3 here.

Escaping text in HTML

Thought I just share this little function on its own. I created a simple method that escape (convert) a string to make it ‘safe’ for html display.

public static string EscapeText(string text)
{

string escapeChars2 = @”<>`´àáâãäåèéêëìíîïñòóôõö÷øùúûüýÀÁÂÃÄÅÈÉÊËÌÍÎÏÑÒÓÔÕÖרÙÚÛÜÝ“;

if (string.IsNullOrEmpty(text))

return “”;

else if (text.IndexOfAny(escapeChars2.ToCharArray()) == -1)

return text;

else
{

return text
.Replace(“<“, “&lt;”)
.Replace(“>”, “&gt;”)
.Replace(“&”, “&amp;”)
.Replace(“`”, “&#96;”)
.Replace(“´”, “&acute;”)
.Replace(“à”, “&agrave;”)
.Replace(“á”, “&aacute;”)
.Replace(“â”, “&acirc;”)
.Replace(“ã”, “&atilde;”)
.Replace(“ä”, “&auml;”)
.Replace(“å”, “&aring;”)
.Replace(“è”, “&egrave;”)
.Replace(“é”, “&eacute;”)
.Replace(“ê”, “&ecirc;”)
.Replace(“ë”, “&euml;”)
.Replace(“ì”, “&igrave;”)
.Replace(“í”, “&iacute;”)
.Replace(“î”, “&icirc;”)
.Replace(“ï”, “&iuml;”)
.Replace(“ñ”, “&ntilde;”)
.Replace(“ò”, “&ograve;”)
.Replace(“ó”, “&oacute;”)
.Replace(“ô”, “&ocirc;”)
.Replace(“õ”, “&otilde;”)
.Replace(“ö”, “&ouml;”)
.Replace(“÷”, “&divide;”)
.Replace(“ø”, “&oslash;”)
.Replace(“ù”, “&ugrave;”)
.Replace(“ú”, “&uacute;”)
.Replace(“û”, “&ucirc;”)
.Replace(“ü”, “&uuml;”)
.Replace(“ý”, “&yacute;”)
.Replace(“ÿ”, “&#255;”)
.Replace(“À”, “&Agrave;”)
.Replace(“Á”, “&Aacute;”)
.Replace(“”, “&Acirc;”)
.Replace(“Ô, “&Atilde;”)
.Replace(“Ä”, “&Auml;”)
.Replace(“Å”, “&Aring;”)
.Replace(“È”, “&Egrave;”)
.Replace(“É”, “&Eacute;”)
.Replace(“Ê”, “&Ecirc;”)
.Replace(“Ë”, “&Euml;”)
.Replace(“Ì”, “&Igrave;”)
.Replace(“Í”, “&Iacute;”)
.Replace(“Δ, “&Icirc;”)
.Replace(“Ï”, “&Iuml;”)
.Replace(“Ñ”, “&Ntilde;”)
.Replace(“Ò”, “&Ograve;”)
.Replace(“Ó”, “&Oacute;”)
.Replace(“Ô”, “&Ocirc;”)
.Replace(“Õ”, “&Otilde;”)
.Replace(“Ö”, “&Ouml;”)
.Replace(“×”, “&times;”)
.Replace(“Ø”, “&Oslash;”)
.Replace(“Ù”, “&Ugrave;”)
.Replace(“Ú”, “&Uacute;”)
.Replace(“Û”, “&Ucirc;”)
.Replace(“Ü”, “&Uuml;”)
.Replace(“Ý”, “&Yacute;”);

}

}

There are other ways to do the string matching – like Regular Expressions, but they add some other overhead for a method that does something simple like this.