Wednesday, December 17, 2008

Saving an encrypted password in the DB

One of the worst security issues is "saving a password". The best method is use a HASH (not the already broken MD5). The limitation for this method is obvious: the machine can only authenticate the user, but it can't log-on as the user to another machine.

In the case I was working on the problem was the machine needed to log-on to a different machine in some future time using this user. Which machine is unknown, what time is unknown and most of the other "password saving bypasses" are not applicable.

Bottom line I had to save the password in a DB which can be accessed by some of the users (with permissions and some protection).

I decided to use an asymmetric encryption for the obvious reason, the user inserting (or updating) the data will be able to encrypt it, but won't be able to decrypt it. The methods for the encryption and decryption are as follows:


private static string EncryptText(string data, string PublicKeyXML)
{
System.Security.Cryptography.RSACryptoServiceProvider l_RSA = new System.Security.Cryptography.RSACryptoServiceProvider();
l_RSA.FromXmlString(PublicKeyXML);
byte[] encBytes = Encoding.Default.GetBytes(data);
byte[] encData = l_RSA.Encrypt(encBytes, false);
return ByteAtoString(encData);
}

private static string DecryptText(string data, string PrivateKeyXML)
{
System.Security.Cryptography.RSACryptoServiceProvider l_RSA = new System.Security.Cryptography.RSACryptoServiceProvider();
l_RSA.FromXmlString(PrivateKeyXML);
byte[] encBytes = StringtoByteA(data);
byte[] encData = l_RSA.Decrypt(encBytes, false);
return Encoding.Default.GetString(encData);
}

There's a catch inhere, the byte-array to string conversion (and its reverse). The problem is that using encoding conversion might contain illegal characters. This "string" has to travel from one system to another, using different encoding, locales and all kinds of horrors. So I ended writing in-house "byte to string" and "string to byte" methods.

Remember those methods are NOT optimized. They are however easy to understand and maintain.

private static string ByteToString(byte bt)
{
int tmp_byte = System.Convert.ToInt16(bt);
string res = System.Convert.ToString(tmp_byte, 16); ;
while (res.Length < 2)
{
res = "0" + res;
}
return res;
}

private static string ByteAtoString(byte[] arr)
{
string res = "";
for (int i = 0; i < arr.Length; i++)
{
res += ByteToString(arr[i]);
}
return res;
}

private static byte[] StringtoByteA(string arr)
{
byte[] res = (byte[])System.Array.CreateInstance(typeof(byte), arr.Length / 2);
for (int i = 0; i < arr.Length; i++)
{
int tmp = int.Parse(arr[i].ToString(), System.Globalization.NumberStyles.AllowHexSpecifier) * 16;
i++;
tmp += int.Parse(arr[i].ToString(), System.Globalization.NumberStyles.AllowHexSpecifier);
res[i / 2] = System.Convert.ToByte(tmp);
}
return res;
}


Of course you'll need an XML key in order to use the functions, a simple generator is:

private static void GenerateKeys(out string PrivateKey, out string PublicKey)
{
System.Security.Cryptography.CspParameters persistantCsp = new System.Security.Cryptography.CspParameters();
persistantCsp.KeyContainerName = "LIRON";
System.Security.Cryptography.RSACryptoServiceProvider l_RSA = new System.Security.Cryptography.RSACryptoServiceProvider(persistantCsp);
l_RSA.PersistKeyInCsp = true;
PrivateKey = l_RSA.ToXmlString(true);
PublicKey = l_RSA.ToXmlString(false);
}


P.S. it's a good practice to delete the passwords after usage, this way a break-in would have only a limited time to occur in.

Wednesday, December 3, 2008

The methods to verify empty strings

This posting was born after reading a nice comparison in "Maximizing .NET Performance" by Nick Wienholt.

The book claims that comparing an empty string to "" or string.Empty is slower than comparing the Length to 0.
Testing this claim is one of the easiest things to do:
int j = 0;
int k = 0;
int l = 0;
string s = "";
Console.WriteLine(DateTime.Now);
for (int i = 0; i < int.MaxValue; i++)
{
if (s == "")
{
j++;
}
}
Console.WriteLine(DateTime.Now);
for (int i = 0; i < int.MaxValue; i++)
{
if (s == string.Empty)
{
k++;
}
}
Console.WriteLine(DateTime.Now);
for (int i = 0; i < int.MaxValue; i++)
{
if (s.Length == 0)
{
l++;
}
}
Console.WriteLine(DateTime.Now);
Console.ReadKey();
//Avoid the optimiser by "using" the action variables
Console.WriteLine(j);
Console.WriteLine(k);
Console.WriteLine(l);
The results are shocking at first:
1st run:
Compare to "": 32 sec
Compare to string.Empty: 51 sec
Compare the Length to 0: 26 sec
2nd run:
Compare to "": 28 sec
Compare to string.Empty: 48 sec
Compare the Length to 0: 7 sec
3rd run:
Compare to "": 28 sec
Compare to string.Empty: 48 sec
Compare the Length to 0: 7 sec

While the book claimed that comparing to "" or to string.Empty is the same, my computer "translated" the string.Empty to "" prior to the comparison. The result is a slower comparison.

The main conclusion from this experiment is:
TEST THE BLOODY THING ON YOUR PLATFORM! The book is usually tested on some platforms, but not on YOUR platform.

Thursday, October 30, 2008

Battery status with speach

After a long absence I'm back for a short while. The main problem is we're still lacking some furniture in our home (ahhm… table… ahhm) so I don't really have a place to sit and write things.
Anyway the current code is due to the most annoying yet simple things that happen. The laptop is on, you're in the kitchen / on the phone / watching TV / etc… It wasn't a long time, yet suddenly you hear that annoying sound windows closing and (hopefully) saving your data. In my wife's case it means that everything got trashed due to a bug somewhere in the M$ windows.
The solution is most simple, add a warning, something like "You have got 180 of seconds of battery time left" squeaking it's way through the speakers. Easy said, and can be achieved almost as easily.

First thing to do is get the battery status:
System.Windows.Forms.SystemInformation.PowerStatus.BatteryLifeRemaining gives the required value. There are also other values available, but for the most basic thing it's enough. There's only one pothole: "what happens when the battery is being charged?" The answer is almost as simple System.Windows.Forms.SystemInformation.PowerStatus.BatteryChargeStatus gives the charging status, note it's a bitwise data. Or use the easy way, the life remaining returns -1 when connected to the power supply.

Second thing to do is the speech. For this I've used the "M$ SpeechLib", it's a COM object. If you need the full SDK it's available in this link http://www.microsoft.com/downloads/details.aspx?FamilyId=5E86EC97-40A7-453F-B0EE-6583171B4530&displaylang=en
SpeechLib.SpVoice oVoice = new SpeechLib.SpVoice();
SpeechLib.SpFileStream cpFileStream = new SpeechLib.SpFileStream();
string Line = "Battery has " + System.Windows.Forms.SystemInformation.PowerStatus.BatteryLifeRemaining.ToString() + " seconds.";
oVoice.Speak(Line, SpeechLib.SpeechVoiceSpeakFlags.SVSFDefault);
oVoice = null;

Third thing to do is surround everything in a "nice to have" service:
private static System.Threading.Thread th;
private static void RunningThread()
{
int MainLoop = 60000;
int CriticalLoop = 5000;
int ThreshHold = 300;
while (true)
{
if ((System.Windows.Forms.SystemInformation.PowerStatus.BatteryLifeRemaining > 0) && (System.Windows.Forms.SystemInformation.PowerStatus.BatteryLifeRemaining < ThreshHold))
{
SpeechLib.SpVoice oVoice = new SpeechLib.SpVoice();
SpeechLib.SpFileStream cpFileStream = new SpeechLib.SpFileStream();
string Line = "Battery has " + System.Windows.Forms.SystemInformation.PowerStatus.BatteryLifeRemaining.ToString() + " seconds.";
oVoice.Speak(Line, SpeechLib.SpeechVoiceSpeakFlags.SVSFDefault);
oVoice = null; System.Threading.Thread.Sleep(CriticalLoop);
}else{
System.Threading.Thread.Sleep(MainLoop);
}
}
}
protected override void OnStart(string[] args)
{
th = new System.Threading.Thread(RunningThread);
th.Start();
}
protected override void OnStop()
{
th.Abort();
}

Note the OnStart and OnStop, they are the methods the OS uses in order to start and stop the service. The actual working thread must be a different thread in order for the service to work. Otherwise the service might be declared as not working (or not starting/stopping).

Fourth thing to do is add an installer class to the service:
private System.ServiceProcess.ServiceProcessInstaller processInstaller;
private System.ServiceProcess.ServiceInstaller serviceInstaller;
public BatterySVC_inst()
{
InitializeComponent();
processInstaller = new System.ServiceProcess.ServiceProcessInstaller();
serviceInstaller = new System.ServiceProcess.ServiceInstaller();
processInstaller.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
serviceInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
serviceInstaller.ServiceName = "BatterySVC";
Installers.Add(serviceInstaller);
Installers.Add(processInstaller);
}


Just remember to run the "installutil" on the resulting EXE.
P.S. I used a post-build event in order to stop, unregister and re-register.

Thursday, August 21, 2008

Active Directory – using it to check permissions

In large organizations there's a tendency to use ActiveDirectory. Recently I had to find a method to use its tree structure. The first thing to do is delve down the tree, manager to employees. The following code does that:


private static int GetChilds(string path)
{
DateTime START = DateTime.Now;
System.DirectoryServices.DirectoryEntry DE = new System.DirectoryServices.DirectoryEntry("LDAP://" + path);
System.DirectoryServices.DirectorySearcher DS = new System.DirectoryServices.DirectorySearcher(DE);
System.DirectoryServices.SearchResult res = DS.FindOne();
if (res.Properties["directreports"].Count > 0)
{
int cnt = 1;
for (int i = 0; i < res.Properties["directreports"].Count; i++)
{
cnt += GetChilds((string)(res.Properties["directreports"][i]));
}
sw.WriteLine(res.Properties["name"][0] + " - Time: " + DateTime.Now.Subtract(START).ToString() + " (" + cnt.ToString() + ")");
sw.Flush();
return cnt;
}
else
{
return 1;
}
}


Assuming that sw is a streamwriter already initialized, the code delves down and writes the results + times. If your organization has more than 100 people the CEO would kick your ass if he'll have to wait for this list (or a permission using this method of search)

So I had to find a better way to check whether USER_A is a manager of USER_B. I chose this method:

private static bool IsManagerOf(string managerName, string path)
{
System.DirectoryServices.DirectoryEntry DE = new System.DirectoryServices.DirectoryEntry("LDAP://" + path);
System.DirectoryServices.DirectorySearcher DS = new System.DirectoryServices.DirectorySearcher(DE);
System.DirectoryServices.SearchResult res = DS.FindOne();
if (managerName.ToLower() == ((string)(res.Properties["samaccountname"][0])).ToLower())
{
return true;
}
else if (res.Properties["manager"].Count == 0)
{
return false;
}
else
{
return IsManagerOf(managerName, (string)(res.Properties["manager"][0]));
}
}


This method assumes that there's only one direct-manager per user, if it's not the case, quit the company and move to a company that wants to make money.

The 3rd method I needed is for the "special users" (usually they're the ones hiding in the HR-dep). They must have full access even though they don't have any connection the other users. For this case you'll need to define a new group in the active directory and do a check like this:

private static bool IsMemberOf(string GroupName, string path)
{
System.DirectoryServices.DirectoryEntry DE = new System.DirectoryServices.DirectoryEntry("LDAP://" + path);
System.DirectoryServices.DirectorySearcher DS = new System.DirectoryServices.DirectorySearcher(DE);
System.DirectoryServices.SearchResult res = DS.FindOne();
for (int i = 0; i < res.Properties["memberof"].Count; i++)
{
string curGroup = res.Properties["memberof"][i].ToString().Split(',')[0].Substring(3);
if (curGroup.ToLower() == GroupName.ToLower())
{
return true;
}
}
return false;
}


And the method for deciding whether to give access to view a certain user looks like:

private static bool GrantAccess(string Cur_Account, string userName, string GroupName)
{
System.DirectoryServices.DirectoryEntry DE = new System.DirectoryServices.DirectoryEntry("LDAP://MyCorp"");
System.DirectoryServices.DirectorySearcher DS = new System.DirectoryServices.DirectorySearcher(DE);
DS.Filter = "samaccountname=" + Cur_Account;
System.DirectoryServices.SearchResult res = DS.FindOne();
if (IsMemberOf(GroupName, (string)(res.Properties["distinguishedname"][0])))
{
return true;
}
else
{
DS = new System.DirectoryServices.DirectorySearcher(DE);
DS.Filter = "samaccountname=" + userName;
res = DS.FindOne();
if (IsManagerOf(Cur_Account, (string)(res.Properties["distinguishedname"][0])))
{
return true;
}
}
return false;
}
}

Tuesday, July 22, 2008

XSD to class or how to read an xml based on xsd

Not a long time ago I've encountered an XML which was created based on XSD. This XML is actually a class with the properties set in the XSD. But I needed only a 20% of the class' properties.

The 1st approach is using the "xsd.exe" found in the .NET framework to create a dll (or a source code file) of a class based on the XSD. I assume this thing actually works in regular cases, but it didn't work for me, in the case I had the XSD had a node referring to another XSD. "xsd.exe" theoretically can resolve that, but in practice a human can't use it.

The 2nd approach is using Biztalk, it has all the mechanisms for this type of action, it produces a good cs file. And it feels like using a Nuke on a fly, though an annoying fly.

The 3rd approach, the one I used, is just get the data from the XML while ignoring the name spaces:


private static System.Xml.XmlNode GetNodeByName(ref System.Xml.XmlNode nd, string NodeName)
{
int i = 0;
while (i < nd.ChildNodes.Count)
{
if (nd.ChildNodes[i].LocalName == NodeName)
{
return nd.ChildNodes[i];
}
i++;
}
return null;
}

This is the most basic function, but what do you do if you need to locate a path? Well just wrap it a bit and the result is:

private static System.Xml.XmlNode GetPath(ref System.Xml.XmlNode nd, string NodeName)
{
string cur_name = NodeName;
string path = "";
if (cur_name.IndexOf('/') > -1)
{
path = cur_name.Substring(cur_name.IndexOf('/') + 1);
cur_name = cur_name.Substring(0, cur_name.IndexOf('/') - 1);
}
int i = 0;
while (i < nd.ChildNodes.Count)
{
if (nd.ChildNodes[i].LocalName == NodeName)
{
if (path == "")
{
return nd.ChildNodes[i];
}
else
{
System.Xml.XmlNode tmp = nd.ChildNodes[i];
System.Xml.XmlNode res = GetPath(ref tmp, path);
if (res != null)
{
return res;
}
}
}
i++;
}
return null;
}

In the case I needed it for using the "path" approach was too expensive, I had to fetch 6 values from the same node with a 3 levels path. So I decided to fetch the node once for each level, and hitting the last level 6 times in a row for 6 different nodes.


If you have to use it a lot (i.e. it's not just get the 6 values and dispose it) you might consider rebuilding the XML as a namespace-free XML, the technique is similar, but requires more coding, and I don't really see the benefit I'll be making from it.

Sunday, July 6, 2008

Editing a generic table using ASP .NET datagrid

The reason I'm writing this article is very simple:

"I haven't found an existing solution to this damn problem!"

What I did find are bits and pieces of code in VB and C#, referring to the events of the datagrid.

The first thing to do is add the datagrid to the aspx. Note: you'll need to hook the events and change a few properties. I did it in this way:

<asp:GridView ID="Main_grd" runat="server" AllowPaging="true" AllowSorting="true" AutoGenerateDeleteButton="true" AutoGenerateEditButton="true" Width="100%" AutoGenerateSelectButton="false" OnRowDeleting="Main_grd_Deleting" OnPageIndexChanging="Main_grd_Paging" OnRowEditing="Main_grd_Editting" OnSorting="Main_grd_Sorting" OnRowCancelingEdit="Main_grd_CancelingEdit" OnRowUpdating="Main_grd_Updating"> </asp:GridView>

Now comes the "interesting" part, which events get the whole data, and which get only partial. Unfortunately the update is the most problematic, but it will wait for a while.

The easiest to implement is "Paging", the code behind is:

string sql = "SELECT * FROM " + Request["tablename"];
Main_grd.DataSource = GetDatasetFromQuery(sql);
Main_grd.PageIndex = e.NewPageIndex;
Main_grd.DataBind();


The sorting is supposed to almost identical. I've implemented it manually, though the reasons to do so are unclear. The implementation uses 2 hidden fields, one to indicate the direction (Ascending / Descending) and the other to identify the field

string sql;
if ((LastFilter_hid.Value == e.SortExpression) && LastDirection_hid.Value == "DESC")
{
sql = "SELECT * FROM " + Request["tablename"] + " ORDER BY " + e.SortExpression + " ASC";
LastDirection_hid.Value = "ASC";
}
else
{
sql = "SELECT * FROM " + Request["tablename"] + " ORDER BY " + e.SortExpression + " DESC";
LastDirection_hid.Value = "DESC";
}
Main_grd.DataSource = GetDatasetFromQuery(sql);
LastFilter_hid.Value = e.SortExpression;
Main_grd.DataBind();


Next comes the editing

MakeWhereClause(e.NewEditIndex);
string sql = "SELECT * FROM " + Request["tablename"] + " ORDER BY " + LastFilter_hid.Value + " " + LastDirection_hid.Value;
Main_grd.DataSource = GetDatasetFromQuery(sql);
Main_grd.EditIndex = e.NewEditIndex;
Main_grd.DataBind();


Note the first line. In the update event there are fields for OldValues and NewValues, but in a generic case they don't update. Leaving a nice "Null Reference" exception for the poor programmer who needs them. In order to by pass it, I did the "where clause" on the edit event, put it in a hidden field, and used it on the update. I've also used the same "where" in the delete.

private string GetValue(System.Web.UI.WebControls.WebControl ctrl)
{
string res = "";
switch (ctrl.GetType().ToString())
{
case "System.Web.UI.WebControls.TextBox":
res = "'" + ((System.Web.UI.WebControls.TextBox)ctrl).Text + "'";
break;
case "System.Web.UI.WebControls.CheckBox":
if (((System.Web.UI.WebControls.CheckBox)ctrl).Checked)
{
res = "1";
}
else if (!((System.Web.UI.WebControls.CheckBox)ctrl).Checked)
{
res = "0";
}
break;
default:
res = "";
break;
}
if ((res == "") || (res == "''"))
{
res = "NULL";
}
return res;
}

private void MakeWhereClause(int RowNum)
{
System.Text.StringBuilder update_cmd = new System.Text.StringBuilder();
for (int i = 0; i < Main_grd.Rows[RowNum].Cells.Count; i++)
{
string FieldName = ((System.Web.UI.WebControls.DataControlFieldCell)(((System.Web.UI.WebControls.TableRow)(Main_grd.Rows[RowNum])).Cells[i])).ContainingField.SortExpression;
string txt = Main_grd.Rows[RowNum].Cells[i].Text;
if ((txt != null) && (txt != "") && (txt.ToLower() != " "))
{
update_cmd.Append(" AND " + FieldName + "='" + txt + "'");
}
}
if (update_cmd.ToString() != "")
{
Where_hid.Value = update_cmd.ToString().Substring(5);
}
else
{
Where_hid.Value ="";
}
}


The delete looks like

MakeWhereClause(e.RowIndex);
System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand("DELETE FROM " + Request["tablename"] + " WHERE " + Where_hid.Value, GetConnection());
try
{
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
AddErrorMessage(ex.Message);
}
string sql = "SELECT * FROM " + Request["tablename"] + " ORDER BY " + LastFilter_hid.Value + " " + LastDirection_hid.Value;
Main_grd.DataSource = GetDatasetFromQuery(sql);
Main_grd.EditIndex = -1;
Main_grd.DataBind();


And the update looks like


bool HasChanges = false;
System.Text.StringBuilder update_cmd = new System.Text.StringBuilder();
update_cmd.AppendLine("UPDATE " + Request["tablename"] + " ");
for (int i = 1; i < Main_grd.Rows[e.RowIndex].Cells.Count; i++)
{
string txt = GetValue((System.Web.UI.WebControls.WebControl)(Main_grd.Rows[e.RowIndex].Cells[i].Controls[0]));
string FieldName = ((System.Web.UI.WebControls.DataControlFieldCell)(((System.Web.UI.WebControls.TableRow)(Main_grd.Rows[e.RowIndex])).Cells[i])).ContainingField.SortExpression;
if (Where_hid.Value.IndexOf(FieldName + "=" + txt) < 0)
{
if (HasChanges)
{
update_cmd.Append(", " + FieldName + "=" + txt);
}
else
{
update_cmd.Append("SET " + FieldName + "=" + txt);
HasChanges = true;
}
}
}
if (HasChanges)
{
update_cmd.AppendLine(" WHERE (");
update_cmd.AppendLine(Where_hid.Value);
update_cmd.AppendLine(")");
System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand(update_cmd.ToString(),GetConnection());
try
{
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
AddErrorMessage(ex.Message);
}
}
string sql = "SELECT * FROM " + Request["tablename"] + " ORDER BY " + LastFilter_hid.Value + " " + LastDirection_hid.Value;
Main_grd.DataSource = GetDatasetFromQuery(sql);
Main_grd.EditIndex = -1;
Main_grd.DataBind();


Canceling the edit mode is simply by setting the edit-index to -1. Just remember to erase the "where" or you might get an unexpected result.

The code assumes that it's already initialized in some way, that's because I did the initialization in the page_load. One can implement it in whichever way he likes, just remember to do so.

I hope there aren't too many errors, I used M$ Word to write this, and even god gave up on understanding this bloody software.

Monday, June 30, 2008

.NET conversion

This post comes after a LOOOOOOOONG silence. The reason is quite simple, I moved to a new job and currently I spend most of the time trying to figure out what did the previous programmer do. In some cases it's more like "why hadn't anybody shot him down while he was young."

But I did manage to do a small and annoying task, that's what this post about:)

Conversion of .NET1.1 to .NET 2.0

After converting 2 solutions from the .NET 1.1 to .NET 2.0 I've discovered a few nice things:

The 1st one is no matter how hard you try to avoid it, at the end you'll have to convert the solution project by project. Even if M$ claims it's not needed.

2nd because of new checks in the compilation the amount of warnings grows. In addition there are warnings about the conversion itself; why convert when you can annoy the programmer with a silly message like "you should x.x.y() instead of x.y()."

3rd issue is the moving of some code files. This thing is relevant for web applications and maybe for things I didn't need. I'm doing programs for actual users, not for the sake of M$'s revenues. The splitting of the forms to 2 files results in form.aspx and form.aspx.cs, just like before with one exception. The cs is now located next to the aspx, it goes to the App_Code only on "publish" with the compile at runtime. It would be OK if the need to re-copy the old app wasn't needed every now and then during the conversion. The result for me was a meaningless error message complaining on dupes. Luckily enough I managed to delete it after a few hours of chasing my tail trying to figure it out.

Hopefully I'll be able to write the post on dynamic javascript soon, it'll happen as soon as I get the thing to work...

Sunday, April 27, 2008

This post is a bit strange. I'm moving from one company to another, decided to work a bit closer to home (45 minutes drive is better than 1:30...)
The result is: "No new projects for me to work on". The old projects are running quite smoothly, thus leaving me time to "pass the knowledge" (probably to the wall), and conduct researches.

The results of such a research are usually some "good-to-knows", but not always. In some cases it's the "WHY???", annoy us/post that/were you born at all... After encountering this type of post in CodeProject I decided to show 2 very ugly coding I've seen, both in the same program. I'm writing an explanation on why it's bad coding, and how to write the same thing correctly.

1. Re-throwing an exception:
We all know how to write a try/catch block, the problem is what do we do with the exception? The code I've seen had this:

private void DoSomeCommands(...) {
try {
Do_Some_Commands...
} catch (Exception ex) {
MessageBox.Show(ex.Message);
throw ex; } }

This behavior is strange, but acceptable in the debugging phase, use something like:
#ifdef DEBUG
MessageBox.Show(ex.Message);
#endif

The problem was with the way the function was called:
...
for (...) {
try {
DoSomeCommands(...);
} catch {
continue; }
... }
...

The excetion is being thrown inside the same class, this is called bad programming. In addtion the thrown exception is discarded. One can hardly get any lower. The correct code would look like:

private void DoSomeCommands(...) {
Do_Some_Commands... }

for (...) {
try {
DoSomeCommands(...);
...
#ifdef DEBUG
} catch (Exception ex) {
MessageBox.Show(ex.Message);
#else
} catch {
#endif
} }
...

It might look a bit strange at first, but this way it has all the features the previous code had with the extra of not showing the message in the real-world.

2. Letting the application "hang".
Cursor=Cursors.WaitCursor;
try {
LongProcess(...)
} finally {
Cursor=Cursors.Default; }

Is there any problem adding the following block to any command that takes more than 2 seconds? No, yet there are too many released codes that ignore that. For those who forget to add it (me for example), use a friend, it's the first thing any user notices. The second is the fact that no screen update is issued.

P.S. the program in question is doing a network mapping of the computers, a long and nasty process on a big net with "dead computers". Unfortunately it's a must from time to time.

Sunday, April 13, 2008

Reflection aka Dynamic Loading

In this post I'm showing the simplest usage of the system.reflection (forgive me for the case sensitivity, it's too problematic when there are a few editors open.)

The scenario is simple: a need to use a DLL, but not to use any real binding to it. In my work the need arose when we weren't sure which server has which version.
The simplest solution (from the programmer's point of view) is: "Use the latest version everywhere." Unfortunately, that's a "NO GO" due to the backwards compatibility tests needed.
The 2nd option is use reflection. How do you do that?

//This short code runs over the DLL's object-types, printing the basic details
System.Reflection.Assembly a = System.Reflection.Assembly.LoadFrom(args[0]);
foreach (Type tp in a.GetTypes())
{
Console.WriteLine(tp.FullName);
foreach (System.Reflection.MemberInfo inf in tp.GetMembers())
{
Console.WriteLine("(" + inf.MetadataToken.GetType().Name + ") " + inf.Name + " - " + inf.MemberType.ToString());
}
}


Usually we already know those details, but it's good to check the type of member you're gonna' access. There are some differences between accessing a variable and a property, methods certainly have a different approach and you know the rest...

How do I just use the bloody reflection?
1. Load the DLL:
System.Reflection.Assembly a = System.Reflection.Assembly.LoadFrom(args[0]);

2. Instantiate an object:
Type t = a.GetType(args[1]);
object o = Activator.CreateInstance(t);

NOTE: use the example program to find the Type if you encounter problems with the gettype.

3. Run a method:
object[] par = new object[] { false };
t.InvokeMember("A_Method", System.Reflection.BindingFlags.Default | System.Reflection.BindingFlags.InvokeMethod, null, o, par);


Note:
You can use t.GetProperty("A_Property").GetValue(o, null); or t.GetField("A_Field").GetValue(o); to get properties' (fields') values. Set value has an additional field for the set value.

Tuesday, April 1, 2008

Reporting Services - The quick way to hell

Usually I post some code, not this time.
The main reason is simlpe: It didn't work.

I tried implementing a line chart inside an ASP .NET page. Simple isn't it? Well, not if you want changes of the formats at runtime.

First thing was the series' colors, after putting the code into the rdlc (as M$ explained in some article) we had to remove it. IT WAS TOO SLOW, no sane man would look at a page rendering for 30 seconds.

Leaving the colors behind I got the second thing. The X-Axis should be formatted dynamically. Another no biggy, not when it comes to M$. Finding the locations was easier than finding the locations of the first thing, but it's by far less feature rich. The format is set during the design time and stays. Unfortunately a month needs a different format than a day.
Solution: use different reports and load the correct report at runtime, NO-GO, it doesn't refresh the format.

To tell the truth, this thing is the most annoying job I had to do recently. Mainly because it's poor documents and the changing requirements from my boss.

Bottom line
DAMN I WASTED A DAY BECAUSE MY BOSS THOUGHT HE KNOWS BETTER,
now comes the harder part: explaining to his boss that it's his fault and not mine ("you should have said it n+1 times...")

Thursday, March 13, 2008

Deletion Web Service

After a long time of being off the blog I'm returning with a nice thingie:
A web service to delete files from the server.

This version is the most basic one. Meaning: it doesn't do any checks for permissions, no massive error handling and almost nothing else. That's the reason it works.

So to the code. First thing is the deletion procedure, DeleteFile, which returns true upon success and false upon failure. The err is used in order to return a better description of the error when failing to do the job.

private static bool DeleteFile(string Filename, out string err)
{
err = "";
try
{
if (System.IO.File.Exists(Filename))
{
System.IO.File.Delete(Filename);
return true;
}
else
{
err = "file does not exist!";
return false;
}
}
catch (Exception ex)
{
err = ex.Message;
return false;
}
}

And now for the access point the web method (note the header)
[WebMethod]
public System.Xml.XmlDocument DeleteMedias(System.Xml.XmlDocument doc)
{
foreach (System.Xml.XmlNode nd in doc.SelectNodes("/files/file"))
{
if (nd.SelectSingleNode("filename") != null)
{
System.Xml.XmlNode result = doc.CreateNode(System.Xml.XmlNodeType.Element, "result", "");
string err;
if (DeleteFile(nd.SelectSingleNode("filename").InnerText, out err))
{
result.InnerText = "success";
}
else
{
result.InnerText = "error: " + err;
}
nd.AppendChild(result);
}
}
return doc;
}

It's almost self-explaining, pass an XML in this format:
<files>
<file>
<filename>
c:\xxx\yyy.zzz
</filename>
</file>
</files>

and the reply would be:
<files>
<file>
<filename>
c:\xxx\yyy.zzz
</filename>
<result>
success
</result>
</file>
</files>

This is no rocket science, actually this is a 15 minutes job that was done in order to replace a way too heavy code block.

Wednesday, February 13, 2008

File Splitting

This splitter has been built because I didn't find a decent one in 5 minutes search. The reason behind a splitter is simple: Send files as an attachment (in my case using the gmail & gspace).
As for the code, I'm posting the full program here, and some highlights afterwards.

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

namespace FileSplitter
{
class Program
{
const int DefalutBlockSize = 1000000;
private static byte[] STAMP = { 1, 2, 3 };

private static byte[] GetMD5(byte[] buf)
{
System.Security.Cryptography.MD5CryptoServiceProvider md = new System.Security.Cryptography.MD5CryptoServiceProvider();
md.Initialize();
return md.ComputeHash(buf);
}
private static void Split(string Filename, int BlockSize)
{
try
{
Console.WriteLine("Splitting " + Filename + " to " + BlockSize.ToString() + " bytes parts");
int part = 0;
byte[] buf;
System.IO.StreamReader in_fl = new System.IO.StreamReader(Filename);
System.IO.BinaryReader in_b = new System.IO.BinaryReader(in_fl.BaseStream);
while (true)
{
Console.WriteLine("Reading part " + part.ToString());
buf = in_b.ReadBytes(BlockSize);
if (buf.Length == 0)
{
in_b.Close();
Console.WriteLine("No more parts");
return;
}
Console.WriteLine("Writing part " + part.ToString());
System.IO.StreamWriter out_fl = new System.IO.StreamWriter(Filename + "." + part.ToString());
System.IO.BinaryWriter out_b = new System.IO.BinaryWriter(out_fl.BaseStream);
out_b.Write(STAMP);
byte[] MD = GetMD5(buf);
byte len = System.Convert.ToByte(MD.Length);
out_b.Write(len);
out_b.Write(MD);
out_b.Write(buf);
out_b.Flush();
out_b.Close();
part++;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
private static void Merge(string Filename)
{
try
{
Console.WriteLine("Merging " + Filename);
int part = 0;
byte[] buf;
System.IO.StreamWriter out_fl = new System.IO.StreamWriter(Filename);
System.IO.BinaryWriter out_b = new System.IO.BinaryWriter(out_fl.BaseStream);
while (System.IO.File.Exists(Filename + "." + part.ToString()))
{
Console.WriteLine("Reading part " + part.ToString());
System.IO.StreamReader in_fl = new System.IO.StreamReader(Filename + "." + part.ToString());
System.IO.BinaryReader in_b = new System.IO.BinaryReader(in_fl.BaseStream);
buf = in_b.ReadBytes(STAMP.Length);
for (int i = 0; i < STAMP.Length; i++)
{
if (buf[i] != STAMP[i])
{
Console.WriteLine("Incorrect STAMP!");
Environment.Exit(2);
}
}
byte len = in_b.ReadByte();
byte[] file_MD = in_b.ReadBytes(System.Convert.ToInt32(System.Convert.ToInt32(len)));
buf = in_b.ReadBytes(System.Convert.ToInt32(in_b.BaseStream.Length));
byte[] calc_MD = GetMD5(buf);
for (int i = 0; i < calc_MD.Length; i++)
{
if (file_MD[i] != calc_MD[i])
{
Console.WriteLine("Incorrect HASH!");
Environment.Exit(3);
}
}
out_b.Write(buf);
out_b.Flush();
Console.WriteLine("Wrote part " + part.ToString());
part++;
}
out_b.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
private static void ShowUsage()
{
Console.WriteLine("Usage: FileSplitter.exe -Filename <filename> [-blocksize <size>] [-stamp <file stamp>] {-split | -merge}");
Console.WriteLine("filename - the original filename");
Console.WriteLine("size - the size of the splitted parts");
Console.WriteLine("stamp - the header of the splitted parts");
Console.WriteLine("Parameters can be used more than once in the string, each split/merge is ran with the \"current\" values");
}
static void Main(string[] args)
{
Console.WriteLine("File splitting / merging utility by Liron");
Console.WriteLine("This utility is available as is.\r\n");
string Filename = "";
int BlockSize = DefalutBlockSize;
for (int i = 0; i < args.Length; i++)
{
switch (args[i].ToLower())
{
case "-stamp":
i++;
STAMP = new byte[args[i].Length];
for (int j = 0; j < args[i].Length; j++)
{
STAMP[j] = System.Convert.ToByte(args[i][j]);
}
break;
case "-filename":
i++;
Filename = args[i];
break;
case "-blocksize":
i++;
BlockSize = System.Convert.ToInt32(args[i]);
break;
case "-split":
Split(Filename, BlockSize);
break;
case "-merge":
Merge(Filename);
break;
case "-h":
ShowUsage();
Environment.Exit(0);
break;
default:
ShowUsage();
Environment.Exit(255);
break;
}
}
}
}
}


Some highlights
Note the way the HASH is added. The resulting file is in the following structure:
[STAMP][HASH length][HASH][Original part]
This structure allows an easy reconnection of the parts, plus a check for errors.
Another thing related to the structure is the fact that the resulting size would be BLOCKSIZE + HEADERSIZE which is slightly beigger than the intended size. It can be fixed quite easily by reading BLOCKSIZE - HEADERSIZE bytes instead of BLOCKSIZE bytes. I didn't need it so I left it out.

The most important note: This code has the minimal checks, if you want to use it, you'll need to add some checks (users can do many strange things if there's no check)

Wednesday, February 6, 2008

C# vs VBScript

After writing another VBScript script for our System I began wondering what's the difference in performance between a script and a compiled program.
So I began by writing a nice VBScript that does heavy calculation (I used the calculation of n! for n=1 to 100) Using a normal computer it takes almost no time, well, repeat it for 10000 times... The script I wrote is:
Option Explicit

Call Main()

Sub Main()
Dim StartingTime
Dim EndingTime

StartingTime = Now()
Call DoHardWork(100)
EndingTime = Now()
WScript.Echo "Started at: " & StartingTime
WScript.Echo "Finished at: " & EndingTime
WScript.Echo "Total time: " & DateDiff("s",StartingTime,EndingTime) & " sec"
End Sub

Sub DoHardWork(MaxI)
Dim i
Dim j

For j = 1 To 10000
For i = 1 To MaxI
Call RecursiveCalc(i)
Next
Next
End Sub

Function RecursiveCalc(CurNum)
If curnum = 1 Then
RecursiveCalc = 1
Else
RecursiveCalc = CurNum * RecursiveCalc (CurNum - 1)
End If
End Function


Dumb, does the job using no cache and other enhancements. The script took 107s on my computer. I felt this is a good benchmark, I'll write a simple C# program and run it. How fast can it be? The C# program I wrote is almost an exact copy of the VBS:
using System;
using System.Collections.Generic;
using System.Text;

namespace TimeCheck
{
class Program
{
static void Main(string[] args)
{
DateTime StartingTime = DateTime.Now;
DoHardWork(100);
DateTime EndingTime = DateTime.Now;
Console.WriteLine("Started at: " + StartingTime.ToString());
Console.WriteLine("Finished at: " + EndingTime.ToString());
Console.WriteLine("Total time: " + EndingTime.Subtract(StartingTime).ToString());
}

static void DoHardWork(int MaxI)
{
for (int j = 1; j < 10000; j++)
{
for (int i = 1; i < MaxI; i++)
{
RecursiveCalc(i);
}
}
}

static long RecursiveCalc(int CurNum)
{
if (CurNum == 1)
{
return 1;
}
else
{
return CurNum * RecursiveCalc(CurNum - 1);
}
}
}
}


The results are: 1.57464s for the "Debug" compilation.
The "Release" took 1.023516s. If anyone needs a reminding of the difference between programs and scripts.

P.S. if anyone wants to do the same test in another language, please send me the results, I'd like to add more to the results chart in the future.

Sunday, January 20, 2008

File Stream and WebService

As usual the need arises for a better and securer way to transfer the files across a net. In the case I'm dealing with it's a server sending file to the clients, with the extra requirement of being able to "put the files for the client".
Meaning:
1. The client connects to the server and requests which updates should it do.
2. The client downloads the updates.
3. The client notifies the server that everything is OK.

Pretty simple when put this way, getting it from the customer is like taking a pin out of the ass using boxing-gloves.

In order to support more than a few clients, actually I need to support a few hundreds of them, I decided to use a DB. Since I don't want the server to be crippled, but I want the DB to be a fileDB, I went for the FireBird a good fileDB. For those who think about mdb, I'll remind you I want the server to run without human-maintenance every other day.
In general there's a table for the stations, the files and a connection table.

Next build the WebService:
There are basically 3 methods for the service:
1. GetUpdatesList(string Station)
2. GetFile(string fileID)
3. CompareFileHashes(string fileID, string Hash)

In order to add security the GetFile implements encryption (and the client implements the decryption.)
The CompareFileHashes is also an indication that a transfer was completed, and knowing the quality of the lines I'll use it's an important check.

Of course once you get to the actual writing the 3 methods get a dozen support methods which are hidden, the client application has to implement some safeguarding, and the whole system has to implement a logging mechanism in order for the future debugging / trouble locating. As I've already stated the hardware I'll have to put it on is shaky at best.

P.S. There's no code in this post, I didn't find a reason to put the code behind the methods or the creation of the tables. It seems almost obvious, but if I'll find something interesting while debugging this, I'll put it in a new post with the methods code (well, the closest I'm allowed anyway...)

Wednesday, January 9, 2008

Debugging a playlist

I'm back:) And married.

My current project involves the famous "Windows Media Services" (WMS). Mainly what I had to do is devise a way to play a continuos list while the list is being updated.
That thing can be easily achieved using the httpd directive and refering it to an ASP/ASP .NET generated SMIL.
The problems begin when you want to debug this process... There's no way to get the actual SMIL from the publishing point, at least not by any means which we considered harmless.
There's a way to bypass it by attaching a small monitor utility to the publishing point. It's potential for strange effects has been considered too great.
The second method, the method I already implemented for debugging purposes, is save the SMIL from within the ASP.
The last option, which is the best if you can afford it, is build your own plug-in. In this case you'll need the enterprise edition (otherwise it might not function on the next version).

Once you got past the first obstacle, comes the bigger one. How do we test it? In our case it was easy. Once I made a player-wrapper for a different check, adding the "write what you played" function to it was easy. Just to be sure we're on the right truck, I added a <clientData Title="filename">. This way I got the comparison between the "Original" and the "Actual"

As a by product (mainly for later debugging usage) I made a small routine that runs over the events the server's recorded and returns them:

WMSServerLib.WMSServer Server;
Type tServerType = Type.GetTypeFromProgID("WMSServer.Server");
Server = (WMSServerLib.WMSServer)Activator.CreateInstance(tServerType);
string res = "";
WMSServerLib.IWMSDiagnosticEvents Events;
Events = Server.DiagnosticEvents;
foreach (WMSServerLib.IWMSDiagnosticEvent evnt in Events)
{
res += "Event: " + evnt.PublishingPointName + "\r\n";
res += "Time: " + evnt.Time.ToString() + "\r\n";
res += evnt.Name + " - " + evnt.ErrorCode.ToString() + "\r\n";
res += evnt.AdditionalInfo + "\r\n";
}
return res;


This code was copied from M$, and editted a bit to fit my needs at the time.