Tuesday 21 October 2008

Spending time on the train

I have been spending a lot of time on the train this week commuting into London for a database training course. During the 2 hours of commuting I have been trying to make productive use of my time by reading on the way in and listening to podcasts on the way home (when my brain is too fried to read!).

One of the .Net Rocks podcasts (episode 232) suggests blogging about some of the technical books you've read and trying to explain why they were of value to you so it might encourage others to read them too. So here goes:

Currently reading (i.e. on the train) : 
C# in Depth by Jon Skeet. Bought it after hearing his DNR appearance (episode 383) and was intrigued to find a book that explains the detailed how and why of C#, not just the simple get stuff working level material. He really concentrates on the details of the language and how it has evolved through its three major versions.

I have only read the first couple of chapters (lots to absorb and think about as I read), but have already learnt some interesting things about the language such as:
  • More about nullable types and that there is a null-coalescing operator ?? used to detect nullness and return a default value instead
INullable i = null;
int j = i ?? 10;
i = 5;
int k = i;
At the end of this, j = 10 and k = 5.
  • I have also learnt a lot about generics. I knew enough to be dangerous when using the System.Collections.Generic namespace before, but now I feel I could write a genric class with mulitple where statements.
  • I have also learnt (much to my surprise) that C# does not "pass everything by reference" - I'll let you read the book to see why!
This book fills the whole on my shelf that "Effective C#" would fill if I owned a copy. The Scott Meyers [More] Effective C++ books were two of the most important books I read when I was learning C++ in the days before Google. I am hoping this book will fill in some of the "hard-core" details of C# in the same kind of vain. I spend so much of my life with the C# compiler, it helps to know what it is doing on my behalf (and what it could do if only I knew how to ask!)

I have also recently read (last year or so) :-

Agile Software Development - Alaistair Cockburn - the book that managed to convince me that there is a reason why Agile software development seems to work. It explains some of the theory as to why people on your team behave the way they do and what can be done to influence them.

Patterns of Enterprise Application Architecture - Martin Fowler - An extremely useful and interesting book describing a set of commonly used patterns when building larger scale enterprise applications. The big brother to the gang of four patterns which look at design in the small.

xUnit Test Patterns - Gerard Meszaros - a very thought provoking book on unit testing and its associated patterns. First third is some interesting theory and then the second part is a set of patterns for unit testing. Very useful book to have around in a team trying hard to adopt TTD.



Saturday 4 October 2008

Definitive NHibernate Resources

At last I have found the definitive source of all things NHibernate, www.nhforge.org. Previously I had been using www.hibernate.org/343.html which seems to be a little out of date (latest version info is wrong as an example), but the new NHibernate community forge site seems to be bang up to date and contain a lot of very interesting community generated content.


Tuesday 16 September 2008

Windowless ActiveX controls are not supported

I recently started to see this intriguing exception message from a WinForm application that we write. After much googling, I came across a number of other blog entries suggesting a wide range of issues, none of which helped much. This is code that has worked for years, but had suddenly ceased to do so!

In the end it turns out that the underlying ActiveX dll had become unregistered on the system, so it was failing the COM initialisation process. An interestingly obscure error message for this condition, but a quick regsvr32 call later and it all worked again!


Certified ScrumMaster

I have just completed my Certified ScrumMaster training, provided by Danube, in London. An excellent course taught actually using Scrum as the delivery technique, a very efficient way of learning, i.e. by doing!

Now looking forward to rolling Scrum out to my team at work and seeing how we improve quality and productivity at the same time!


Wednesday 10 September 2008

CS1504 & MSBuild

I have just spent half a day trying to reason why VS2008 is happy to build a solution in a particular configuration, but MSBuild 2008 is not prepared to build exactly the same solution in the same configuration!

First a bit of background, we use a AssemblyInfoCommon.cs file to contain all of the common bits of the assembly information, such as copyright, dates, build numbers etc. This is included into each of our C# projects as a link. It then appears in the VS Solution Explorer and all is well.

When I was getting this strange error code of CS1504 and complaining of not being able to find the common file in the project folder, I was a little bemused because the file doesn't live there, but in the parent folder. After much faffing, I discovered that the project file had been hand edited (always a warning sign) and the link to ..\AssemblyInfoCommon.cs was actually ../AssemblyInfoCommon.cs. The incorrect /, rather than \, was the culprit, looks like MSBuild cannot handle it and bails, where as VS2008 just ignores it, so in this case it didn't appear in the solution explorer!


Friday 8 August 2008

String to Enumeration

This brief post concerns converting the string form of an enumeration back into the enum class version in C++/CLI.

There are plenty of sites around the internet that tell you how to convert the string form on an enumeration value back into the enum value in C#, but I couldn't find one in C++/CLI. The C# is as follows:

status.State = (State)Enum.Parse(GetType(State), "State String", bool ignoreCase);

The C++/CLI equivalent is

status->State = (States)Enum::Parse(States::typeid, gcnew String(cacheItem->state));

As you can see, cacheItem->state is a CString, not a System::String, so we need to quickly create a System::String for it.


Thursday 7 August 2008

Logging ADODB Recordsets

The first real post on my blog is the solution to a problem I have had recently. We use ADO (not ADO.Net) recordsets to communicate with our database (Access, yes sorry I know).

These recordsets are generated from the database tables, populated in memory by the software and then we use

recordset->UpdateBatch(ADODB::adAffectAll);


to push the changes back to Access.

We have been having problems with this and getting the very helpful "External component has thrown an exception" error.

Given that the VS debugger can't debug into the COM recordsets, it is not simple to find out what the recordset contains. The solution we settled on is to dump the contents of the recordset to the Output window using the System::Diagnostics::Trace class.

Compared to a similar operation using an ADO dataset, it is rather tedious to write out the contents of any recordset, rather than coding for a specific one.

The following code extract shows how we achived it. This code first checks to see if there are any rows in the recordset, then gets the FieldsPtr for the recordset and iterates through each field, getting the name of each.

Once this has been done, we reset the cursor back to the first row and get each field in the row. The values in the "cells" of the recordset are the good old OleVariants,so you need to determine which of the various variant type the field is, then convert it to something that .Net understands.

Finally, I have used the "|" character to separate the fields and "#" to end each line, just to be sure!

aRecordset->MoveFirst();

    FieldPtr pField;

 

 

    FieldsPtr pFields = aRecordset->GetFields();

    long numberOfColumns = pFields->GetCount();

    System::Diagnostics::Trace::WriteLine("Number of columns in set: " + numberOfColumns + ";");

    System::Diagnostics::Trace::WriteLine("Number of records in set: " + aRecordset->GetRecordCount() + ";");

 

    if (aRecordset->GetRecordCount() > 0)

    {

        for(int i = 0 ; i < numberOfColumns; i++)

        {

            variant_t index;

            index.intVal = i;

            index.vt = VT_I4;

            pFields->get_Item(index, &pField);

            BSTR bstr = pField->GetName();

            System::Diagnostics::Trace::Write(gcnew String(bstr));

            System::Diagnostics::Trace::Write("|");

        }

        System::Diagnostics::Trace::WriteLine("");

 

        aRecordset->MoveFirst();

 

        while(!aRecordset->adoEOF)

        {

 

            for(int count = 1; count < numberOfColumns; count++)

            {

                variant_t index;

                index.intVal = count;

                index.vt = VT_I4;

                pFields->get_Item(index, &pField);

                variant_t var = pField->GetValue();

 

                if (var.vt == VT_R8)

                {

                    System::Diagnostics::Trace::Write(var.dblVal);

                }

                else if (var.vt == VT_R4)

                {

                    System::Diagnostics::Trace::Write(var.fltVal);

                }

                else if (var.vt == VT_I4)

                {

                    System::Diagnostics::Trace::Write(var.lVal);

                }

                else if (var.vt == VT_BSTR)

                {

                    System::Diagnostics::Trace::Write(gcnew String(var.bstrVal));

                }

                else if (var.vt == VT_DATE)

                {

                    COleDateTime DateInfo(var);

                    System::Diagnostics::Trace::Write(gcnew String(DateInfo.Format()));

                }

                else if (var.vt == VT_NULL)

                {

                    System::Diagnostics::Trace::Write("NULL");

                }

                else if (var.vt == VT_BOOL)

                {

                    if (var.boolVal == true)

                    {

                        System::Diagnostics::Trace::Write("true");

                    }

                    else

                    {

                        System::Diagnostics::Trace::Write("false");

                    }

                }

                else

                {

                    System::Diagnostics::Trace::Write("UKNOWN VALUE TYPE");

                }

 

                System::Diagnostics::Trace::Write("|");

            }

            System::Diagnostics::Trace::WriteLine("#");

 

            aRecordset->MoveNext();

        }

    }



I'd like to thank Phil for writing the first version of this code :)


Welcome!

Welcome to Colin's Corner, my new blog now that I have decided to join the 21st Century!

I am professional software developer working mainly with VC++, C++/CLI and C# for .Net 2.0, and just starting to use Spring and NHibernate. Our main product is a older MFC application we have dragged into the .Net age by extensive use of C++/CLI. This has led me to develop a reasonable understanding of some of the more knarly problems involved in working with "mixed" code on Windows.

This blog is (hopefully) going to be some of the more difficult/interesting tit-bits of knowledge I pick up through my development activities.

Enjoy and I hope you find it useful!

Colin