I was just reading an article on Builder.com about exception handling and it reminded me of some of my pet peeves regarding exception handling.

  1. Don't assume you have permissions to write to the Event Log! - Many people add code to log exceptions to the Event Log however they do not add any exception handling in case it fails. This is especially true for ASP.NET as many corporate networks are severely restricting permissions. It is possible for the System.Diagnostics.EventLog.WriteEntry() to throw exceptions! The exception for permissions issue is System.Security.SecurityException.
    • Best practice is to use a wrapper function write the information. This also makes it possible to abstract which event log to write to instead of hard-coding to System.Diagnostics.EventLog. For more information search the net for classes such as LogException or use the Microsoft Patterns & Practices Exception Handling Block.
    • Use regedit to give permissions to write to the Event Log. Open regedit to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Security and right-click on the node and select Permissions. For ASP.NET add the ASPNET user if you are using Win2k3 or 'NETWORK SERVICE' for WinXP. See this blog entry for more information.
  2. Don't throw away the handled exception! - If you "re-throw" the exception or derive another exception type, do not discard the current exception information. It makes debugging applications so much more difficult without that information. Do not display the gory details to the end user, but at least provide a means to debug the application.
// Example poor exception handling from a DAL.
// Not my code, but just an example. Comments reflect code deficiencies.
// DO NOT USE CODE LIKE THIS!!!!
try
{
    SqlCommand cmd = new SqlCommand();
    cmd.Connection = SqlConn;
    cmd.CommandText = "...";
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.ExecuteNonQuery();
}
catch (Exception ee)
{
    SqlConn.Close();
    // Notice only the ee.Message is used
    // so extended details (StackTrace, etc.) are lost
    // the catch should have specified DBException first
    throw new DBException(ee.Message); // BAD! BAD! BAD!
}
// DO NOT USE CODE LIKE THIS!!!!

Updated: 2007-05-04 09:05:48 -05:00 Added comments to bad exception handling example.


Comment Section

Comments are closed.


Programming Oddities sizeof

Comments [0]

One of the strangest programming concepts that I came across was working with an embedded processor, Analog Devices SHARC ADSP-21062, on a project. I was shocked when I found out the following:

sizeof(char) == sizeof(short) == sizeof(int) == 1

I didn't think this was possible, but sure enough it was. After reviewing the C standard I realized that this was allowed and the only requirement was: sizeof(char) <= sizeof(short) <= sizeof(int).

This actually had a profound affect on a later project using the same processor family when I was porting an embedded GUI, Nano-X or formerly microwindows, to that processor. The issue was mainly with assumptions on casting to (char). This affected several areas in the source code as well as the fonts. The assumption was that casting to (char) automatically bit-masked the value to 8-bits:

result = (char) value;    // assumed 8-bit result
result = value & 0x00FF;  // explicit bitmask

The solution was to locate all casts and determine the impact and convert them to casts. I ended up using a preprocessor macro to maintain compatibility and speed where it would use the cast on normal operating systems and processors but use the bitmask on the Analog Devices processor.


Comment Section

Comments are closed.


When I was debugging communication protocols a long time ago, I would often have to convert a 32-bit integer into a 32-bit floating point number. This did not mean using atof, fscanf, _fcvt or similar function, but rather interpretting the bits as an IEEE 754 or IEC 60559:1989 floating point number. An example would be:

1.0 == 0x3F800000 (1065353216)

In the process, I wrote a tool that displays the components of the floating point number and allows you to change the raw hexadecimal bits. Here is a screen shot:

FloatScreenShot01

This tool was written with VB6 however I have always wanted to make a C++/WTL version of the tool. Maybe I will get around to it sometime. Here is a decent article describing the floating point format.

Float Tool Source: 18KB


Comment Section

Comments are closed.


One of the trickiest issues with databases and information is the storage of dates and dealing with time zones. I have to use data historians in most of my projects which means that most of the data is time-series based and not necessarily relational. Data historians (OSISoft PI, Wonderware Historian (InSQL), etc.) usually store time zone information with each data acquisition which explicitly stores the exact time zone information at the moment the data is obtained. In most relational databases, the DBA or designer does not always store time zone information along with a time stamp. The time stamp should be stored in UTC along with useful information such as the time zone of the user or server, and possibly if any daylight savings (DST) is in effect.

One of the challenges I encountered is how to retrieve data from relational databases when the time stamp is in UTC and display it to the user in his or her preferred time zone. In order to accomplish the time zone conversion, I used a code utility to process each DateTime field and convert it to the selected time zone. I used a client cookie to store the time zone in the web browser.

function checkClientTimeZone() {
    // Set the client time zone
    var dt = new Date();
    SetCookieCrumb("ClientDateTime", dt.toString());

    var tz = -dt.getTimezoneOffset();
    SetCookieCrumb("ClientTimeZone", tz.toString());

    // Expire in one year
    dt.setYear(dt.getYear() + 1);
    SetCookieCrumb("expires", dt.toUTCString());
}

The code to process the Dataset was based on a Microsoft KB article and modified to use the cookie as shown above.

/// 
/// Summary description for TimeZoneUtility. Adapted from
/// http://support.microsoft.com/default.aspx?scid=kb;en-us;842545
/// 
public static class TimeZoneUtility
{
    public static string AdjustDSTimeZone(DataSet ds)
    {
        // Obtains the time difference on the sender computer that
        // remoted this dataset to the Web service.
        string sourceTicksString = ds.ExtendedProperties["UTCDifference"].ToString();
        long sourceTicks = long.Parse( sourceTicksString );

        // Obtain the UTC offset for the remote computer.
        //DateTime baseUTC = DateTime.Now;
        //long UtcTicksLocal = TimeZone.CurrentTimeZone.GetUtcOffset( baseUTC ).Ticks;

        // Obtain the time difference between the sender computer and the remote computer.
        //long ticksDifference = sourceTicks - UtcTicksLocal;
        //TimeSpan timespan = new TimeSpan( ticksDifference );

        TimeSpan timespan = new TimeSpan( sourceTicks );

        // The following code iterates through each table, and find all the columns that are
        // DateTime columns. After identifying the columns that have to be adjusted,
        // it traverses the data in the table and adjusts the DateTime columns back to their
        // original values. You must leave the RowState of the DataRow in the same state
        // after making the adjustments.
        foreach ( DataTable table in ds.Tables )
        {
            DataColumnCollection columns = table.Columns;
            int[] ColumnNumbers = new int[columns.Count];
            int   ColumnNumbersIndex = 0;
            for ( int i = 0; i < columns.Count; i++ )
            {
                DataColumn col = columns[i];
                if ( col.DataType == typeof( DateTime ) )
                {
                    ColumnNumbers[ColumnNumbersIndex] = i;
                    ColumnNumbersIndex++;
                }
            }
            foreach ( DataRow row in table.Rows )
            {
                switch ( row.RowState )
                {
                    case DataRowState.Unchanged:
                        AdjustDateTimeValues( row, ColumnNumbers,
                            ColumnNumbersIndex, timespan );
                        row.AcceptChanges();	// This is to make sure that the
                        // row appears to be unchanged again.
                        Debug.Assert( row.RowState == DataRowState.Unchanged );
                        break;

                    case DataRowState.Added:
                        AdjustDateTimeValues( row, ColumnNumbers, ColumnNumbersIndex, timespan );
                        // The row is still in a DataRowState.Added state.
                        Debug.Assert( row.RowState == DataRowState.Added );
                        break;

                    case DataRowState.Modified:
                        AdjustDateTimeValues( row, ColumnNumbers, ColumnNumbersIndex, timespan );
                        // The row is a still DataRowState.Modified.
                        Debug.Assert( row.RowState == DataRowState.Modified );
                        break;

                    case DataRowState.Deleted:
                        //   This is to make sure that you obtain the right results if
                        //the .RejectChanges()method is called.
                        row.RejectChanges();	// This is to "undo" the delete.
                        AdjustDateTimeValues( row, ColumnNumbers, ColumnNumbersIndex, timespan );
                        // To adjust the datatime values.
                        // The row is now in DataRowState.Modified state.
                        Debug.Assert( row.RowState == DataRowState.Modified );
                        row.AcceptChanges();	// This is to mark the changes as permanent.
                        Debug.Assert( row.RowState == DataRowState.Unchanged );
                        row.Delete();
                        // Delete the row. Now, it has the same state as it started.
                        Debug.Assert( row.RowState == DataRowState.Deleted );
                        break;

                    default:
                        throw new ApplicationException
                            ( "You must add a case statement that handles the new version of the dataset." );
                }
            }
        }

        string str = sourceTicksString; // ds.Tables["MyTable"].Rows[0][1].ToString() ;
        return str;
    }

    public static void AdjustDateTimeValues(DataRow row, int[] ColumnNumbers, int columnCount, TimeSpan timespan)
    {
        for ( int i = 0; i < columnCount; i++ )
        {
            int columnIndex = ColumnNumbers[i];
            if (row[columnIndex] != DBNull.Value)
            {
                DateTime original = (DateTime)row[columnIndex];
                DateTime modifiedDateTime = original.Add(timespan);
                row[columnIndex] = modifiedDateTime;
            }
        }
    }

    /// 
    /// Returns the client (if available in cookie) or server timezone.
    /// 
    /// 
    /// 
    public static int GetTimeZoneOffset(HttpRequest Request)
    {
        TimeZone tz = TimeZone.CurrentTimeZone;
        TimeSpan ts = tz.GetUtcOffset(DateTime.Now);
        int result = (int) ts.TotalMinutes;
        HttpCookie cookie = Request.Cookies["ClientTimeZone"];
        if ((cookie != null) && AppUtility.IsNumeric(cookie))
            result = Convert.ToInt32(cookie.Value);
        return result;
    }

    /// 
    /// Returns the client (if available in cookie) or server timezone.
    /// 
    /// 
    /// 
    public static int ClientTimeZoneOffset
    {
        get
        {
            return GetTimeZoneOffset(HttpContext.Current.Request);
        }
    }

    public static TimeSpan ClientTimeZoneSpan
    {
        get
        {
            TimeSpan ts = TimeSpan.MinValue;
            HttpContext Context = HttpContext.Current;

            // Check session for client time zone
            object tz = Context.Session["ClientTimeZone"];
            if (tz != null)
            {
                ts = (TimeSpan) tz;
            }
            else
            {
                // Check for client cookie
                HttpCookie cookie = Context.Request.Cookies["ClientTimeZone"];
                if ((cookie != null) && AppUtility.IsNumeric(cookie.Value))
                {
                    int minutes = Convert.ToInt32(cookie.Value);
                    ts = new TimeSpan(0, minutes, 0);
                    Context.Session.Add("ClientTimeZone", ts);
                }
            }

            return ts;
        }
    }

    /// 
    /// Returns the server timezone.
    /// 
    /// 
    /// 
    public static int ServerTimeZoneOffset()
    {
        TimeZone tz = TimeZone.CurrentTimeZone;
        TimeSpan ts = tz.GetUtcOffset(DateTime.Now);
        int result = (int) ts.TotalMinutes;
        return result;
    }

    public static TimeSpan ServerTimeZoneSpan
    {
        get
        {
            DateTime dt = DateTime.Now;
            TimeSpan ts = TimeZone.CurrentTimeZone.GetUtcOffset(dt);
            return ts;
        }
    }

    public static object UTCtoClientTZ(object value)
    {
        object result = value;
        if (value is DateTime)
        {
            DateTime dt = (DateTime) value;
            TimeSpan ts = ClientTimeZoneSpan;

            // Adjust to client time zone if set, or use the server timezone.
            if (ts != TimeSpan.MinValue)
                result = dt.Add(ts);
            //else
            //	result = UTCtoServerTZ(dt);
        }
        return result;
    }

    public static object UTCtoServerTZ(object value)
    {
        object result = value;
        if (value is DateTime)
        {
            DateTime dt = (DateTime) value;
            TimeZone tz = TimeZone.CurrentTimeZone;
            result = tz.ToLocalTime(dt);
        }
        return result;
    }

    public static object ClientToServerTZ(object value)
    {
        object result = value;
        if (value is DateTime)
        {
            DateTime dt = (DateTime) value;
            result = UTCtoServerTZ(dt.ToUniversalTime());
        }
        return result;
    }

    public static object ServerToClientTZ(object value)
    {
        object result = value;
        if (value is DateTime)
        {
            DateTime dt = (DateTime) value;
            result = UTCtoClientTZ(dt.ToUniversalTime());
        }
        return result;
    }
}

One of the remaining challenges is recalling the data using the correct DST adjustment. When I get more time I will add DST adjustment to the code


Comment Section

Comments are closed.


I am just going through some of my old code looking for useful snippets. I found one snippet that was pretty cool (at least it was at the time!) where I used a grid control to display items for the user to configure. The grid had folders and so I wanted to use the system folder icon that are used in Windows Explorer. After a little searching on the Internet and MSDN, I came up with the following:

#include 
// ...
SHFILEINFO shfi;
HIMAGELIST hImageList = NULL;
HICON hIconClosed = NULL;
HICON hIconOpen = NULL;
UINT flags;

// Closed folder
ZeroMemory(&shfi, sizeof(shfi));
flags = SHGFI_ICON | SHGFI_SYSICONINDEX | SHGFI_SMALLICON;
hImageList = (HIMAGELIST) SHGetFileInfo("", 0, &shfi, sizeof(shfi), flags);
hIconClosed = ImageList_GetIcon(hImageList, shfi.iIcon, ILD_TRANSPARENT);

// Open folder
flags = SHGFI_ICON | SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_OPENICON;
hImageList = (HIMAGELIST) SHGetFileInfo("", 0, &shfi, sizeof(shfi), flags);
hIconOpen = ImageList_GetIcon(hImageList, shfi.iIcon, ILD_TRANSPARENT);

Don't forget to call DestroyIcon() when you are finished. When I actually used it in an application (BCB - Borland C++ Builder 5), it looked more like the following:

// (From header file)
#define SAFE_DELETE(x)        \
  if (x) {                    \
    delete (x);               \
    (x) = NULL;               \
  }
// (From form class header)
TImageList *MyGridImages;

// (From form cpp file)
// Add closed and open folder icons to image list.
Graphics::TIcon* pIcon = NULL;
HIMAGELIST hImageList = NULL;
SHFILEINFO shfi;
HICON hIcon = NULL;
UINT flags;

// Closed folder
ZeroMemory(&shfi, sizeof(shfi));
flags = SHGFI_ICON | SHGFI_SYSICONINDEX | SHGFI_SMALLICON;
hImageList = (HIMAGELIST) SHGetFileInfo("", 0, &shfi, sizeof(shfi), flags);
hIcon = ImageList_GetIcon(hImageList, shfi.iIcon, ILD_TRANSPARENT);
pIcon = new Graphics::TIcon();
pIcon->Handle = hIcon;
MyGridImages->AddIcon(pIcon);
SAFE_DELETE(pIcon);

// Open folder
flags = SHGFI_ICON | SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_OPENICON;
hImageList = (HIMAGELIST) SHGetFileInfo("", 0, &shfi, sizeof(shfi), flags);
hIcon = ImageList_GetIcon(hImageList, shfi.iIcon, ILD_TRANSPARENT);
pIcon = new Graphics::TIcon();
pIcon->Handle = hIcon;
MyGridImages->AddIcon(pIcon);
SAFE_DELETE(pIcon);

// Create an array of IPictureDisp handles
MPictures.resize(MyGridImages->Count);
for (size_t i = 0; i < MPictures.size(); ++i)
{
 std::auto_ptr pPicture( new Graphics::TPicture() );
 MyGridImages->GetBitmap(i, pPicture->Bitmap);
 _di_IPictureDisp pVal;
 GetOlePicture(pPicture.get(), pVal);
 MPictures[i] = pVal;
}


Comment Section

Comments are closed.


<< Older Posts | Newer Posts >>