Thursday, September 3, 2009

The Mysterious Missing Attribute

Copyright © 2009, Steven E. Houchin

Something about my XP system has been driving me crazy for a long time. I use Visual Source Safe for source control of my development projects on. VSS makes use of Windows’ Read Only file attribute to prevent modification of source-controlled files in a project’s working directory. When a file is checked out of VSS, that attribute is cleared, and the user can then change the file. When checked back in, Read Only is set again.

So, what was my problem? It’s this: the Read Only attribute on those source-controlled files (and others) would simply disappear periodically. I’d have to go through all my files and reassert Read Only by hand. I looked into a number of causes: that VSS was doing it, or Visual Studio; maybe it was compiling the files over a network share; maybe it was my anti-virus software.

Then I stumbled across the cause: backing up the files to a CD via Windows Explorer. It seems that somebody in Redmond Land decided it would be a clever idea to clear the Read Only attribute for all files burned to a CD. When I searched the web for others with this problem, I actually saw the opposite complaint: that files retained their R/O attributes on a CD. Okay, so what? The CD is … wait for it … wait for it … READ ONLY!

Maybe there is some obscure Windows setting that will stop this behavior on my system. Let me know if you find one. In the meantime, I now have to first copy my files into a zip file, then back up the zip – a real pain.

So, call me crazy. This problem has certainly driven me mad.

Monday, April 27, 2009

Setting a Win32 Control's Font and Sizing It to Fit the Text

Copyright © 2009, Steven E. Houchin

In my latest application, I have a Static control that I use as a label at the top of a rectangular box. I wanted to change the font, then resize the control to fit the text exactly. First, I needed to set the font. This is done by initializing a LOGFONT structure for the desired font family and size. The code below speicifies the minimum LOGFONT fields necessary:


LOGFONT lf;
ZeroMemory(&lf, sizeof(LOGFONT));
lf.lfHeight = 12;
_tcscpy(lf.lfFaceName, _T("MS Sans Serif"));

Now that we have a LOGFONT, a Win32 font object needs to be created and associated with the control:


// the LOGFONT must have some valid data in it
HFONT hFont = CreateFontIndirect(&lf);
if ((HFONT)0 != hFont)
{
SetWindowFont(hCtrl, hFont, TRUE);
}

The SetWindowFont call is really a macro that expands into a call to SendMessage that specifies the WM_SETFONT message. At this point, when the control is redrawn, it will use the new font.

Now, we need to resize the control so it fits exactly the text it contains. Let's assume the variable lpszText points to our null-terminated text string that the Static control already displays. We must call the Win32 GetTextExtentPoint32 function to get the dimensions of the text. This function requires the device context (DC) of the control. And, to make sure we're getting the proper dimensions, our new font must be selected into the device context.


HDC hdc = GetDC(hCtrl); // get the static control's DC
HFONT hfOld = reinterpret_cast(SelectObject(hdc, hFont));
DeleteObject(hfOld); // free the old font handle

// measure the text height and width
size_t iLen = _tcslen(lpszText);
SIZE s;
if (GetTextExtentPoint32(hdc, m_pszLabelText,
static_cast(iLen), &s))
{
// control's x,y position set by CreateWindow
POINT p = this->ptControlOrigin;
MoveWindow(hCtrl, p.x, p.y, s.cx, s.cy,
FALSE /* no repaint now */);
}
ReleaseDC(hCtrl, hdc); // free the DC


That completes the task. We've changed the control's font to 12 point "MS Sans Serif" and have resized the control to fit the control's text string exactly. If the control's text string is changed, then the calls to GetTextExtentPoint32 and MoveWindow must be repeated for the new string's length.

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 SQLDbConn is 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 IDbConn object 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 SQLDbConn to 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 visit method 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 SQLDbConn class, its accept function 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 visit method implementation within CreateDbModelVisitor is 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.