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