We help IT Professionals succeed at work.

GetCurSel() for a CComboBox is returns 2 different values on consecutive calls

emitchell
emitchell asked
on
I am trying to develop a dropdown combobox that will open up when sufficient leading characters have been entered to uniquely match one of the strings. This was working fine until I added a PreTranslateMessage() handler to look for an Enter (return) key which would close the combobox dropdown. I want to use this in a dialog where the Enter key will do the default action. Without this modification, the user has to hit the enter key twice, once to close the drop down and again for the default OnOK action of the dialog. Thus I look for the Return key and close the drop down. It's at this point that the GetCurSel() returns a -1 for some funny reason.

This is the listing for this message handler:

////////////////////////////////////////////////////////////////////////
//
// we need to check for backspace or delete. when we see
// one of these characters, we don't do the autocomplete
// since the same text would be written over the deleted stuff
//
BOOL
CAutoComboBox::PreTranslateMessage(MSG* pMsg)
{
    // if we have a key pressed and we are autocompleting
    if(pMsg->message == WM_KEYDOWN) {
        int key = pMsg->wParam;
        if(m_bAutoComplete) {
            // set to not autocomplete on the next real character
            if(key == VK_DELETE || key == VK_BACK) m_bAutoComplete = false;
        }
        // ?? remove
        int iSelectedRow = GetCurSel();
        int iCount = GetCount();
        TRACE(_T("PreTranslate1::m_iSelectedRow=%d: iSelectedRow=%d\n")
         , m_iSelectedRow, iSelectedRow);
        TRACE(_T("Key=%d: Count=%d\n"), pMsg->wParam, iCount);

        // if we have the enter key, close the drop down here so that
        // we only have to hit enter once
        if(key == VK_RETURN) {

            // this is due to a problem of sail numbers 1 and 100. the
            // iSelectedRow is sometimes CB_ERR for an unknown reason
            // and the nested SelChange message will change the internal
            // selected row (m_iSelectedRow)
            int iSelectedRowSave = m_iSelectedRow;
            ShowDropDown(FALSE);
            // if closing the drop down has changed the selected row
            if(m_iSelectedRow != iSelectedRowSave) {
                m_iSelectedRow = iSelectedRowSave;
            }
        }
        // ?? remove
        iSelectedRow = GetCurSel();
        TRACE(_T("PreTranslate2::m_iSelectedRow=%d: iSelectedRow=%d\n")
         , m_iSelectedRow, iSelectedRow);
    }
    return CComboBox::PreTranslateMessage(pMsg);
}

The TRACEs are for me to try and track down what is happening. I've also added code to compensate for the bad value of the selected row.

The ShowDropDown(FALSE) issues an CBN_SELCHANGE notification messsage that I have connected to my own routine as follows:

void
CAutoComboBox::OnSelChange()
{
    int iSelectedRow = GetCurSel();
    int iCount = GetCount();

    // ?? remove
    TRACE(_T("OnSelChange1::iSelectedRow=%d: m_iSelectedRow=%d\n")
     , iSelectedRow, m_iSelectedRow);

    // only change the selected row if a real selection
    if(iSelectedRow >= 0) {
        m_iSelectedRow = iSelectedRow;
    }
    // make sure that we only use a good row
    else if(m_iSelectedRow < iCount) {
        SetCurSel(m_iSelectedRow);
    }
    else {
        // this indicates no current selection
        m_iSelectedRow = -1;
    }

    // ?? remove
    CString line;
    // get the text from the user input
    GetWindowText(line);

    // ?? remove
    iSelectedRow = GetCurSel();
    TRACE(_T("OnSelChange2::iSelectedRow=%d: m_iSelectedRow=%d: line=%s\n")
     , iSelectedRow, m_iSelectedRow, (LPCSTR) line);
}

My combobox has 15 entries of which the first 3 lines are as follows:

    0
    1
    100
    ...

and I type in 1 and then a 0. The first 1 matches the second line. The second character, the 0, then allows the unique match to the 100. At this point I hit the Enter key to do the selection.

Now the output in the debug window is as follows:

...
 1> PreTranslate1::m_iSelectedRow=-1: iSelectedRow=-1
 2> Key=49: Count=15
 3> PreTranslate2::m_iSelectedRow=-1: iSelectedRow=-1
 4> SelectedRow: 1 Line: 1 Matched Text: 1
 5> PreTranslate1::m_iSelectedRow=1: iSelectedRow=-1
 6> Key=48: Count=15
 7> PreTranslate2::m_iSelectedRow=1: iSelectedRow=-1
 8> SelectedRow: 2 Line: 10 Matched Text: 100
 9> PreTranslate1::m_iSelectedRow=2: iSelectedRow=-1
10> Key=13: Count=15
11> OnSelChange1::iSelectedRow=1: m_iSelectedRow=2
12> OnSelChange2::iSelectedRow=1: m_iSelectedRow=1: line=100
13> PreTranslate2::m_iSelectedRow=2: iSelectedRow=1

The 1 key is listed as 49. Line 4 comes from the matching part of the code and says that we have selected line 1 by matching a text string of "1". The next character the zero or key 48 shows up as the PreTranslate1:: of line 5. Here the iSelectedRow is -1 or CB_ERR. There really is a selected row though, row one. We continue through to the Return (13) on line 10 and again the selected row is returned as CB_ERR or -1. This also shows the OnSelChange action as a result of the ShowDropDown(FALSE). In this code, the GetCurSel() returns a 1 (line 11 shows the iSelectedRow at this point). This is the selection of two characters ago!

I ran Spy to log the messages that were being sent to the combobox and got the following:

<00001> 00000A14 S CB_GETCURSEL
<00002> 00000A14 R CB_GETCURSEL index:CB_ERR
<00003> 00000A14 S CB_GETCOUNT
<00004> 00000A14 R CB_GETCOUNT cItems:15
<00005> 00000A14 S CB_GETCURSEL
<00006> 00000A14 R CB_GETCURSEL index:CB_ERR
<00007> 00000A14 S ..CB_FINDSTRING indexStart:0 lpszFind:0096AF8C ("1")
<00008> 00000A14 R ..CB_FINDSTRING index:1
<00009> 00000A14 S ..CB_GETLBTEXTLEN index:1
<00010> 00000A14 R ..CB_GETLBTEXTLEN cchText:1
<00011> 00000A14 S ..CB_GETLBTEXT index:1 lpszBuffer:0096AC6C
<00012> 00000A14 R ..CB_GETLBTEXT cchText:1 lpszBuffer:0096AC6C ("1")
<00013> 00000A14 S ..CB_SHOWDROPDOWN fShow:True
<00014> 00000A14 R ..CB_SHOWDROPDOWN lResult:True
<00015> 00000A14 S ..CB_SETCURSEL index:1
<00016> 00000A14 R ..CB_SETCURSEL lResult:0001
<00017> 00000A14 S ..CB_SETEDITSEL ichStart:1 ichEnd:65535
<00018> 00000A14 R ..CB_SETEDITSEL lResult:True
<00019> 00000A14 S CB_GETCURSEL
<00020> 00000A14 R CB_GETCURSEL index:CB_ERR
<00021> 00000A14 S CB_GETCOUNT
<00022> 00000A14 R CB_GETCOUNT cItems:15
<00023> 00000A14 S CB_GETCURSEL
<00024> 00000A14 R CB_GETCURSEL index:CB_ERR
<00025> 00000A14 S ..CB_FINDSTRING indexStart:0 lpszFind:0096AF8C ("10")
<00026> 00000A14 R ..CB_FINDSTRING index:2
<00027> 00000A14 S ..CB_GETLBTEXTLEN index:2
<00028> 00000A14 R ..CB_GETLBTEXTLEN cchText:3
<00029> 00000A14 S ..CB_GETLBTEXT index:2 lpszBuffer:0096AC6C
<00030> 00000A14 R ..CB_GETLBTEXT cchText:3 lpszBuffer:0096AC6C ("100")
<00031> 00000A14 S ..CB_FINDSTRING indexStart:2 lpszFind:0096AF8C ("10")
<00032> 00000A14 R ..CB_FINDSTRING index:2
<00033> 00000A14 S ..CB_SHOWDROPDOWN fShow:True
<00034> 00000A14 R ..CB_SHOWDROPDOWN lResult:True
<00035> 00000A14 S ..CB_SETCURSEL index:2
<00036> 00000A14 R ..CB_SETCURSEL lResult:0002
<00037> 00000A14 S ..CB_SETEDITSEL ichStart:2 ichEnd:65535
<00038> 00000A14 R ..CB_SETEDITSEL lResult:True
<00039> 00000A14 S CB_GETCURSEL
<00040> 00000A14 R CB_GETCURSEL index:CB_ERR
<00041> 00000A14 S CB_GETCOUNT
<00042> 00000A14 R CB_GETCOUNT cItems:15
<00043> 00000A14 S CB_SHOWDROPDOWN fShow:False
<00044> 00000A14 S ....CB_GETCURSEL
<00045> 00000A14 R ....CB_GETCURSEL index:1
<00046> 00000A14 S ....CB_GETCOUNT
<00047> 00000A14 R ....CB_GETCOUNT cItems:15
<00048> 00000A14 S ....CB_GETCURSEL
<00049> 00000A14 R ....CB_GETCURSEL index:1
<00050> 00000A14 R CB_SHOWDROPDOWN lResult:True
<00051> 00000A14 S CB_GETCURSEL
<00052> 00000A14 R CB_GETCURSEL index:1
<00053> 00000A14 S .CB_GETDROPPEDSTATE
<00054> 00000A14 R .CB_GETDROPPEDSTATE fDropped:False

The first part is the matching of the strings. However, on both lines <00019> and <00039> is the CB_GETCURSEL that returns the CB_ERR.

Line <00043> is the CBN_SHOWDROPDOWN and then we get the nested message to the OnSelChange() routine which does the CB_GETCURSEL with a return of 1.

How can it be that on line <00039> the current selection is a CB_ERR but the message almost immediately after (albeit nested) returns a one.  The one is wrong though since the last set was on line <00036> where the current selection was set to 2.

It's a shame that we can't see inside the combo box code and so don't know where the numbers are coming from. If I could watch the place where the current selection was stored, I could find the code that changed it!
Comment
Watch Question

CERTIFIED EXPERT
Author of the Year 2009

Commented:
From MSDN:
The CBN_SELCHANGE notification message is sent when the user changes the current selection in the list box of a combo box. The user can change the selection by clicking in the list box or by using the arrow keys. The parent window of the combo box receives this notification in the form of a WM_COMMAND message with CBN_SELCHANGE in the high-order word of the wParam parameter.

and,,,

The CBN_SELCHANGE notification message is not sent when the current selection is set using the CB_SETCURSEL message.

So it is not surprizing that you are not getting notifications (onSelChange) when you change the selection programmatically.

You might consider calling your OnSelChange fn yourself, or just changing your algorithm with this important detail in mind.

-- Dan


Author

Commented:
I do get the correct selection change when I find the row that I want to identify. My problem came about when I put in the PreTranslateMessage(...) code to trap the Enter key. There is something funny with the two dots that indicate a nested message from the Spy log. If I copy just a part of this log:

...
<00035> 00000A14 S ..CB_SETCURSEL index:2
<00036> 00000A14 R ..CB_SETCURSEL lResult:0002
<00037> 00000A14 S ..CB_SETEDITSEL ichStart:2 ichEnd:65535
<00038> 00000A14 R ..CB_SETEDITSEL lResult:True
<00039> 00000A14 S CB_GETCURSEL
<00040> 00000A14 R CB_GETCURSEL index:CB_ERR
<00041> 00000A14 S CB_GETCOUNT
<00042> 00000A14 R CB_GETCOUNT cItems:15
<00043> 00000A14 S CB_SHOWDROPDOWN fShow:False
<00044> 00000A14 S ....CB_GETCURSEL
<00045> 00000A14 R ....CB_GETCURSEL index:1
<00046> 00000A14 S ....CB_GETCOUNT
<00047> 00000A14 R ....CB_GETCOUNT cItems:15
<00048> 00000A14 S ....CB_GETCURSEL
<00049> 00000A14 R ....CB_GETCURSEL index:1
...

On line <00035> I've found the index to be 2 (matches the 100 of the 10 entered in the third row) and set it.

The next key stroke is the Enter which goes to the PreTranslateMessage(...) function.  The GetCurSel() on line <00039> returns CB_ERR and the line is listed without the two periods that I believe indicates some sort of nesting. In fact all the GetCurSel() returns show a CB_ERR when there are no lead periods.

At line <00043> I close the drop down window which appears to send the SelChange message but it's not shown in the Spy listing. I say that this must be so because the TRACE output indicates that I have gone to the OnSelChange() routine. In this there is a GetCurSel() that shows up in the Spy trace (line <00044>) that says that the selection is now 1 and the line has 4 dots in front of this.

I'm trying to find out why when I make what appears to be three successive class to GetCurSel() I get three different results, CB_ERR, 2 and 1.

More background. The ComboBox is a drop down variety, not a drop down list. I have a derived class CAutoComboBox that traps any update to the edit box, only allowing characters that are part of the drop down list and completing the selection when there are sufficient characters entered for uniqueness. I extracted the CAutoCombBox class from my main (big) app and I get the same effect with a small test program. I can send this if the actual listing would help.
CERTIFIED EXPERT
Author of the Year 2009

Commented:
Just for laffs, stop checking for Enter, but take its action whenever you press a Z.  That will indicate some special low-level handling for the Enter key.

Also, You could play around with *not* passing Enter on to the base class OnPreTranslate... To solve your double-enter problem, just use PostMessage to post an Enter Key to the parent dialog or to the control (I'd try both).

Most of your problems could be those dots... the combobox is sending messages to itself and you are taking actions that it does not expect (sending messages).  If there are ways to defer your actions (such as PostMessage rather than SendMessage or by setting a flag that means "do this later") then your problems may be solved.

-- Dan

Author

Commented:
Not much success. I changed the Enter to test for a Z and get the same results except that I eventually had to push Enter to do the dialog default OK button.

I then changed the PreTranslateMessage() code to include:

            // if the list box is visible
            if(GetDroppedState()) {
                iSelectedRow = GetCurSel();

                // close it up
                ShowDropDown(FALSE);
                PostMessage(WM_KEYDOWN, VK_RETURN, 0);
                return TRUE;
            }

when I had just found the VK_RETURN key press.

Then I used the debugger to stop on the above GetCurSel() and found the handle. Stepping into this GetCurSel() leads to the execution of:

::SendMessage(m_hWnd, CB_GETCURSEL, 0, 0);

I checked the handle (0x1664) and the return value is -1 (CB_ERR).

The ShowDropDown(FALSE) sends the OnSelChange() message where again I had set a breakpoint. Stepping in the GetCurSel() routine had the the same handle for the above SendMessage() call but the return at this point was 1.

How can two successive SendMessages to the same hWnd return different selections?

The modification to handle the VK_RETURN didn't really change anything because the odd behaviour manifested itself in the first call to PreTranslateMessage(). The GetCurSel() in this routine consistently returns -1.

Author

Commented:
Not much success. I changed the Enter to test for a Z and get the same results except that I eventually had to push Enter to do the dialog default OK button.

I then changed the PreTranslateMessage() code to include:

            // if the list box is visible
            if(GetDroppedState()) {
                iSelectedRow = GetCurSel();

                // close it up
                ShowDropDown(FALSE);
                PostMessage(WM_KEYDOWN, VK_RETURN, 0);
                return TRUE;
            }

when I had just found the VK_RETURN key press.

Then I used the debugger to stop on the above GetCurSel() and found the handle. Stepping into this GetCurSel() leads to the execution of:

::SendMessage(m_hWnd, CB_GETCURSEL, 0, 0);

I checked the handle (0x1664) and the return value is -1 (CB_ERR).

The ShowDropDown(FALSE) sends the OnSelChange() message where again I had set a breakpoint. Stepping in the GetCurSel() routine had the the same handle for the above SendMessage() call but the return at this point was 1.

How can two successive SendMessages to the same hWnd return different selections?

The modification to handle the VK_RETURN didn't really change anything because the odd behaviour manifested itself in the first call to PreTranslateMessage(). The GetCurSel() in this routine consistently returns -1.
CERTIFIED EXPERT
Author of the Year 2009

Commented:
>>How can two successive SendMessages to the same hWnd return different selections?

My theory is that the control is in the processing of changing the selection when you ask it for the current selection.  It is in effect, "no can do... I'll do you a favor by not crashing, but any other value that I sent would be misleading..."

It is probably complicated by the fat that it is a dropdowm, not a droplist.  The Edit control that is at the top of a dropdown is a sort of kludge that's been in Windows since day 1.  It works when you don't try to do anything unusual, so nobody complains....

I think you are fighting it out with the control to see who gets to set the selection (you *do* know that the dropdown automaticlaly changes the selection as the user types...)

Would there be a significant loss of functionality if you switched to using a drop list?  

-- Dan

Author

Commented:
I think that I am going to try the DropList next as you suggest. I need however to be able to write in the edit control something that is a partial match. Say I have sail numbers:

    24556
    245337
    223513

When the user types a 2, a 4 and then a 5, I haven't got the unique match yet so I have to show a 245 in the edit control successively as 2, 24 and 245. As soon as the user next types a 3 say, I can then select the second row, show the  full 245337 with a highlight over the end 37 that hasn't been typed yet.

I was using the OnEditUpdate() message to capture the user's characters, using SetCurSel() when sufficient characters.

I'll modify the code to capture any printing characters in the PreTranslateMessage() and I hope, use the SetWindowText() to fill the edit box. I'm a little worried that the DropDownList won't allow me to change the edit field unless it is a real selection. In that case I'll have to get the handle to the edit box via GetComboBoxInfo(). With that I should be able to write on the edit field directly.

To capture the characters, should I be using the PreTranslateMessage() or handling something like WM_KEYDOWN?

Are you sure the DropDown changes the selection? I've never seen a change in my class from this. I know the DropDownList changes the selection but it usually is just from the first single character. Each character seems to change the selection but only based on what was last typed.

I'm proposing to keep all the characters away from the droplist combo box, just setting the selection when I have collected sufficient to find the match.

CERTIFIED EXPERT
Author of the Year 2009
Commented:
Oops, I misspoke myself.  It is a *droppist* that attempts to search for the best match.

In fact, in playing around, I'm not certain that there is any reason to do what you are doing!

Create a plain vanilla DropList combo.  Put this data into it:
24556
245337
223513

Click on it and type
   2
the 223513 is displayed (the fist matching string that starts with 2).  Next press
   4
The 245337 is displayed (the first item that starts with 24).  Press
   5
(no change).  Press
   5
the 24556 is displayed (the first item that starts with 2455.

And at any time, you can press Alt+down arrow to open the list and highlight the current choice.  Wait a few seconds and you can type
   2
to restart the sequence.  Press Alt+DownArrow and watch the list selection change as you type the digits.

Thus, the standard control already does what (I think) you were hoping to do with your customization.

-- Dan

Author

Commented:
I tried the drop list. I must admit that I thought that one just saw the first character and the box would show the first string that had this character. Then the next character showed a new string starting with this. But as you say, not so. If one knows what to type, eventually the unique match is found.

However, I think Microsoft's UI is terrible. The user has no feedback of what characters have been typed. If they enter 242 in the above, the number 24556 is shown (the 2 is correctly rejected) but with some extra characters it's easy to go back to show one of the other numbers. I can't quite figure out the logic chosen by Microsoft to handle non-matching characters but it's not obvious. I'd hate to give this to a neophyte customer and have them try and understand when the selection is correct.

I'd like to try writing on the Edit control inside the Combo box but I have to go back to Florida by boat (from Cape Cod) and will be away for about 3 weeks.

Can we keep this open until I get there?
CERTIFIED EXPERT
Author of the Year 2009

Commented:
sure.. just post a note and I'll get an email

Have nice trip.  Avoid sharks.

-- Dan

Author

Commented:
We are now in Florida after a two week trip by boat. No harks! The computer caught up with us finally.

I was able to get more information for the messages being handled by the combobox by using Paul DiLascia's PixieLib (http://www.dilascia.com) which is an excellent package.

Just to document what I did to see the messages, I created my CHookTrace class derived from Paul's CSubclasswnd:

class CHookTrace : public CSubclassWnd {

    DECLARE_DYNAMIC(CHookTrace);
    virtual LRESULT WindowProc(UINT msg, WPARAM wp, LPARAM lp);
};

and then defined my WindowProc as follows to trace what message are being sent to the control:

LRESULT
CHookTrace::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
    TRACEFN(_T("CHookTrace: %s %d(0x%x), %d(0x%x)\n")
     , _TR(msg), wp, wp, lp, lp);
    LRESULT lRes = CSubclassWnd::WindowProc(msg, wp, lp);
    TRACE(_T("= %d\n"), lRes);

    return lRes;
}

The _TR(...) macro is Paul's method for coming up with meaningful names for the messages. I had to add the definitions for the CB_* which are missing from PixieLib. The TRACEFN(...) sets an indent level through some of Paul's magic so that if there are any recursive message calls, they are visible with a one space indentation. The result of the message is also listed above after the subclassed WindowProc() has been called.

In the dialog that contained my problem combobox, I added the following to the InitDialog() function:

    ...
    // hook up to trace messages to this window
    m_hookTrace1.HookWindow(m_cbSailNumber);
    ...

where the m_cbSailNumber is the combobox member variable.

This works much better than Spy. Every time I use Spy I have to reboot the machine (Win98).

I changed the combobox to owner draw since I eventually want to have more than one column and I have to position the second and subsequent columns directly. I've shown the output that follows my Enter (key = 13). There has been a change. Now when I ask for the GETCURSEL in the PreTranslateMessage() routine, the answer is correctly row 3, not CB_ERR which is what I was seeing before. I trap the 13 and closeup the listbox. This corresponds to line 4 with a SHOWDROPDOWN false message. This triggers the DRAWITEM message for the owner draw style. The action is a 2 (line 13), change selection, for item (ID) 3 and says to set this to unselected (state is 0).

The next DRAWITEM (line 17) says it also changes the selection (Action = 2) and sets item 0 to selected.

    ...
00> CHookTrace: CB_GETCURSEL 0(0x0), 0(0x0)
01> = 3
02> PreTranslate1::m_iSelectedRow=3: iSelectedRow=3
03> Key=13
04> CHookTrace: CB_SHOWDROPDOWN 0(0x0), 0(0x0)
05>  CHookTrace: WM_CTLCOLORLISTBOX 1922(0x782), 3112(0xc28)
06>  = 2122
07>  CHookTrace: WM_DRAWITEM 1000(0x3e8), 7793518(0x76eb6e)
08>   Entering DrawItem
09>    CHookTrace: CB_GETLBTEXTLEN 3(0x3), 0(0x0)
10>    = 3
11>    CHookTrace: CB_GETLBTEXT 3(0x3), 9119836(0x8b285c)
12>    = 3
13>   DrawItem Action=2, ID=3, State=0x0, Rect(0,8a,3c,50)
14>  = 1
15>  CHookTrace: WM_CTLCOLORLISTBOX 1922(0x782), 3112(0xc28)
16>  = 2122
17>  CHookTrace: WM_DRAWITEM 1000(0x3e8), 7793518(0x76eb6e)
18>   Entering DrawItem
19>    CHookTrace: CB_GETLBTEXTLEN 0(0x0), 0(0x0)
20>    = 1
21>    CHookTrace: CB_GETLBTEXT 0(0x0), 9119836(0x8b285c)
22>    = 1
23>   DrawItem Action=2, ID=0, State=0x1, Rect(0,8a,0,14)
24>  = 1
25> ...

This is the key to the whole problem. From the indentation, it looks like all the messages are produced within the combobox itself, after I have sent the SHOWDROPDOWN message.

I don't know why the SHOWDROPDOWN false message would
unselect a row but even though one assumes this, what would allow it to then go and change the selection to something completely different?

Author

Commented:
Thanks for the feedback on the combobox. I finished up with a workaround. Set a flag in the autocombobox class when the PreTranslateMessage() sees the Enter key and then not allow any more changes. Once the return key is seen, it's closing down so I don't have to worry anymore.

It would be nice to see inside the combobox to see what Microsoft is doing.
CERTIFIED EXPERT
Author of the Year 2009

Commented:
>>It would be nice to see inside the combobox to see what Microsoft is doing.

It may be impossible to find, but some time ago, I answered (or at least replied to) a question in the JavaScript (or perhaps the HTML) section in which a person was having trouble making a behavior for a <SELECT> tag.  There was some Microsoft sample code that purported to make the <SELECT> work more like the standard dropbox with interactive keyboard searching.  So there is some Microsoft code available.  As I recall is was a mess, including several boolean flags to keep track of the current input state/mode.

-- Dan

Author

Commented:
This comment is just to finish off a somewhat inconclusive exchange.

The problem of a unsolicited OnSelChange message has been acknowledged by Microsoft to be a bug in Windows98 and similar OSs. It doesn't happpen in NT or XP.

The incident id for this is:  SRX020705601306

There is no fix!