Monday, August 4, 2008

Machinations to Set Text Color in Win32

Copyright © 2008, Steven E. Houchin
Recently, I wanted to set the text color for a Win32 Static control within a dialog window. I'm a relative novice when it comes to old fashioned Win32 message pump applications, so I had to scrounge around for the right API calls to do this. I assumed there must be a simple one-time call to set the text attributes for a given control. Silly me.
It took some digging to discover that the answer was to handle the WM_CTLCOLORSTATIC message. The M*soft documentation states that this message is sent when "a static control, or an edit control that is read-only or disabled, ... is about to be drawn." In other words, the color is modified right before the text gets drawn. It seems you have to do this each and every time you wish to draw the text, not just as a one-time setup. So I wrote this:

case WM_CTLCOLORSTATIC:
{
     HDC hDC = (HDC)wParam;
     ::SetTextColor(hDC, RGB(255,0,0)); // red text
     return TRUE;
}

Seems simple enough. The result? Plain old black text. It turns out that WM_CTLCOLORSTATIC requires an HBRUSH as its return value. Since TRUE isn't a valid brush, it ignored the whole thing. So, where do I get a brush? The documentation says the brush will be used to paint the control's background. I tried this:

return (LRESULT)::GetStockObject(HOLLOW_BRUSH);

The result was red text with each character on a white background. Plus, the old text wasn't erased when new text was written to the control. I realized that HOLLOW_BRUSH is essentially no brush, so the old text wasn't getting wiped. I needed the brush for the control's plain old gray background. So, I tried this:

...
COLORREF bg = ::GetBkColor(hDC);
HBRUSH hb = ::CreateSolidBrush(bg);
return (LRESULT)hb;

Surely this would work. Nice try, but no cigar. This fixes the problem of ghost text hanging around. But it has the side effect of turning the control's background entirely white. In other words, GetBkColor() is returning white, even though the background of the dialog is the usual gray. So, two problems still remain: the character background is white, and now the control's background is white. For the first problem, I discovered this:

...
::SetBkMode(hDC, TRANSPARENT);
::SetTextColor(hDC, RGB(255,0,0)); // red text
...

By temporarily forcing the returned brush to a stock light gray (using GetStockObject()), I could see that the text backgound was now what I wanted: red text on the control's background color (stock light gray).
So, where could I get the dialog's usual gray background to return as a brush? After lots of searching around, I discovered the answer: send a WM_CTLCOLORDLG message. This message returns the background brush for the dialog box, as long as I'm not handling that message myself in DlgProc() - which could cause a chicken-and-egg problem.

...
HBRUSH hBrush = (HBRUSH)SendMessage(
     hDlg,
     WM_CTLCOLORDLG,
     (WPARAM)hDC,
     lParam /* the control's HWND */);
     return (LRESULT)hBrush;

I executed this and ... ugh! This set me all the way to black text on the gray background. My previous attempts told me that the problem was an invalid brush. Later, I was moving around the code into separate functions, and it accentally started to work. I had done this:

...
HBRUSH hBrush = (HBRUSH)SendMessage(
     hDlg,
     WM_CTLCOLORDLG,
     GetWindowDC((HWND)lParam),
     lParam /* the control's HWND */);

Voila! A miracle occured. I finally see the result I wanted from the start: red text on the standard gray background. All of that work just to draw RED TEXT!
I'm guessing that the HDC provided via 'wParam' is for the text
glyphs, which is different than the HDC associated with the control's HWND via 'lParam'. Here's the final magic code:

DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
...
case WM_CTLCOLORSTATIC:
{   
     // handle just for my MSG control
     if ((HWND)lParam == GetDlgItem(m_hDlg, IDC_STATIC_MSG))
     {
          HDC hDC = (HDC)wParam;  // use for the text
          ::SetBkMode(hDC, TRANSPARENT);
          ::SetTextColor(hDC, RGB(255,0,0)); // red text
          HBRUSH hb = (HBRUSH)SendMessage(
               hDlg,
               WM_CTLCOLORDLG,
               (WPARAM)GetWindowDC((HWND)lParam),
               lParam /* the control's HWND */);
          return (LRESULT)hb;
     }
}

The WM_CTLCOLORDLG also works by using 'hDlg' instead of 'lParam', even though it results in different hDC's. It would also be possible to send a WM_CTLCOLORSTATIC message itself to get an HBRUSH, but that seems like it could result in an infinite loop - sending a message from the same message's handler. I suppose a different target HWND might avoid that. So, it now works, but I don't know all the details under the hood.

Wednesday, May 14, 2008

Accessing Attributes in AssemblyInfo.cs

Copyright © 2008, Steven E. Houchin
For a new C# .NET application, an AssemblyInfo.cs file is automatically created. It contains empty attributes that can be used to describe the assembly. These attributes are queried by Windows when the Properties of an executable are examined from Windows Explorer. They are also useful within the application itself to set the string in the title bar and for populating an About dialog. For example, I set the following values for one of my applications (with names changed to protect confidentiality):

[assembly: AssemblyTitle("My Application")]
[assembly: AssemblyCompany("Forest Park Lab")]
[assembly: AssemblyProduct("My Product")]
[assembly: AssemblyCopyright("Copyright © 2004-2008, Forest Park Lab.")]
[assembly: AssemblyTrademark("My Product ™ is a trademark of Forest Park Lab.")]

Setting these values doesn't make them automatically available to your application code. In order to access them, I have a piece of boilerplate code I include in the AssemblyInfo.cs file to make these attributes readily accessible. I define an "AssemblyInfo class" as follows:

using System.Reflection;
using System.Runtime.CompilerServices;

public class AssemblyInfo
{
   private AssemblyTitleAttribute  _title = null;
   private AssemblyCopyrightAttribute _copyright = null;
   private AssemblyProductAttribute _product = null;
   private string    _version;

   public AssemblyInfo()
   {
      System.Reflection.Assembly assembly =
      System.Reflection.Assembly.GetExecutingAssembly();
      // get the version string
      _version = assembly.GetName().Version.ToString();

      // get other desired attributes
      object[] attributes = assembly.GetCustomAttributes(false);
      for (int i = 0; i < attributes.Length; i ++)
      {
         if (_title == null)
         {
            _title = attributes[i]
               as AssemblyTitleAttribute;
            if (_title != null)
               continue;
         }
         if (_copyright == null)
         {
            _copyright = attributes[i]
               as AssemblyCopyrightAttribute;
            if (_copyright != null)
               continue;
         }
         if (_product == null)
         {
            _product = attributes[i]
               as AssemblyProductAttribute;
            if (_product != null)
               continue;
         }
      }
   }
   public string Title  { get { return _title.Title; } }
   public string Product  { get { return _product.Product; } }
   public string Copyright  { get { return _copyright.Copyright; } }
   public string Version  { get { return _version; } }
}

The way this code works is that it fetches all of the customizable attributes from the assembly (GetCustomAttributes()) into an array of objects. It then loops through each object and attempts to cast it (using the C# "as" operator) to one of the desired attribute types. If the cast fails, the result is null. Otherwise, the object is remembered as a property within the AssemblyInfo class for later access by the application.
To make use of these attributes in the application, simply create an instance of the AssemblyInfo class and access the properties, as follows:

AssemblyInfo appInfo = new AssemblyInfo();
// set the title in title bar
this.Text = appInfo.Title;
// set the product name
this.lblProduct.Text = appInfo.Product;

Tuesday, April 15, 2008

Shared .NET Configuration Files

Copyright © 2008, Steven E. Houchin

One C# .NET application I’ve been working on for a while consists of two executables: a main application and a taskbar tray applet. The two apps needed to share common configuration settings, which I planned to implement via the app.config file. The way .NET is set up (I’m using .NET 1.1), each .EXE loads its config file based upon the executable’s file name. For example, xyz.exe loads xyz.exe.config. This is a problem when abc.exe needs to access the same config settings. Now, .NET may have some clever API to solve this problem, but I didn’t find one.

My answer to the situation was to create a file named common.config, which I placed within the project for a shared library. Inside this config file are the <appSettings> and </appSettings> tags, with the application’s key, value pairs coded inside:

<appSettings>
<add key="database_type" value="MSACCESS"/>
<add key="datasource" value="mydatabase.mdb"/>
<add key="default_browser" value="C:\Program Files\Internet Explorer\iexplore.exe"/>
</appSettings>

Once the common.config file was created, I needed to reference it from each executable’s app.config file. I accomplished this using the “file=” parameter of the <appSettings> tag. Below is my entire app.config file, which I duplicate in the projects for the main application and a taskbar tray applet:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings file="common.config">
</appSettings>
</configuration>

Note that the above does not include path information for common.config. The reason for this is that common.config is installed on a user’s system in a single directory, along with the application executables. Thus, simply using the current directory is appropriate. However, this doesn’t work when using the Visual Studio development environment, where each part of the application is built in a different directory. So, for each executable to find this file during Debug execution, I added a Pre-Build Event to each executable’s project, which first copies common.config to that project’s directory:

copy $(SolutionDir)MyCommonDll\common.config $(TargetDir)

With this done, I now have a configuration file that is shared by each executable in my application.

Monday, March 31, 2008

MSMQ Startup Failure on XP Pro

Copyright © 2008, Steven E. Houchin

Yesterday, I installed an application I'd written onto a new machine. It uses Message Queuing for two processes to communicate with each other, so I needed MSMQ installed and running on the new machine (which is XP Pro). Much to my surprise, the MSMQ service refused to start. In Event Viewer, I saw the following cryptic error:

Event Type: Error
Event Source: MSMQ
Event Category: Kernel
Event ID: 2047
Date: 3/30/2008
Time: 1:31:10 PM
User: N/A
Computer: ********
Description:A connection to the Distributed Transaction Coordinator cannot be established. Consequently, transactions cannot be supported.

Uninstalling and reinstalling the MSMQ Windows components (via Add/Remove Programs) still resulted in failure. After hours of fruitless research on the web, trying a half-dozen search keyword combinations, I finally found the answer. The problem -- just as the error message says -- was with the Distributed Transaction Cordinator service (MS DTC). This is apparently a facility that MSMQ uses for routing its messages.

The problem was apparently that MS DTC service needs to execute under the NETWORK SERVICE user in order to work properly with MSMQ. I didn't have to do this on my other XP system, so something I installed (such as .NET 3.5?) on this newer system seems to have messed up either MSMQ or MS DTC.

The actual process for the fix is:

a) Uninstall MSMQ
b) Stop the MS DTC service
c) Change MS DTC's execution user to NETWORK SERVICE
d) Open a Command Prompt and execute "msdtc -resetlog"
e) Restart MS DTC
f) Install MSMQ

Lots of mystery here, but others on the net have seen this same MSMQ problem. Does anyone out there know more about what's going on?

Monday, February 18, 2008

VISITOR Design Pattern

Copyright © 2008, Steven E. Houchin

I ran into an architectural problem after I developed a generic library class, SQLDbConn, that connects to a SQL database given a connection string. It implements an interface IDbConn that can also be used to implement a similar class for MS Access or other databases. An instance of SQLDbConnis created by the application via a Factory function:

static public IDbConn MakeDatabaseConnFactory
(DatabaseConnType eType)

After a databse connection is made through this "Connection" object, the nuts and bolts of accessing the database and retrieving rowsets is done using an application-specific "Model" object, defined by an IDbModel interface. For a SQL connection, that would be an instance of the SQLDbModel class.

Here's the design problem I encountered: the application shouldn't have any hard-coded knowledge of what kind of database it is using. It only has a reference to its IDbConnobject after calling the Factory function above. So, it needs to instantiate its Model object without knowing it will actually be a SQLDbModel object. I don't want to put the logic in SQLDbConnto create it, because that Connection class is generic, so shouldn't know about the application-specific Model class. I also don't want to simply create another Factory function - though that would work - because I have already created a database-type aware object - the Connection object - that knows what kind of database is in use. So, how do I get the generic Connection object (SQLDbConn) to create the specific Model object for me, and still remain application neutral?

The solution I came up with uses the VISITOR design pattern. I added the VISITOR pattern's accept method to the IDbConn interface:

bool AcceptVisitor(IDbConnVisitor visitor);

This allows my application to generically extend the connection object's functionality after its instantiation. The IDbConnVisitor interface referenced above specifies a visitmethod for each type of supported database, but does not dictate what that method does (they could order sandwiches from the deli):

public interface IDbConnVisitor
{
   bool VisitMsAccess(IDbConn dbConn);
   bool VisitSqlOle(IDbConn dbConn);
   bool VisitSqlDa(IDbConn dbConn);
}

Since each instance of a Connection object (like SQLDbConn) knows what kind of database it is, it can call its appropriate visit function, without knowing what that function actually does. For the SQLDbConnclass, its acceptfunction looks like this:

public bool AcceptVisitor(IDbConnVisitor visitor)
{
   try
   {
      return visitor.VisitSqlDa(this);
   }
   catch (System.Exception excp)
   {
      _errorText = excp.Message;
   }
   return false;
}

An MS Access Connection object's AcceptVisitor would similarly call VisitMsAccess.

My application then defines an implementation of IDbConnVisitor via a new class, such as CreateDbModelVisitor, whose job is to provide functions that will instantiate the appropriate Model object for any of the supported database types. Inside, it has a property of type IDbModel that the visitor methods instantiate:

public class CreateDbModelVisitor : IDbConnVisitor
{
   ...
   protected IDbModel _model = null;
   ...
   public bool VisitMsAccess(IDbConn dbConn) { ... }
   public bool VisitSqlOle(IDbConn dbConn) { ... }
   public bool VisitSqlDa(IDbConn dbConn) { ... }
   public IDbModel Model { get { return _model; } }

}

For the SQL database, the visitmethod implementation within CreateDbModelVisitoris as follows:

public bool VisitSqlDa(IDbConn dbConn)
{
   _errorText = "";
   try
   {
      if (dbConn.IsConnected)
      {
         _model = new SqlDbModel(dbConn);
         return true;
      }
      else
      {
         _errorText = "Database has not been connected."
      }
   }
   catch (System.Exception excp)
   {
      _errorText = excp.Message;
   }
   return false;
}

Putting it all together, the application would do the following:

IDbConn dbConn = MakeDatabaseConnFactory(config.DbType);
IDbConnVisitor dbVisitor = new CreateDbModelVisitor();
if (dbConn.AcceptVisitor(dbVisitor))
{
   IDbModel model = dbVisitor.Model;
   ...
   // make Model calls here to access database
}

Note that the only thing the application knows of its database type is a value that it picks up from a config file.

By using a VISITOR design pattern, the database-specific Connection object (IDbConn), which is supposed to be application neutral, can execute an application-specific function (via AcceptVisitor) for that particular type of database, without knowing the details of what that function does. It just calls the accept method, and that's that.

Wednesday, February 13, 2008

Fun with DataGrids

Copyright © 2008, Steven E. Houchin

Recently, I was implementing a DataGrid for an ASP.NET application. ASP.NET is not usually my forte, so I was once again in learning mode.

The first obstacle I stumbled onto was that a lot more columns were displayed when the page came up than what I intended. My DataSource was an ArrayList of objects, which represented information culled from the database. In the page's Page_Load function, I had code that created each column explicitly, using ButtonColumn and BoundColumn objects. The extraneous unwanted columns that displayed were duplicates of the DataSource object's properties.

I discovered the problem was with the AutoGenerateColumns property of the DataGrid. Its default value was True, which causes a DataGrid object to automatically create columns (duh!) identical to the DataSource object's properties ... just what I was seeing. Setting this to False prior to assigning the DataSource value fixed that.

The next obstacle was that my ItemCommand handler wouldn't fire when I clicked on a ButtonColumn item in the list. Worse, my grid completely disappeared afterward. Searching around on the web revealed many others who also had this problem, but nobody seemed to have a good answer. There were some postings about EnableViewState related to this, but they didn't help.

Eventually, I discovered that when the Click event caused a PostBack to the page, the DataGrid reverted back to its initial empty state. My Page_Load code populated the DataGrid only when the call was not a PostBack. Armed with this insight, I changed Page_Load so a PostBack call only decides where to get the data: a database call for non-PostBack versus session variable on PostBack. The code now creates and binds the DataGrid columns each time through Page_Load, since my columns are created on-the-fly. The fixed code follows:



ArrayList allUsers = null;
if (IsPostBack)
{
   // restore array of all users
   allUsers = controller.GenericPageData as ArrayList;
}
else
{
      // get an array of all users as UserProfile objects
      controller.Model_GetAllUserInfo(ref allUsers);

      // save the user data in the controller
      controller.GenericPageData = allUsers as object;
}

if (null != allUsers)
{
   // set the array of users as the data source
   dgUsers.AutoGenerateColumns = false;
   dgUsers.DataSource = allUsers;

   // configure the columns
   ButtonColumn colName = new ButtonColumn();
   colName.DataTextField = "FullName";
   colName.HeaderStyle.BorderStyle = BorderStyle.Outset;
   colName.HeaderStyle.BackColor = Color.Ivory;
   colName.HeaderText = "Full Name";
   colName.CommandName = "View";

   BoundColumn colLogin = new BoundColumn();
   colLogin.DataField = "LoginName";
   colLogin.HeaderText = "Login Name";
   colLogin.HeaderStyle.BorderStyle = BorderStyle.Outset;
   colLogin.HeaderStyle.BackColor = Color.Ivory;

   BoundColumn colId = new BoundColumn();
   colId.DataField = "KeyId";
   colId.HeaderText = "Record Id";
   colId.HeaderStyle.BorderStyle = BorderStyle.Outset;
   colId.HeaderStyle.BackColor = Color.Ivory;

   // add the columns to the datagrid
   dgUsers.Columns.Add(colId);
   dgUsers.Columns.Add(colName);
   dgUsers.Columns.Add(colLogin);

   dgUsers.EnableViewState = false;
   dgUsers.DataKeyField = colId.DataField;
   dgUsers.DataBind();

   // set up the click and edit event callbacks
   dgUsers.ItemCommand +=
      new DataGridCommandEventHandler(this.UsersGrid_OnClick);
   return;
}