Script to rescan your disks via DiskPart.
If you own a SATA HDD dock and tend to switch HDDs around or power them off when not in use. Depending on your SATA controller on your motherboard it may or may not automatically detect a HDD when you switch your SATA HDD dock back on without rebooting the machine. Windows 7 allows for this plug and play capability of SATA controllers, but some controllers don’t play nice. I find myself having to open the Computer Management Console and navigating to the Disk Management to rescan the disks so they can be accessible. After a year+ of this I finally decided to not be lazy and just write a script to do this via DISKPART. I essentially used the diskpart /s argument to pass in the diskpart RESCAN command.
RescanDisks.bat:
@ECHO OFF
TITLE Diskpart – Rescan DISKS
Diskpart /s RescanDisks.txt
ECHO. Rescanning HDDs…
RescanDisks.txt:
RESCAN
EXIT
The EXIT may not be necessary, but it shouldn’t hurt. Both files should be in the same directory.
I haven’t been able to blog much since I’m working at a full-time gig as a build engineer/developer/tester and also developing additional features for the Inventory Tool for InterConnection in Seattle.
Two methods to query SQL from C#.
Below are two methods to query the database for multiple parameters/values. The first method consists of creating a SQL connection then issuing a SQL command that calls a stored procedure. The SQL command parameters are defined and the return values are cast into the appropriate datatypes.
public static bool GetCheckin2(CheckinInfo returnCheckinInfo)
{
// Handling errors from calling method.
SqlCommand sqlCmdGetCheckin = new SqlCommand("GetCheckin", dbConnection);
sqlCmdGetCheckin.CommandType = CommandType.StoredProcedure;
// Preparing input value parameter.
sqlCmdGetCheckin.Parameters.Add("@Checkin_ID", SqlDbType.Int);
sqlCmdGetCheckin.Parameters["@Checkin_ID"].Value = returnCheckinInfo.CheckinID;
// Preparing return/output parameters.
sqlCmdGetCheckin.Parameters.Add("@DateSubmitted", SqlDbType.DateTime);
sqlCmdGetCheckin.Parameters.Add("@DateCreated", SqlDbType.DateTime);
sqlCmdGetCheckin.Parameters.Add("@DescriptionOfProblem", SqlDbType.NVarChar, 4000);
sqlCmdGetCheckin.Parameters.Add("@DescriptionOfFix", SqlDbType.NVarChar, 4000);
sqlCmdGetCheckin.Parameters.Add("@Changelist", SqlDbType.Int);
sqlCmdGetCheckin.Parameters.Add("@OwnerAlias", SqlDbType.NVarChar, 30);
sqlCmdGetCheckin.Parameters.Add("@AutoCheckin", SqlDbType.Bit);
sqlCmdGetCheckin.Parameters.Add("@MailCC", SqlDbType.NVarChar, 4000);
sqlCmdGetCheckin.Parameters.Add("@TestedBy", SqlDbType.NVarChar, 30);
// Setting output parameter direction.
sqlCmdGetCheckin.Parameters["@DateSubmitted"].Direction = ParameterDirection.Output;
sqlCmdGetCheckin.Parameters["@DateCreated"].Direction = ParameterDirection.Output;
sqlCmdGetCheckin.Parameters["@DescriptionOfProblem"].Direction = ParameterDirection.Output;
sqlCmdGetCheckin.Parameters["@DescriptionOfFix"].Direction = ParameterDirection.Output;
sqlCmdGetCheckin.Parameters["@Changelist"].Direction = ParameterDirection.Output;
sqlCmdGetCheckin.Parameters["@OwnerAlias"].Direction = ParameterDirection.Output;
sqlCmdGetCheckin.Parameters["@AutoCheckin"].Direction = ParameterDirection.Output;
sqlCmdGetCheckin.Parameters["@MailCC"].Direction = ParameterDirection.Output;
sqlCmdGetCheckin.Parameters["@TestedBy"].Direction = ParameterDirection.Output;
// Run Query
sqlCmdGetCheckin.ExecuteScalar();
returnCheckinInfo.DateSubmitted = (DateTime)sqlCmdGetCheckin.Parameters["@DateSubmitted"].Value;
returnCheckinInfo.DateCreated = (DateTime)sqlCmdGetCheckin.Parameters["@DateCreated"].Value;
returnCheckinInfo.DescProb = (string)sqlCmdGetCheckin.Parameters["@DescriptionOfProblem"].Value;
returnCheckinInfo.DescFix = (string)sqlCmdGetCheckin.Parameters["@DescriptionOfFix"].Value;
if (sqlCmdGetCheckin.Parameters["@Changelist"].Value == DBNull.Value)
returnCheckinInfo.Changelist = -1;
else
returnCheckinInfo.Changelist = (int)sqlCmdGetCheckin.Parameters["@Changelist"].Value;
returnCheckinInfo.Username = (string)sqlCmdGetCheckin.Parameters["@OwnerAlias"].Value;
returnCheckinInfo.AutoCheckin = (bool)sqlCmdGetCheckin.Parameters["@AutoCheckin"].Value;
returnCheckinInfo.MailCC = (string)sqlCmdGetCheckin.Parameters["@MailCC"].Value;
returnCheckinInfo.TestedBy = (string)sqlCmdGetCheckin.Parameters["@TestedBy"].Value;
dbConnection.Close();
return true;
}
Note that the SqlCommand.ExecuteScalar() is used instead of SqlCommand.ExecuteNonQuery(). The former is appropriate for queries while the latter is for other actions such as DELETE, INSERT, and UPDATE. You may get an exception if you use the later for a SELECT type statement or if the SPROC doesn’t return a value.
ALTER PROCEDURE dbo.GetCheckin ( @Checkin_ID int, @DateSubmitted datetime OUTPUT, @DateCreated datetime OUTPUT, @DescriptionOfProblem nvarchar(4000) OUTPUT, @DescriptionOfFix nvarchar(4000) OUTPUT, @Changelist int OUTPUT, @OwnerAlias varchar(30) OUTPUT, @AutoCheckin bit OUTPUT, @MailCC nvarchar(2000) OUTPUT, @TestedBy nvarchar(30) OUTPUT ) AS BEGIN -- Select values: SELECT @DateSubmitted=DateSubmitted FROM Checkin WHERE Checkin_ID = @Checkin_ID SELECT @DateCreated=DateCreated FROM Checkin WHERE Checkin_ID = @Checkin_ID SELECT @DescriptionOfProblem=DescriptionOfProblem FROM Checkin WHERE Checkin_ID = @Checkin_ID SELECT @DescriptionOfFix=DescriptionOfFix FROM Checkin WHERE Checkin_ID = @Checkin_ID SELECT @Changelist=Changelist FROM Checkin WHERE Checkin_ID = @Checkin_ID SELECT @OwnerAlias=OwnerAlias FROM Checkin WHERE Checkin_ID = @Checkin_ID SELECT @AutoCheckin=AutoCheckin FROM Checkin WHERE Checkin_ID = @Checkin_ID SELECT @MailCC=MailCC FROM Checkin WHERE Checkin_ID = @Checkin_ID SELECT @TestedBy=TestedBy FROM Checkin WHERE Checkin_ID = @Checkin_ID END
Note if any dataypes are incorrectly defined in the SPROC or DAL, you may get a String[n]: the Size property has an invalid size of 0. Below is the second method, which uses a SqlDataReader object to store all the return values from the locally defined SELECT statement.
public static bool GetCheckin(CheckinInfo returnCheckinInfo)
{
string sqlStatement = "Select * FROM Checkin WHERE Checkin_ID = @Checkin_ID";
SqlCommand sqlCmdGetCheckin = new SqlCommand(sqlStatement, dbConnection);
sqlCmdGetCheckin.CommandType = CommandType.Text;
sqlCmdGetCheckin.Parameters.Add("@Checkin_ID", SqlDbType.Int);
sqlCmdGetCheckin.Parameters["@Checkin_ID"].Value = returnCheckinInfo.CheckinID;
SqlDataReader objSqlDR = sqlCmdGetCheckin.ExecuteReader();
objSqlDR.Read();
returnCheckinInfo.DateSubmitted = (DateTime)objSqlDR["DateSubmitted"];
returnCheckinInfo.DateCreated = (DateTime)objSqlDR["DateCreated"];
returnCheckinInfo.DescProb = (string)objSqlDR["DescriptionOfProblem"];
returnCheckinInfo.DescFix = (string)objSqlDR["DescriptionOfFix"];
string s = objSqlDR["Changelist"].ToString();
if (objSqlDR["Changelist"] == DBNull.Value)
returnCheckinInfo.Changelist = -1;
else
returnCheckinInfo.Changelist = (int)objSqlDR["Changelist"];
returnCheckinInfo.Username = (string)objSqlDR["OwnerAlias"];
returnCheckinInfo.AutoCheckin = (bool)objSqlDR["AutoCheckin"];
returnCheckinInfo.MailCC = (string)objSqlDR["MailCC"];
returnCheckinInfo.TestedBy = (string)objSqlDR["TestedBy"];
dbConnection.Close();
return true;
}
The second method is much less code and doesn’t require a stored procedure. The SqlReader object must have its Read() method called to populate the object with the return values of the SQL command. The second method may be used if the stored procedure called does more than just SELECT statements. The NVarchar SQL datatype is implicitly cast to the String C# datatype as long as the object is less than 4000 characters. Also it is interesting what the actual difference (performance and implementation) is between the NText/Text and the NVarChar/Varchar SQL datatypes, described here.
Calling a SPROC from C#.
I’ve been investigating a few ways to select values from a row in a SQL table. In the past I’ve used SQL statements executed directly from the ASP.NET code behind, but a more organized option is to have the code behind or Business Logic Layer (BLL) call a separate Data Access Layer (DAL) to perform the database actions. The following code calls a stored procedure for inserting the specified parameters and then returning the Checkin_ID which is the identity column of that table.
public static bool InsertCheckin(CheckinInfo checkinInfo)
{
// Handling errors from calling method.
SqlCommand insertCheckin = new SqlCommand("InsertCheckin", dbConnection);
insertCheckin.CommandType = CommandType.StoredProcedure;
// Preparing return value parameter.
insertCheckin.Parameters.Add("@Checkin_ID", SqlDbType.Int);
insertCheckin.Parameters["@Checkin_ID"].Direction = ParameterDirection.ReturnValue;
// Preparing insert parameters.
insertCheckin.Parameters.Add("@DateSubmitted", SqlDbType.DateTime);
insertCheckin.Parameters["@DateSubmitted"].Value = DateTime.Now;
insertCheckin.Parameters.Add("@DateCreated", SqlDbType.DateTime);
insertCheckin.Parameters["@DateCreated"].Value = checkinInfo.DateCreated;
insertCheckin.Parameters.Add("@DescriptionOfProblem", SqlDbType.NVarChar, 4000);
insertCheckin.Parameters["@DescriptionOfProblem"].Value = checkinInfo.DescProb;
insertCheckin.Parameters.Add("@DescriptionOfFix", SqlDbType.NVarChar, 4000);
insertCheckin.Parameters["@DescriptionOfFix"].Value = checkinInfo.DescFix;
insertCheckin.Parameters.Add("@Changelist", SqlDbType.Int);
if (checkinInfo.Changelist <= 0)
insertCheckin.Parameters["@Changelist"].Value = DBNull.Value;
else
insertCheckin.Parameters["@Changelist"].Value = checkinInfo.Changelist;
insertCheckin.Parameters.Add("@OwnerAlias", SqlDbType.VarChar, 30);
insertCheckin.Parameters["@OwnerAlias"].Value = checkinInfo.Username;
insertCheckin.Parameters.Add("@AutoCheckin", SqlDbType.Bit);
insertCheckin.Parameters["@AutoCheckin"].Value = checkinInfo.AutoCheckin;
insertCheckin.Parameters.Add("@MailCC", SqlDbType.NVarChar, 2000);
insertCheckin.Parameters["@MailCC"].Value = checkinInfo.MailCC;
insertCheckin.Parameters.Add("@TestedBy", SqlDbType.NVarChar, 30);
insertCheckin.Parameters["@TestedBy"].Value = checkinInfo.TestedBy;
insertCheckin.ExecuteNonQuery();
checkinInfo.CheckinID = (int)insertCheckin.Parameters["@Checkin_ID"].Value;
dbConnection.Close();
return true;
}
Below is the stored procedure that is called:
ALTER PROCEDURE dbo.InsertCheckin
(
-- @Checkin_ID int,
@DateSubmitted datetime,
@DateCreated datetime,
@DescriptionOfProblem nvarchar(4000),
@DescriptionOfFix nvarchar(4000),
@Changelist int,
@OwnerAlias varchar(30),
@AutoCheckin bit,
@MailCC nvarchar(2000),
@TestedBy nvarchar(30)
) AS
-- Begining whole transaction.
--BEGIN TRANSACTION
-- INSERT the new Checkin
INSERT INTO Checkin(DateSubmitted, DateCreated, DescriptionOfProblem, DescriptionOfFix, Changelist, OwnerAlias, AutoCheckin, MailCC, TestedBy)
VALUES(@DateSubmitted, @DateCreated, @DescriptionOfProblem, @DescriptionOfFix, @Changelist, @OwnerAlias, @AutoCheckin, @MailCC, @TestedBy)
-- Rollback the transaction if there were any errors
--IF @@ERROR <> 0
-- BEGIN
-- Rollback the transaction
-- ROLLBACK
-- Raise an error and return
-- RAISERROR('Error in inserting Checkin.', 16, 1)
--END
-- Return the Checkin_ID of the newly inserted record
RETURN SCOPE_IDENTITY()
--COMMIT
The commented out code is for additional actions if needed, which should be carried out via transaction.
Refurbisher Inventory Tool
I was recently contacted by a local non-profit PC refurbisher, that I used to work with in my last group. I developed a tool that helped to inventory the PCs that they were refurbishing. I called it the Refurbisher Inventory Tool. It consisted of pulling hardware information like processor type/speed, RAM, HDD size, and a lot of other WMI retrievable information from barebones PCs and uploading that information to a backend. The inventory was managed and tracked through an ASP.NET site. It had authentication and authorization configurations, custom business functions, and reporting. Essentially, a technician boots from CD or flash drive then serializes all hardware information to xml which is then uploaded to a intermediate server that does other business functions. Some scripts run on the intermediate server that uploads the information to the inventory database. The whole project was proof of concept, but it does address an inventory need that all refurbishers have.
The refubisher had wanted to share this tool with other non-profit refurbishers. I told the owner that he could try a couple of contacts, but I doubt that the company had any plans to bring this particular tool to the market.
It would be a nice feature add to have machines boot via network and be inventoried.
Here is a sample of what hardware information is pulled:
</pre> <?xml version="1.0" encoding="UTF-8" ?> <Computer SerialNumber="R000037250"> <Audio> </Audio> <HDD> <BytesPerSector>512</BytesPerSector> <Capabilities>3, 4</Capabilities> <CapabilityDescriptions></CapabilityDescriptions> <Description>QUANTUM FIREBALLlct10 10</Description> <InterfaceType></InterfaceType> <Manufacturer></Manufacturer> <Model></Model> <SectorsPerTrack>63</SectorsPerTrack> <Size>10256924160</Size> <TotalCylinders>1247</TotalCylinders> <TotalHeads>255</TotalHeads> <TotalSectors>20033055</TotalSectors> <TotalTracks>317985</TotalTracks> <TracksPerCylinder>255</TracksPerCylinder> <InstallDate></InstallDate> <PNPDeviceID>IDE\DISKQUANTUM_FIREBALLLCT10_10________________A03.0900\3738303230313233343030322020202020202020</PNPDeviceID> <DeviceID>\\.\PHYSICALDRIVE0</DeviceID> </HDD> <RAM> <TotalPhysicalMemory>132620288</TotalPhysicalMemory> <DIMMCapacity>134217728</DIMMCapacity> <Caption>Physical Memory</Caption> <DataWidth>64</DataWidth> <Description>Physical Memory</Description> <DeviceLocator>A0</DeviceLocator> <FormFactor>8</FormFactor> <InterleaveDataDepth>0</InterleaveDataDepth> <InterleavePosition>0</InterleavePosition> <Manufacturer></Manufacturer> <MemoryType>0</MemoryType> <Model></Model> <Name>Physical Memory</Name> <PartNumber></PartNumber> <RAMSerialNumber></RAMSerialNumber> <Speed></Speed> <Tag>Physical Memory 0</Tag> <TotalWidth>64</TotalWidth> <TypeDetail>0</TypeDetail> <InstallDate></InstallDate> </RAM> <Motherboard> <Manufacturer> </Manufacturer> <Caption>Base Board</Caption> <Model></Model> <Name>Base Board</Name> <PartNumber></PartNumber> <Product>i815-W83627</Product> <MBSerialNumber> </MBSerialNumber> <Version> </Version> <BIOSCharacteristics>4 ,7 ,9 ,10 ,11 ,12 ,14 ,15 ,16 ,17 ,19 ,22 ,23 ,24 ,25 ,26 ,27 ,28 ,29 ,30 ,32 ,33 ,34 ,36 ,37</BIOSCharacteristics> <BIOSVersion1>IntelR - 42302e31 ,Award Modular BIOS v6.00PG</BIOSVersion1> <BIOSVersion2>IntelR - 42302e31</BIOSVersion2> <BIOSCaption>Award Modular BIOS v6.00PG</BIOSCaption> <CurrentLanguage>n|US|iso8859-1</CurrentLanguage> <Description>Award Modular BIOS v6.00PG</Description> <BIOSManufacturer>Award Software International, Inc.</BIOSManufacturer> <BIOSName>Award Modular BIOS v6.00PG</BIOSName> <ReleaseDate>20011006******.******+*** --Unexpected WMI date format--</ReleaseDate> <BIOSSerialNumber> </BIOSSerialNumber> <SMBIOSBIOSVersion>6.00 PG</SMBIOSBIOSVersion> <SMBIOSMajorVersion>2</SMBIOSMajorVersion> <SMBIOSMinorVersion>2</SMBIOSMinorVersion> <SMBIOSPresent>True</SMBIOSPresent> <TargetOperatingSystem>0</TargetOperatingSystem> <InstallDate></InstallDate> </Motherboard> <Optical> </Optical> <Processor> <Caption>x86 Family 6 Model 8 Stepping 10</Caption> <DataWidth>32</DataWidth> <Description>x86 Family 6 Model 8 Stepping 10</Description> <Architecture>0</Architecture> <MaxClockSpeed>851</MaxClockSpeed> <Family>17</Family> <L2CacheSize>256</L2CacheSize> <L2CacheSpeed>851</L2CacheSpeed> <Level>6</Level> <Manufacturer>GenuineIntel</Manufacturer> <Name>Intel(R) Pentium(R) III processor</Name> <PowerManagementSupported>False</PowerManagementSupported> <ProcessorId>0383F9FF0000068A</ProcessorId> <ProcessorType>3</ProcessorType> <Revision>2058</Revision> <Role>CPU</Role> <SocketDesignation>Socket 370</SocketDesignation> <Stepping>10</Stepping> <UniqueId></UniqueId> <UpgradeMethod>8</UpgradeMethod> <Version>Model 8, Stepping 10</Version> <ExtClock>100</ExtClock> <InstallDate></InstallDate> </Processor> <Video> <AcceleratorCapabilities></AcceleratorCapabilities> <CapabilityDescriptions></CapabilityDescriptions> <AdapterCompatibility>(Standard display types)</AdapterCompatibility> <AdapterDACType>8 bit</AdapterDACType> <AdapterRAM>1048576</AdapterRAM> <Description>Standard VGA Graphics Adapter</Description> <MaxMemorySupported></MaxMemorySupported> <Name>Standard VGA Graphics Adapter</Name> <VideoArchitecture>5</VideoArchitecture> <VideoMemoryType>2</VideoMemoryType> <VideoProcessor>Intel(R) 815 Chipset Video BIOS</VideoProcessor> <InstallDate></InstallDate> <PNPDeviceID>PCI\VEN_8086&DEV_1132&SUBSYS_11328086&REV_04\3&13C0B0C5&0&10</PNPDeviceID> <DeviceID>VideoController1</DeviceID> </Video> </Computer>
Here are some screenshots:
Malevich – Web-based Code Review
My team and I have been working on a workflow tool and one of the features is code review. We’ll be leveraging existing code review tools and leaning towards Malevich . It is based off Google’s code review tool named, Mondrian (created by Guido van Rossum – also the creator of Python). The creator of Malevich (Sergey Solyanik) has made it available here. One wonders why code review hasn’t been integrated into Visual Studio or TFS…
Casting with ‘AS’ operator.
I’m surprised that there isn’t a TryCast() in C#. I am trying to cast an object to either a Button or RadioTabButton and decide what to do in the method from there. I could use a try-catch block, but that would mean an exception must occur for the other condition, which is a performance hit.
private void PromptToSave(object sender, EventArgs e)
{
Button thisSender = sender as Button; // TryCast to see who sender is.
promptToSaveAtTabChange = false;
DialogResult result = MessageBox.Show("Save Checkin?", "Confirm", MessageBoxButtons.YesNoCancel);
if (result == DialogResult.Yes)
{
SaveFileDialog saveFileDialog1 = new SaveFileDialog();
saveFileDialog1.InitialDirectory = Path.GetDirectoryName(Application.ExecutablePath).ToString();
saveFileDialog1.Filter = "Averen Checkin Configuration Files (*.xml)|*.xml";
saveFileDialog1.FilterIndex = 1;
saveFileDialog1.RestoreDirectory = true;
DateTime dt = new DateTime();
dt = DateTime.Now.ToUniversalTime();
string parsedDT = String.Format("{0:s}", dt);
parsedDT = parsedDT.Replace("-", "");
parsedDT = parsedDT.Replace(":", "");
parsedDT = parsedDT.Replace("T", "_");
saveFileDialog1.FileName = CurrentBranchName + "_" + parsedDT + "-UTC";
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
//All Serialize();
workflowPage.Serialize(saveFileDialog1.FileName);
if (thisSender != null)
this.Close();
else
newRadioButton_Clicked(sender, e);
}
}
if (result == DialogResult.Cancel)
{
TabRadioButton newRadioButton = (TabRadioButton)oldSender;
promptToSaveAtTabChange = true;
newRadioButton.Checked = true;
tlpCurrentPage.Controls[0].Focus(); // So user can scroll after cancel without clicking.
}
if (result == DialogResult.No)
{
if (thisSender != null)
this.Close();
else
newRadioButton_Clicked(oldSender, e);
}
}
Here the condition is based off whether or not a valid cast has occurred with
Button thisSender = sender as Button; if (thisSender != null) //Do stuff
At the the beginning of the method. The ‘as’ operator will try to cast the expression (in this case the object sender) as the specified type (Button). If it fails to do so, null is returned. You do have to becareful with how the operator is used since a failed cast in this form will not throw an exception, but something like this will:
Button thisSender = sender as Button; if ( (sender as Button).Text != “Button Text”) //Do stuff
This would throw a NullReferenceException where as a normal cast – (Button)sender would throw a InvalidCastException. So depending on which cast method you use, it may make it more difficult to debug your code if there is a cast exception.
Assemblies and Reflection
In the last two weeks I’ve been getting some good experience with Reflection, Serialization, Events and Delegates, and OOP programming. Haven’t had time to update this blog.
One interesting issue I’ve ran into concerns a base class that has a virtual function which is intended to be overridden by a method in a derived class. That class is in a separate dynamically loaded assembly. When loading the assembly, it is cast into the base class and held in a container of that class.
public class WorkflowModule : UserControl
{
public virtual XmlDocument ()
{
throw new NotImplementedException();
}
}
public partial class Foo : WorkflowModule
{
public override XmlDocument ()
{
XmlDocument doc = new XmlDocument();
// Encoding = null; Will default UTF8
XmlDeclaration dec = doc.CreateXmlDeclaration("1.0", null, null);
doc.AppendChild(dec);
XmlElement root = doc.CreateElement("Foo");
doc.AppendChild(root);
XmlElement file = doc.CreateElement("File");
XmlElement filePath = doc.CreateElement("FilePath");
file.SetAttribute("Type", "Boolean");
filePath.SetAttribute("Type", "String");
if (rbUsePackFile.Checked)
{
file.Value = "true";
filePath.Value = txtPackFile.Text;
}
else
{
file. InnerText = "false";
filePath. InnerText = null;
}
doc.AppendChild(file);
doc.AppendChild(filePath);
return doc;
}
It seems when I call the Serialize() it is calling the base class implementation since the class that actually calls the Serialize function implements a foreach loop on a WorkflowModule containers. I spent an hour trying to figure out how to upcast the WorkflowModule object to the derived type. One method I tried was to store the child type in the WorkflowModule object when the derived class was loaded. I also tried Convert.ChangeType(). It wasn’t until I double checked if the assembly of the derived class was built correctly or not. It was building, but there was also an older version in the same load directory that did not have the Serialize() defined. The application was probably loading that one first and trying to call Serialize() which was not defined in that assembly.
09142009-Interviews
I had an interview on Monday with a shared services team at Microsoft. I did not sleep well at all the night before. I faced a bunch of technical questions ranging from SQL, C#, networking, and security. I was surprised that there was no whiteboard coding questions. Last I heard was that the manager was on the fence.
Today’s interview with the lead build engineer/PM for the Windows Live team consisted of previous work experience questions. There were no technical questions what so ever. The role consists of part support/dev/test/build engineer. It seemed a perfect fit for me as I have the support/dev/test experience and would love to get more into build engineering before taking on more involved SDE/STE/SDET roles. The hiring manager was pretty laid back and did a good job explaining the responsibilities. I felt a little disappointed that there were no coding or technical questions. Since the development in this project is for internal use, the coding for efficiency (O(n)) type questions were less relevant, but part of me wanted to illustrate my thought process in breaking down and coding a problem. The other part of me was thankful since coding questions vary in difficulty. I think he mostly wanted to see me due to the role’s fit to my job experience and my track history of learning new technologies. I also communicated my resourcefulness when it comes troubleshooting and developing automation/applications. For effort, I later received an offer.
I have a planned interview with Intel next week in Oregon. I plan to hear them out and just so I can say I interviewed with Intel. I also look forward to drinking with my ex-boss. I also received notification today that there is a government job dealing with UI automation testing for a ASP.NET website and that the hiring manager wants an interview. Additional perk, they are willing to go with $60/hr. Downside, its in Austin, TX.
I am most likely going to take the SDET position with Windows Live since it offers varied challenges and great build engineering experience with MSBuild, Magellian, CoreXT, BuildTracker, TFS, and other technologies.
Test Harness: Email Alert
After some research on UI test automation frameworks for ASP.NET pages I got side tracked and wondered how you could send email alerts if a test failed or if you want to send a test summary, below is my first attempt at sending an email for a failed test case:
public static class Email_Alert
{
public static bool Email(string subject, TestCase tc)
{
try
{
MailMessage email = new MailMessage();
email.From = "RIT Automation Test Harness"; //Customize for product.
email.To = "test@microsoft.com"; //Customize to test alias.
email.Subject = subject;
email.BodyFormat = MailFormat.Html;
email.BodyEncoding = System.Text.Encoding.UTF8;
email.Priority = MailPriority.High;
//Add other needed information.
email.Body = tc.ID + " failed!"
+ Environment.NewLine
+ tc.description;
SmtpMail.SmtpServer = "10.0.0.1"; //Change to correct SMTP Server.
SmtpMail.Send(email);
}
catch (Exception ex)
{
Console.WriteLine("Fatal error in sending email: " + ex.Message);
}
}
}
This hasn’t been tested since I don’t have a private SMTP server available. Should be as simple as inputting the IP of the exchange server, but additional issues could pop up over an enterprise network.
Batch Script – Sorting Numbers
One of the reasons why I invested time in C# and C++ was due how god awful difficult it is sometimes to complete even the simplest task with batch script. Sometimes we have no choice but to use batch scripting such as running in WinPE or some other limited environment. I was posed with an interesting question:
Given a list of numbers, write a batch script that can sort them in ascending order.
If this were a programming language, I’d use an array to store the list of numbers. Compare the first one to the next number. If first is smaller than next then next becomes the current number and the next the one after next. If the current number is larger than the next number, then I’d swap the two numbers in the array. Continue until end of list. This is known as the bubble sort algorithm and it is much harder than it looks to accomplish via batch script. For one, there are no array structures in batch script. It did occur to me to store the variables in text files, but the whole looping thing in batch threw me.
Here is a functional and complete bubble sort batch script which had shed more light on batch scripting.
New to me:
For /L (start, increment, end) – actual for loop
SHIFT – increments to next argument
TYPE – Reads directly from file
Read in number from text file – set /p out_num=<number%1.var










