Thoughts from Dan Miser RSS 2.0
 Thursday, October 14, 2004
I was in the process of solving a problem that required me to do some message spying. I haven't used WinSight in several years, but the last time I used it, it was sufficient for my needs. Well, either the past is causing me to remember the experience more fondly (doubtful), or the environments have changed enough over the past few years to cause WinSight to not be as good any more (more likely). The current problem boils down to several windows that were not properly identified, and as a result, I could not trace messages, see window details, or simply navigate through the window hierarchy.

I installed Spy++ from VS.NET, and it worked rather well. I found what I needed with minimal fuss. Spy++ isn't a bad application, overall.

I did do some digging and found a free message spy application. I haven't tried it, but it looks promising and is billed to be better than Spy++. Check out Winspector and try it for yourself.

Thursday, October 14, 2004 9:27:00 AM (Central Standard Time, UTC-06:00)  #    Comments [2] -

 Wednesday, October 13, 2004
I've written some about this interface, and how it allows you to grab data from an HTML page and use that data inside an ActiveX control. In a nutshell, your HTML will have an OBJECT tag for the ActiveX control and - optionally - some PARAM tags inside the OBJECT tag. One thing to keep in mind is that the IPersistPropertyBag methods will only fire if you actually define at least one PARAM tag. Without a PARAM tag, the code will not execute.

The moral of the story is: if you have some kind of initialization code in your IPersistPropertyBag methods, be doubly sure that you define a PARAM tag!

Which reminds me, now that Delphi Informant is no longer in business, I'll see what I can do to get my articles published with them posted on my web site. I'll post an entry here when I can do that.

Wednesday, October 13, 2004 10:49:00 AM (Central Standard Time, UTC-06:00)  #    Comments [2] -
Delphi
In SysUtils, there is a handy function (CheckWin32Version) to check that you are running at least a certain version of Windows. Unfortunately, it is wrong. For example, let's say you want to make sure you are running Win2000 or greater. The following code should do just that:

  if CheckWin32Version(5) then
    CallMyWin2000OrGreaterFunctionHere;
When running on XP, the major version will be 5, and the minor version will be 1. These are stored in writable constants in SysUtils. Inside CheckWin32Version, the check for AMinor is backwards, so your results will be wrong.

I am now using this function instead and everything works fine:


function FixedCheckWin32Version(AMajor: Integer; AMinor: Integer = 0): Boolean;
begin
  Result := (AMajor < Win32MajorVersion) or
            ((AMajor = Win32MajorVersion) and
             (AMinor <= Win32MinorVersion));
end;

Just a side-note: This was fixed in Delphi 7 and above. Of course, if you're using D6, you'll need to use this.

Wednesday, October 13, 2004 10:38:00 AM (Central Standard Time, UTC-06:00)  #    Comments [4] -
Delphi
 Friday, September 24, 2004
I'm a little partial to Borland DB technologies, and especially BDP. ;-) However, I think this demo shows some of the power that BDP has in the upcoming version of Delphi. Check out the BDNtv episode presented by Jason Vokes and see what you think.

I saw many other properties and methods in this demo that weren't touched on. All in all, BDP looks incredibly flexible in Diamondback - especially when either doing multi-tier apps, or using data from heterogenous sources.

Friday, September 24, 2004 8:51:00 AM (Central Standard Time, UTC-06:00)  #    Comments [1] -
Delphi
 Thursday, September 23, 2004
By default, COM Servers register themselves in HKEY_CLASSES_ROOT (HKCR). The actual registry entries will get written to HKEY_LOCAL_MACHINE (HKLM) in most cases. HKCR is an alias to HKEY_LOCAL_MACHINE in older versions of Windows, but starting in Windows 2000 and later, HKCR is a merged view of HKLM and HKEY_CURRENT_USER (HKCU). I needed a way to register my COM server into HKCU so that locked down machines that don't have access to HKLM could still run properly. For some background information on HKCR, check out MSDN.

I came across the function RegOverridePredefKey, and it looked like it would do the job by allowing me to hook into a root HKEY and write to a completely different HKEY. So I started looking in the Delphi source code, and noticed that this function was not imported. The following code snippet shows the procedure that I ended up writing to have anything written to HKCR be written to HKCU instead.


function RegOverridePredefKey(hKey: HKEY; hNewKey: HKEY): Longint; stdcall; 
  external advapi32 name 'RegOverridePredefKey';

procedure OverrideRegistryKey(Register: boolean);
var
  HKCU: HKEY;
  ret: integer;
begin
  if Register then
  begin
    RegOpenKeyEx(HKEY_CURRENT_USER, 'Software\Classes', 0, HEY_ALL_ACCESS, HKCU);
    try
      ret := RegOverridePredefKey(HKEY_CLASSES_ROOT, HKCU);
      if ret <> ERROR_SUCCESS  then
        ShowMessage('Error overriding HKCU:' + #13#10 + SysErrorMessage(ret));
    finally
      RegCloseKey(HKCU);
    end;
  end
  else
    RegOverridePredefKey(HKEY_CLASSES_ROOT, 0);
end;
So that left me with the question "Where do I call this?". My first thought was to hook into the UpdateRegistry method and call my OverrideRegistry procedure there. Unfortunately, by the time UpdateRegistry is called in my Remote DataModule (RDM), several other registration calls have already finished. This left me with some things in HKLM and some in HKCU. Then I remembered that the COM registration occurs when Application.Initialize is called in the dpr file. The following is the code I use in my dpr to write to the right place in the registry.

  OverrideRegistryKey(true);
  try
    Application.Initialize;
  finally
    OverrideRegistryKey(false);
  end;
After doing this, I checked in regedit and verified that everything was registered in HKCU, and it was there. Since HKCR shows the merge of HKLM and HKCU, it was also present in HKCR. This means that COM calls will find the app server. Running a quick test showed me that everything worked as I expected.

In my production code, I'll also put in a quick check to make sure that we're running on Windows 2000 or greater. Since we run both as a Server and a Service, I'll put this code in a conditional define, since there is no concept of HKCU in a service.

Note: I also failed to find the header for RegOpenUserClassesRoot in Delphi. If you find that you need this function, you'll need to import the function yourself.

Thursday, September 23, 2004 3:48:00 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -
Delphi
 Thursday, September 16, 2004
Joe White took some notes on NDataStore. I will be keeping a very, very close eye on this DB. I have used JDataStore in past lives, and it was an extreme pleasure to use it. Steve Shaugnessy really knows how to eek performance out of a DBMS.
Thursday, September 16, 2004 9:30:00 AM (Central Standard Time, UTC-06:00)  #    Comments [3] -
ADO.NET
 Wednesday, September 15, 2004
Take a look at this sneak peek of DiamondBack presented by John Kaster. Major things I noticed just in this 14 minute demo were:
  • Language bundling announement (C#, Delphi, Win32)
  • for..in syntax (similar to C# foreach syntax)
  • A new start page (to replace DelphiDirect, most likely)
  • Refactoring support (available in all languages)
  • Error Insight
  • Help Insight
  • History view (including smart diff engine. Similar to JBuilder's feature of the same name - very nice)
  • Tight StarTeam integration
  • Free floating form designer (a la Delphi 7 and below)
  • Delphi Win32 was enhanced to support many extensions created in Delphi .NET
Wow!! It seems pretty full-featured, and I didn't even see all of the features first-hand at BorCon. I can't wait to get my hands on it. Well done, John and Borland.
Wednesday, September 15, 2004 1:05:00 PM (Central Standard Time, UTC-06:00)  #    Comments [1] -
Delphi
 Tuesday, September 14, 2004
Yup, it's time to talk about ActiveForms again. :-)

If you're using ActiveForms, then you need to know about an ugly little secret. If you use MyDelphiForm.ActiveControl := Edit1, then you will get an exception stating 'Cannot focus a disabled or invisible window'. The reason for this is due to the implementation of TCustomForm.SetActiveControl. This setter method is called when you try to set ActiveControl on a form. The first thing it does is this:


    if not ((Control = nil) or (Control <> Self) and
      (GetParentForm(Control) = Self) and ((csLoading in ComponentState) or
        Control.CanFocus)) then
      raise EInvalidOperation.Create(SCannotFocus);
Note the part where it is trying to assert that the root ParentForm for the Control you are trying to set focus to is the same as the current form. This works fine in regular windows applications, but unfortunately in ActiveForms, the root ParentForm is the actual ActiveForm. Most people use that form as nothing more than a container to then host their forms inside. I wrote about this technique in a previous Delphi Informant, and it has been posted plenty of places on the Internet as well (e.g. Conrad Hermann had the first mention of this that I can remember).

The workaround is to use code like this to set the ActiveControl:


var
  ParentForm: TCustomForm;
begin
  ParentForm := GetParentForm(Self);
  ParentForm.ActiveControl := Edit1;
Tuesday, September 14, 2004 9:37:00 AM (Central Standard Time, UTC-06:00)  #    Comments [1] -
Delphi
 Thursday, September 09, 2004
An ActiveForm will not receive a WM_ACTIVATE message when initializting since WM_ACTIVATE only goes to the top level windows, or in this case, the IE browser window. By sending our own WM_ACTIVATE, we get initial focus set to the ActiveForm, but more importantly, the WMActivate method in TCustomForm calls TCustomForm.SetActive. This in turn, sets the Active property which means that the form has focus. This is important later on, e.g. in TCustomForm.SetActiveControl, the focus will never be set to the ActiveControl.

For example, the code below will do nothing in the default case. It will work fine after you apply the work-around mentioned later on.


var
  ParentForm: TCustomForm;
begin
  ParentForm := GetParentForm(Self);
  ParentForm.ActiveControl := Edit1;
end;

The bad behavior is also apparent when using TPageControl, since TPageControl.ChangeActivePage tries to set the ActiveControl when changing pages.

The simple work-around for this is to call the following in your ActiveForm code: PostMessage(Handle, WM_ACTIVATE, WA_ACTIVE, 0);

Since I use PARAM tags in my OBJECT tag to pass parameters, I am doing this in IPersistPropertyBagLoad method after creating my Delphi form. After doing this, everything is working great.

Thanks to Steve Trefethen for listening to my original vent. :-)

Thursday, September 09, 2004 9:01:00 AM (Central Standard Time, UTC-06:00)  #    Comments [1] -
Delphi
Navigation
Archive
<October 2004>
SunMonTueWedThuFriSat
262728293012
3456789
10111213141516
17181920212223
24252627282930
31123456
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2008
Dan Miser
Sign In
Statistics
Total Posts: 307
This Year: 22
This Month: 1
This Week: 1
Comments: 604
All Content © 2008, Dan Miser
DasBlog theme 'Business' created by Christoph De Baene (delarou)