Thursday, August 18, 2005
Very Interesting Lisp Primer
Monday, August 08, 2005
Find 'hidden' resources in extension DLL's
Identifying the problem
The problem is that MFC assumes that all of the applications' resources reside in the main executable. There are two ways to solve this problem. The first method is to let MFC hunt for the correct resource using AfxFindResourceHandle. The second method revolves around treating the extension DLL like a separate resource DLL.
However, there are significant disadvantages to both methods. The main disadvantage with the first method, which uses AfxFindResourceHandle, is that there can’t be any duplicate resource IDs in any of your applications DLL’s. Also, this method is slower.Both occur because AfxFindResourceHandle searches the entire link chain in the application and returns the first module with a matching resource.
The primary disadvantage with the second method is that you have to explicitly get a handle to the target module; and since loading each kind of resource uses different functions, you end up with code that is almost identical for each kind of resource.
Ask MFC politely for the resource
To have MFC do all the heavy lifting, call AfxFindResourceHandle, like this:
HINSTANCE hInst;
HANDLE hIcon;
hInst = AfxFindResourceHandle(MAKEINTRESOURCE(IDI_YOURICONHERE),
RT_GROUP_ICON);
hIcon = FindResource(hInst, MAKEINTRESOURCE(IDI_YOURICONHERE),
RT_GROUP_ICON);
This will work fine--until someone adds an icon with a duplicate ID to the code base. There is one advantage to this method of loading a resource: You don’t have to know which module the resource resides in.
When something can go wrong, it will.
Being a firm believer in Murphy’s Law; I like to be as explicit as possible. So I prefer the second method of loading resources in my extension DLL’s. Although it requires a little more investment upfront, it pays off when your code goes into maintenance mode. Basically, for each kind of resource you use in your extension DLL, you’ll write a function like this:
CString LoadResString(UINT ResID, CString strModuleName)
{
TCHAR szBuffer[256];
//The module handle can be obtained in a variety of ways the
//following is the most
//straight forward
HMODULE mod = ::GetModuleHandle(strModuleName);
ASSERT(mod);
if (!mod){
AfxMessageBox(_T("GetModuleHandle failed in LoadResString."));
RaiseException(1814, 0, 0, NULL);
}
//This is where the resource actually gets loaded and will be
//different in each function
int count = LoadString(mod, ResID, szBuffer, 255);
ASSERT(count);
if (!count){
AfxMessageBox(_T("LoadString failed in LoadResString."));
RaiseException(1814, 0, 0, NULL);
}
CString rslt(szBuffer);
return rslt;
}
MFC extension DLL’s are a nice feature but because of all the legacy code that Microsoft has to support we are stuck with some “interesting” features like this problem with MFC not looking in the extension DLL for resources first. It’s an easy problem to overcome but it can give you a headache the first time you encounter it. It doesn’t help that you have to know what the problem is before you can find the answer in the documentation.
Note: Technically, there is a third method for specifying which module to load resources from. I chose not to examine it in this article because it's an even less explicit than the AfxFindResourceHandle method.
Friday, August 05, 2005
Marking an ActiveX object safe for scripting
_reg_catids_ = ["{7DD95801-9882-11CF-9FA9-00AA006C42C4}"]
Now the class won't cause Internet Exporer to prompt for permission to load the component. I wonder why there is such a spyware problem? :0
Thursday, August 04, 2005
Event logging the .NET way
The System.Diagnostics name space contains (among other things) the event logging API. The two classes we'll focus on for reading and writing to the event log in .NET are the EventLog and the EventLogEntry classes.
Write to the event log
Writing to the event log in .NET follows the same basic procedure as using the Win32 API. First, open the event log.
//The period means this machine
EventLog log = new EventLog(“Application”, “.”, “MySource”);
Then write the event:
log.WriteEntry(“Test message”, EventLogEntryType.Information);
Read from the event log
Reading the event log the Win32 way requires you to open the log with the OpenEventLog function and retrieve a handle to the Event Log. Then you have to retrieve the EVENTLOGRECORD structure with the ReadEventLog function.
The EVENTLOGRECORD structure has both static and dynamic fields. In order to access the dynamic fields of the structure such as the message and the source, you have to treat the structure like a byte array. To make things inconsistent, you access the static fields with the dot operator like normal. Although it’s not difficult, it can be error-prone, and if you miscount bytes, you'll get access violations or bad data.
Reading the event log the .NET way is much improved. Instead of dealing with an EVENTLOGRECORD structure, you get to deal with a much safer and easier EventLogEntry class. When you create an EventLog instance pointing at a specific log, the Entries property (which is an instance of the EventLogEntryCollection class) automatically fills with EventLogEntries. You can use the following code to access the Entries collection:
foreach EventLogEntry entry in eventLog.Entries{
Console.WriteLine(entry.Source);
Console.WriteLine(entry.Message);
}
Create a custom event log
You can also create a custom event log by calling the static method CreateEventSource of the EventLog class. For example:
//Check to make sure that the source that will
//be associated with the new log doesn’t exist.
//SourceExists is also a static method
if (!EventLog.SourceExists(“MySource”){
//If MyNewLog does not exist it will be created.
EventLog.CreateEventSource(“MySource”, “MyNewLog”);
}
As the above code indicates, you can check to see if a source is registered. If the source name already exists, you could overwrite someone else’s source, creating havoc.
Compare the previous code to what you would have to do to create a custom event log with the Win32 API:
Create a message file.
Compile it using the message compiler.(Be sure to include the created resource into the resources of the message synch DLL or Executable.)
Register the message synch (which is about 100 lines of code) in the event system by creating the requisite registry entries.
Take a look at the keys under HKLM\System\CurrentControlSet\Services\EventLog in the registry.
Open and retrieve a handle to an event log.
Now you can write the custom event log.
Conclusion
As you can see, event logging the .NET way is quite easy. The only drawback is that your application has to be running on a version of Windows based on Windows NT. But since Windows XP is the new consumer OS, this issue will soon be moot.
Monday, August 01, 2005
MASTER THE MYSTERIOUS EVENTLOGRECORD
When dealing with the Windows event log, a developer usually only wants to write events to the log. The Event Viewer, on the other hand, is good at displaying event log messages and lets you view, sort, or filter these messages from a local or remote machine. However, sometimes it's necessary to read events from the log. For instance, if you're dealing with end users and you want the users to be able to click on an icon and have all relevant event log entries sent to you via e-mail.
OVERVIEW
If you look at the documentation for the event-logging API, it appears that reading entries from the log is as simple as this.
DWORD nBytesRead, nBytesMin;
EVENTLOGRECORD record;
HANDLE hEntry = OpenEventLog( NULL, "Application");
While(ReadEventLog(hEvent, EVENTLOG_SEQUENTIAL_READ |
EVENTLOG_FORWARDS_READ,
0, &record, sizeof(EVENTLOGRECORD),
&nBytesRead, &nBytesMin)){
//Do something with record
}
READING AN EVENT
There are a number of problems with the preceding code, which stem from the fact that EVENTLOGRECORD is a dynamic structure. If you look at the documentation for EVENTLOGRECORD, you'll see the following:
typedef struct _EVENTLOGRECORD {
DWORD Length;
DWORD Reserved;
DWORD RecordNumber;
DWORD TimeGenerated;
DWORD TimeWritten;
DWORD EventID;
WORD EventType;
WORD NumStrings;
WORD EventCategory;
WORD ReservedFlags;
DWORD ClosingRecordNumber;
DWORD StringOffset;
DWORD UserSidLength;
DWORD UserSidOffset;
DWORD DataLength;
DWORD DataOffset;
//
// Then follow:
//
// TCHAR SourceName[]
// TCHAR Computername[]
// SID UserSid
// TCHAR Strings[]
// BYTE Data[]
// CHAR Pad[]
// DWORD Length;
//
} EVENTLOGRECORD, *PEVENTLOGRECORD;
All of the fields in comments are just examples. Yet if you try to do something like this:
_tcslen(record.SourceName);
you'll get a compile error. All of the fields after DataOffset are one big buffer, and you have to treat it as such to work with it. For instance, to get the length of the SourceName field, you must do the following.
//precord is declared as a pointer to a byte buffer.
DWORD length = _tcslen((TCHAR*)precord + 56);
The magic number 56 happens to be the total number of bytes preceding the SourceName field. So if you want to get the value of the ComputerName field, you use the following code:
DWORD offset = 56 + _tcslen((TCHAR*)precord + 56) + 1;
//The +1 is for the NULL byte
DWORD length = _tcslen((TCHAR*)precord + offset)
The other major problem is that there isn't a way to tell how big the EVENTLOGRECORD is ahead of time without attempting to read it. This means you have to call ReadEventLog log twice: the first time with parameters that you know will fail and the second time with the correct parameters. If you look at the last parameter, nBytesMin, this variable will contain the number of bytes necessary to read the requested number of records. If you didn't catch that, you can read multiple records with each call to ReadEventLog. Here's some code that works:
DWORD nBytesRead = 0;
DWORD nBytesMin = 0;
DWORD nBytesToRead = 0;
BYTE *precord = NULL;
BYTE fake[1];
HANDLE hEntry = OpenEventLog( NULL, "Application");
//This one fails but gives us the event record size
ReadEventLog(hEvent, EVENTLOG_SEQUENTIAL_READ |
EVENTLOG_FORWARDS_READ,
0, fake, 1,&nBytesRead, &nBytesMin);
nBytesToRead = nBytesMin;
precord = new BYTE[nBytesToRead];
ReadEventLog(hEvent, EVENTLOG_SEQUENTIAL_READ |
EVENTLOG_FORWARDS_READ, 0,
precord, nBytesToRead, &nBytesRead,
&nBytesMin);//read one record
Now we extract some fields.
CString SourceName(precord + 56);
CString ComputerName(precord + 56 + SourceName.GetLength() + 1);
SUMMARY
Even though the event log API isn't the most difficult to master, it isn't well documented or obvious. Yet once you master the EVENTLOGRECORD structure, it's smooth sailing.