Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Dropdown won't select with mouse, only keyboard

Status
Not open for further replies.

foxmuldr3

Programmer
Jul 19, 2012
166
US
Haven't seen this before. We have a customer that purchased three new 24-CPU Dell machines with Windows 11. The FoxPro apps we have don't allow the combobox dropdowns to work without using the keyboard. You can click on the control to bring up the dropdown list, and then arrow up/down and press Enter to choose something, but it won't interact with the mouse other than to close the dropdown when you click on any item or the scrollbar.

Has anyone seen this before?

I did notice on these machines that when we ran our installer, it did not install MSCOMCTL.OCX and MSCOMCT2.OCX automatically. We had to go in behind and install/register them manually. I'm assuming it's something similar with the underlying dropdown control, since other dropdowns work (in Excel, for example, to choose a font size).

--
Rick C. Hodgin
 
I wrote the C++ program below, and here are the results when I open a dropdown in Windows. It produced the following output when the dropdown opens up:

Code:
* Dropdown is opened with mouse click:
#1 -- New window for popup
Add 002e0e88 <no title>
X=56, Y=122, W=560, H=53
Class = vfp994000003
[caption][popup][child][disabled][clipsiblings][caption][border][dlgframe][sysmenu][thickframe][sizebox][tiledwindow]
  Parent = 00080a5a Microsoft Visual FoxPro

#2 -- Shadow
Add 00930a26 <no title>
X=56, Y=122, W=565, H=58
Class = SysShadow
[overlapped]

* Dropdown is closed by losing focus:
#1 -- Delete shadow
Del 00930a26 <no title>

* Dropdown is again opened by mouse click:
#1 -- Delete prior popup
Del 002e0e88 <no title>

#2 -- Add new window just as an overlapped window though
Add 002f0e88 <no title>
X=56, Y=122, W=560, H=53
Class = vfp994000003
[overlapped]
  Parent = 00080a5a Microsoft Visual FoxPro

#3 -- Add shadow
Add 002609de <no title>
X=56, Y=122, W=565, H=58
Class = SysShadow
[overlapped]

* Dropdown is closed by losing focus:
Del 002609de <no title>

Here is the source code to replicate what I did. Compiled in Visual Studio as an x86 (32-bit) project (due to use of GWL_HINSTANCE).

Code:
// GlobalWindows.cpp

#include <windows.h>
#include <list>

struct SHwnd
{
    bool    lDeleted;
    bool    lChanged;
    bool    lNew;
    bool    lDisplayed;

    union
    {
        HWND    hwnd;
        int     _hwnd;
    };
};

std::list<SHwnd*>     hwnds;

bool iiLookupHwnd(HWND hwnd)
{
    SHwnd*                          h;
    std::list<SHwnd*>::iterator     hi;


    // Iterate through the list
    for (hi = hwnds.begin(); hi != hwnds.end(); ++hi)
    {
        // Grab the structure
        h = *hi;

        if (h->hwnd == hwnd)
        {
            // This one matches
            h->lChanged = false;
            return true;
        }
    }

    // If we get here, not found
    return false;
}

void iiAppendHwnd(HWND hwnd)
{
    SHwnd*      h;


    // Create it
    h             = new SHwnd;
    h->hwnd       = hwnd;
    h->lChanged   = true;
    h->lNew       = true;
    h->lDeleted   = false;
    h->lDisplayed = false;

    // Add to the list
    hwnds.push_back(h);
}

BOOL CALLBACK EnumChildWindowsProc(HWND hwnd, LPARAM l)
{
    // Store the window
    if (!iiLookupHwnd(hwnd))
        iiAppendHwnd(hwnd);

    EnumChildWindows(hwnd, EnumChildWindowsProc, NULL);
    return TRUE;
}

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM l)
{
    // Store the window
    if (!iiLookupHwnd(hwnd))
        iiAppendHwnd(hwnd);

    EnumChildWindows(hwnd, EnumChildWindowsProc, NULL);
    return TRUE;
}

int main()
{
    bool        llFirst;
    RECT        lrc;
    wchar_t     title[1024];
    WNDCLASSW   wc;

    SHwnd*                          h;
    std::list<SHwnd*>::iterator     hi;

    union
    {
        HWND    hwndP;
        int     _hwndP;
    };


    // Enter a loop to repeatedly show differences
    llFirst = true;
    while (true)
    {
        // Mark everything changed, which means if it exists now and it's still marked changed at the end, then it's been deleted
        for (hi = hwnds.begin(); hi != hwnds.end(); ++hi)
            (*hi)->lChanged = true;

        // Get all windows, and child windows, and child-of-child windows...
        EnumWindows(EnumWindowsProc, NULL);

        // Report differences
        if (!llFirst)
        {
            // Display any changes
            for (hi = hwnds.begin(); hi != hwnds.end(); ++hi)
            {
                h = *hi;
                if (h->lChanged && (IsWindowVisible(h->hwnd) || h->lDisplayed))
                {
                    // Get the window title/caption
                    h->lDisplayed = true;
                    GetWindowText(h->hwnd, title, sizeof(title) / sizeof(title[0]));

                    // Display the event
                    h->lDeleted = (!h->lNew && h->lChanged);
                    if (h->lDeleted)
                    {
                        // Window has been deleted
                        wprintf(L"%s %08x %s\n", L"\nDel", h->_hwnd, ((title[0] == 0) ? L"<no title>" : title));

                    } else {
                        // New window
                        wprintf(L"%s %08x %s\n", L"\nAdd", h->_hwnd, ((title[0] == 0) ? L"<no title>" : title));
                        GetWindowRect(h->hwnd, &lrc);
                        wprintf(L"X=%d, Y=%d, W=%d, H=%d\n", lrc.left, lrc.top, lrc.right - lrc.left, lrc.bottom - lrc.top);
                        GetWindowLong(h->hwnd, GWL_HINSTANCE);
                        GetClassName(h->hwnd, title, sizeof(title) / sizeof(title[0]));
                        GetClassInfo((HINSTANCE)GetWindowLong(h->hwnd, GWL_HINSTANCE), title, &wc);
                        wprintf(L"Class = %s\n", title);

                        if (wc.style & WS_CAPTION)          wprintf(L"[caption]");
                        if (wc.style == WS_OVERLAPPED)      wprintf(L"[overlapped]");
                        if (wc.style & WS_POPUP)            wprintf(L"[popup]");
                        if (wc.style & WS_CHILD)            wprintf(L"[child]");
                        if (wc.style & WS_MINIMIZE)         wprintf(L"[minimize]");
                        if (wc.style & WS_VISIBLE)          wprintf(L"[visible]");
                        if (wc.style & WS_DISABLED)         wprintf(L"[disabled]");
                        if (wc.style & WS_CLIPSIBLINGS)     wprintf(L"[clipsiblings]");
                        if (wc.style & WS_CLIPCHILDREN)     wprintf(L"[clipchildren]");
                        if (wc.style & WS_MAXIMIZE)         wprintf(L"[maximize]");
                        if (wc.style & WS_CAPTION)          wprintf(L"[caption]");
                        if (wc.style & WS_BORDER)           wprintf(L"[border]");
                        if (wc.style & WS_DLGFRAME)         wprintf(L"[dlgframe]");
                        if (wc.style & WS_VSCROLL)          wprintf(L"[vscroll]");
                        if (wc.style & WS_HSCROLL)          wprintf(L"[hscroll]");
                        if (wc.style & WS_SYSMENU)          wprintf(L"[sysmenu]");
                        if (wc.style & WS_THICKFRAME)       wprintf(L"[thickframe]");
                        if (wc.style & WS_MINIMIZEBOX)      wprintf(L"[minimizebox]");
                        if (wc.style & WS_MAXIMIZEBOX)      wprintf(L"[maximizebox]");
                        if (wc.style & WS_TILED)            wprintf(L"[tiled]");
                        if (wc.style & WS_ICONIC)           wprintf(L"[iconic]");
                        if (wc.style & WS_SIZEBOX)          wprintf(L"[sizebox]");
                        if (wc.style & WS_TILEDWINDOW)      wprintf(L"[tiledwindow]");
                        wprintf(L"\n");

                        hwndP = (HWND)GetWindowLong(h->hwnd, GWL_HWNDPARENT);
                        while (hwndP != NULL)
                        {
                            GetWindowText(hwndP, title, sizeof(title) / sizeof(title[0]));
                            wprintf(L"  Parent = %08x %s\n", _hwndP, ((title[0] == 0) ? L"<no title>" : title));
                            hwndP = (HWND)GetWindowLong(hwndP, GWL_HWNDPARENT);
                        }
                    }

                    // Lower the new flag
                    h->lNew = false;
                }
            }
        }
        llFirst = false;

        // Remove all the deleted ones
        std::list<SHwnd*>::reverse_iterator hir;
        for (hir = hwnds.rbegin(); hir != hwnds.rend(); ++hir)
        {
            h = *hir;
            if (h->lDeleted)
            {
                hwnds.remove(h);
                free(h);
            }
        }
    }
}

--
Rick C. Hodgin
 
Good, in parallel I extended the EnumWindows usage and can confirm that opening a combobox does raise the count by 1 and closing it lowers it again...

Code:
#INCLUDE vfp2c.h

Clear
Set Library To vfp2c32.fll Additive

Public goLogging, goTimer
goLogging = Createobject("Logging")
goTimer = CreateObject("WindowsTimer")

Activate Screen
? 'Initial count'
goTimer.CountNow()

Local loForm
loForm = Createobject("form")
loForm.Left=200
loForm.Show()

Activate Screen
? 'Form started'
goTimer.CountNow()

Create Cursor c_data ;
   ( ;
   cItem      c(120) ;
   )

Insert Into c_data Values ("Item 1 Item 1 Item 1 Item 1 Item 1 Item 1 Item 1 Item 1 Item 1 Item 1 Item 1")
Insert Into c_data Values ("Item 2 Item 2 Item 2 Item 2 Item 2 Item 2 Item 2 Item 2 Item 2 Item 2 Item 2")
Insert Into c_data Values ("Item 3 Item 3 Item 3 Item 3 Item 3 Item 3 Item 3 Item 3 Item 3 Item 3 Item 3")

loForm.AddObject("combobox1","combobox")
With loForm.combobox1
   .RowSource = "c_data"
   .RowSourceType = 2
   .Visible=.T.
EndWith

Activate Screen
? 'combobox added'
goTimer.CountNow()
Keyboard '{Alt+DNARROW}'
? 'dropdown expanded'
goTimer.CountNow()
? 'any change monitored every 10ms'

On Key Label CTRL+F5 Clear Events
Activate Screen
? 'CTRL+F5 to exit'
Read Events
goTimer = .null.
goLogging = .null.
Release All LIKE go*

Define Class WindowsTimer As Timer
   Interval = 10
   
   Procedure Timer()
      Local loParents, loChildren
      loChildren = Createobject('WNDENUMPROC')
      loParents  = Createobject('WNDENUMPROC',loChildren)

      Declare Integer EnumWindows In user32.Dll Integer, Integer
      Declare Integer EnumChildWindows In user32.Dll Integer, Integer, Integer

      EnumWindows(loParents.Address,0)
      loParents = .Null.
      loChildren= .Null.
   EndProc
   
   Procedure CountNow()
       This.Timer()
   EndProc
Enddefine

Define Class Logging As Session
   LastCount = 0

   Procedure Log(tnCount)
      If This.LastCount != tnCount
         This.LastCount  = tnCount
         Activate Screen
         ? Seconds(), tnCount
      Endif
   Endproc
Enddefine

Define Class WNDENUMPROC As Exception
   Address = 0
   nCount = 0
   oChildren = .Null.

   Function Init(toChildren)
      If Pcount()=1
         This.oChildren = toChildren
      Endif
      This.Address = CreateCallbackFunc('EnumWindowsCallback','BOOL','LONG, LONG',This)
   Endfunc

   Function Destroy
      If This.Address != 0
         DestroyCallbackFunc(This.Address)
      Endif
      If Not Isnull(This.oChildren)
         goLogging.Log(This.nCount)
      Endif
   Endfunc

   Function EnumWindowsCallback(hHwnd,Lparam)
      This.nCount = This.nCount + 1
      If Not Isnull(This.oChildren)
         EnumChildWindows(hHwnd,This.oChildren.Address,0)
         This.nCount = This.nCount + This.oChildren.nCount
         This.oChildren.nCount = 0
      Endif
      Return .T.
   Endfunc
Enddefine

Now that we settled that, I still don't know why the list sometimes is mouse sensitive and sometimes not. In my reproducible scenario there's nothing in front of the dropdown list that would block off mouse events.

What is true is that even though the dropdown list is now identified to have its own hwnd it's not necessarily on top, a transparent container will block off mouse events, for example, but in the scenario it's not the reason.

I wanted to add the identification of the new hwnd and its associated rectangle, but that also won't contribute to why it does not react to mousemove/mousover events and in short the dropdown list is not usable with a mouse.

Chriss
 
Since the HWND of the popup is receiving mouse input, it has to send messages back to the parent HWND for VFP to identify the activity. I'm guessing the base number it's using is not being set properly for some reason, and the messages from the mouse are being sent back, but they're not being interpreted by the parent, so they're essentially being ignored.

The customer has also stated that "sometimes it works," which lends credence to that theory, in that there may be a race condition that's allocating for a message base range to send back on, and it's sometimes getting it, sometimes not.

If so, there's no fix for this other than possibly to force an affinity to a single CPU for the VFP app. I might try that.

--
Rick C. Hodgin
 
Setting affinity did not work. Same issue as before.

--
Rick C. Hodgin
 
Another wild guess. Since the new Dell computers seem to be very fast, could it be a timing issue somewere, either in the control or in the way it’s getting created on the form.

I remember many many moons ago we had a problem with FPW 2.6 on high speed CPU’s. Luckily the fpw2600.esl runtime file got fixed, I think in 2000 or 2001.

Regards, Gerrit
 
Is it something in front of the combobox, transparent, invisible, but still blocking?

For example, a container does block mouse usage of a combobox:
Code:
Clear

Local loForm
loForm = Createobject("form")
loForm.Show()

Create Cursor c_data ;
   ( ;
   cItem      v(120) ;
   )

Insert Into c_data Values ("Item 1")
Insert Into c_data Values ("Item 2")
Insert Into c_data Values ("Item 3")

*add combobox
loForm.AddObject("combobox1","combobox")
With loForm.combobox1
   .RowSource = "c_data"
   .RowSourceType = 2
   .Visible=.T.
EndWith

*add container in front of combobox (later added objects are in front of previously added)
loForm.AddObject("container1","container")
loForm.container1.width=loForm.width
loForm.container1.height=loForm.height
loForm.container1.backstyle= 0
loForm.container1.visible = .t.

Keyboard '{ALT+DNARROW}' 

On Key Label CTRL+F5 Clear Events
Activate Screen
? 'CTRL+F5 to exit'
Read Events

As Griff said (a long time ago in a galaxy far far away), the form might not be designed intentionally to have something in front of controls that should be usable, but faulty resizing of form controls might cause that.

Besides that possible reason it won't explain scenarios that sometimes work, sometimes not, but it shows that even though the dropdown list window hwnd is created last at runtime, VFP respects the transparent container in front of the list blocking mouse events from it, the events only arrive at the container object, which you can see when you program a container with click code, mouseenter, mousemove, mouseleave, etc.

In short, if a control is in front of others, that blocks off mouse events to the controls behind it, no matter if the objects in front are transparent or not.

Chriss
 
I'm going to write an extension to a utility DLL I have that will hook into the WndProc() functions used for both windows, intercepting every message. I'm going to compare the messages sent from a system that's working (my PC), and the ones that aren't working, to see if I can see where there's a message discrepancy.

--
Rick C. Hodgin
 
Well, that should help. Before I'd do that, you could of course rule out resizing problems, if it's all your code and you know it inside out anyway, then we'd also not need to guess into that direction. Anything else would be a bug, and I don't think there's a mouse events related bug in VFP9, that would be found very very late. Then I'd rather bet on driver issue, again.

There is one more thing that could happen with anything in VFP that's usually working: A change of Windows could cause VFP to not work properly, because VFP is out of even extended support and not taken into account when Windows changes are made and tested against the codebase of all currently supported MS products to not break them. But that's a cause I'd put into the least probable category the type of conspiracy theories.

Drivers, I remember Dell was nototriously known for driver issues. They got better and so eventually that suspicion about Dell computers having driver issues was dropped, but I wouldn't be surprised, when it turns out the mouse drivers don't feed the Windows event queue correctly, however that would cause trouble only in some controls and not others.

Just a tip about resizing problems of controls moving in front of others: Anchoring is hard to imagine how it effects this, because there's absolute and relative anchroing, anchroing to form border or container border and more. And there's no code involved to cause trouble, just the anchor property setting. Besides, it even sometimes still surprises me by which base (anchor) position anchoring works, when you set control positions and sizes before changing the Anchor property from 0 to some anchroing behavior. In short: It's complicated. And it's easy to achieve some not intended overlap of controls by anchoring.

Chriss
 
Chris, I think you and I would do well collaborating on projects together.

Ever want to work on Visual FreePro?

--
Rick C. Hodgin
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top