user-0 提问: 组合框控件（combobox）

回复:
user: user-1
content: 那确实是包含WS_CHILD风格，可能是为了避免出现任务栏窗口

user: user-0
content: 引用 19 楼 早打大打打核战争 的回复:54808041是十六进制还是十进制  十六进制

user: user-1
content: 54808041是十六进制还是十进制

user: user-2
content: 引用 14 楼 六道佩恩 的回复:1. 顶级窗口的父窗口真的是桌面窗口吗？毕竟GetParent得到的是NULL，凭借对桌面窗口调用GetWindow函数得到第一个顶层窗口的句柄这一点应该也无法说明所有顶级窗口的父窗口是桌面窗口吧？主要是我一直也没看到微软文档说过顶级窗口的父窗口是桌面窗口，不过就“SetParent指定父窗口为NULL时设置桌面窗口为父窗口”，测试结果是设置后GetWindow仍然得到NULL，应该算佐证了“顶级窗口的父窗口真的是桌面窗口”吧。  先说点其他的。。。 因为我又去看了下msdn上GetParent的说明，发现GetParent有了更详细的说明，微软确实一直在更新啊。 以前的版本（vs2008）： If the window is a child window, the return value is a handle to the parent window. If the window is a top-level window, the return value is a handle to the owner window. If the window is a top-level unowned window or if the function fails, the return value is NULL  微软说了，这个函数可以获取owner窗口，但我实际测试时不行，我以前一直疑惑为什么这个函数获取不到我想要的owner窗口，直到我今天看到最新说明。。。  最新说明： If the window is a child window, the return value is a handle to the parent window. If the window is a top-level window with the WS_POPUP style, the return value is a handle to the owner window.  只有带了WS_POPUP风格后，对其GetParent，才能获取其owner  原来，我以前的窗口虽然不带WS_CHILD，但恰好也没有带WS_POPUP风格，而是WS_OVERLAPPED风格（0x00000000）。所以不行。 而且最新的说明还指明了这个函数返回NULL的情况：  This function typically fails for one of the following reasons:  1、The window is a top-level window that is unowned or does not have the WS_POPUP style. 2、The owner window has WS_POPUP style.  这又能看到另一个条件：owner 窗口还不能带WS_POPUP风格（当然也不能带WS_CHILD,必须是WS_OVERLAPPED）  当然，前面都说的是使用GetParent去获取owner的条件，言外之意是，即使获取失败，也并不代表它没有owner。 所以想要健壮的获取owner 窗口，还是用GW_OWNER 调用GetWindow比较好。 这在GetParent的Remark一节中，微软也是这么建议的，在Remark中，我还看到了微软建议的获取真正父窗口该用的函数！  To obtain a window's owner window, instead of using GetParent, use GetWindow with the GW_OWNER flag. To obtain the parent window and not the owner, instead of using GetParent, use GetAncestor with the GA_PARENT flag.  那么终于可以说回正题了，我说了那么多，其实就是想引出GetAncestor这个函数。。微软建议使用GA_PARENT调用GetAncestor来获取窗口的真正父窗口。我也实际测试了，确实可以：使用这个函数后，所有top-level窗口的父窗口确实都是桌面！ 我想这些应该就可以解答你的疑惑了。 但其实我觉得吧，没必要太纠结。毕竟在windows下开发，微软说什么就是什么。。。  引用 14 楼 六道佩恩 的回复:2. 我测试了下，只有子窗口获取父窗口时，才会显示桌面窗口的句柄。这个列表框为什么一定要设置为WS_CHILD？同为桌面窗口的子窗口的话，顶级窗口不行吗？  没太听懂你说的。你是不是说反了。  我说的明明是：下拉列表框必须是top-level的啊。。。因为它很可能需要超出它自己app的主窗口的范围。所以它只能是桌面的子窗口。  而它自己app的主窗口一般就只是它的owner。  引用 14 楼 六道佩恩 的回复:3. 你的4.1我觉得有个地方说错了，“当owner窗口最小化的时候，它所拥有的窗口都会被隐藏；但不影响它所拥有的窗口的可见状态”，可见与否不是看WS_VISIBLE吗，我测过了，拥有者窗口最小化后，拥有的窗口隐藏后，它已经没有WS_VISIBLE样式了，IsWindowVisible也返回0，这应该算是影响了其可见状态吧？  你说的对，可能我以前的印象有误， 我现在看了下，owner窗口有WS_MINIMIZE后（最小化），owned窗口就会被隐藏。而不是取决于owner的WS_VISIBLE。所以窗口C还是不会自动隐藏，因为窗口B只是隐藏了，而没有WS_MINIMIZE（最小化）。

user: user-0
content: 引用 17 楼 user-2 的回复:[quote=引用 16 楼 六道佩恩 的回复:] 我是这样想的，它这样的设计是不是还牵涉到什么原因而使得这个列表框需要一个WS_CHILD  其他的先不讨论，你说的这个我先确定下： 你确定你这个下拉列表框带WS_CHILD？？  不会吧。。。创建桌面的child window 这种做法我自己还真没做过。。。 [/quote]  你对比下样式，WS_CHILD是0x40 00 00 00

user: user-2
content: 引用 16 楼 六道佩恩 的回复:我是这样想的，它这样的设计是不是还牵涉到什么原因而使得这个列表框需要一个WS_CHILD  其他的先不讨论，你说的这个我先确定下： 你确定你这个下拉列表框带WS_CHILD？？  不会吧。。。创建桌面的child window 这种做法我自己还真没做过。。。

user: user-0
content: 原来如此，总算能石锤顶级窗口的父窗口是桌面了 我又试了下IsChild，这个没效果，估计是跟GetParent配套的   引用 15 楼 user-2 的回复:[quote=引用 14 楼 六道佩恩 的回复:] 2. 我测试了下，只有子窗口获取父窗口时，才会显示桌面窗口的句柄。这个列表框为什么一定要设置为WS_CHILD？同为桌面窗口的子窗口的话，顶级窗口不行吗？  没太听懂你说的。你是不是说反了。  我说的明明是：下拉列表框必须是top-level的啊。。。因为它很可能需要超出它自己app的主窗口的范围。所以它只能是桌面的子窗口。  而它自己app的主窗口一般就只是它的owner。  [/quote] 关于这个，我的意思是，列表框的实现，它是创建为以桌面为父窗口的子窗口。然后作为子窗口处于Z序顶端，我的问题是，它为什么不实现为一个TOPMOST的顶级窗口呢，这样的效果感觉应该是一样的？我是这样想的，它这样的设计是不是还牵涉到什么原因而使得这个列表框需要一个WS_CHILD，毕竟我考虑的是由自己来实现类似效果的层面。如果没什么特别原因，那就当我没说

user: user-0
content: 引用 8 楼 早打大打打核战争 的回复:实现很简单，就和组合框的下拉列表框一个套路就可以了，SetParent(LISTBOX_HWND, NULL); 然后显示的时候用SetWindowPos设置HWND_TOP标志把LISTBOX置顶即可 关键是移动窗口呢？窗口的Z序变化呢？

user: user-2
content: 引用 10 楼 六道佩恩 的回复:不过有个问题，它为什么要设为桌面窗口的子窗口？设为顶级窗口不行吗？ 所有顶层窗口（我更愿意称“顶层“窗口为“弹出式”窗口）的父窗口都是桌面。  windows系统中的窗口之间的关系很容易理解错误，个人认为主要原因就在于它有两类窗口（带WS_CHILD风格和不带WS_CHILD风格），却用了同一套窗口之间父子关系建立的API（CreateWindow创建时指定父窗口、SetParent创建后修改父窗口等等）。  所以，你在调用任何有关父子关系的API时，要始终清楚你操作的窗口对象是“弹出式”窗口，还是“子”（带WS_CHILD风格）窗口（我更愿意称“子“窗口为“嵌入式”窗口）。 1.1、当CreateWindow创建“弹出式”窗口A并指定“父窗口”（hWndParent参数）也为“弹出式”窗口时，这个所谓的“父窗口”，在这时其实是指“拥有者”窗口（owner），窗口A的真正父窗口是桌面。  1.2、当CreateWindow创建“弹出式”窗口A并指定“父窗口”（hWndParent参数）为“嵌入式”窗口时，系统将找到容纳该“嵌入式”窗口的“弹出式”窗口，以该“弹出式”窗口作为owner窗口。  1.3、Owner窗口一旦指定不能更改。通过GetWindow函数（指定GW_OWNER标志）可获取owner窗口的句柄（如果存在的话）。  1.4、当CreateWindow创建“嵌入式”窗口B时，必须指定一个父窗口（hWndParent参数），父窗口类型不限。“嵌入式”窗口也可以作为其父窗口。   2.1、当调用SetParent修改“弹出式”窗口A的所谓“父窗口”（hWndNewParent）时，窗口A自动变为“嵌入式”窗口（也印证了1.3条所述），且程序员需要手动修正窗口A的风格（加上WS_CHILD,去掉WS_POPUP），否则窗口可能会表现不正常。  2.2、当调用SetParent修改“嵌入式”窗口B的父窗口（hWndNewParent）时，父窗口类型不限（参照1.4）。   拥有父子关系的窗口表现： 3.1、子窗口只能显示在它的父窗口的客户区中，超出父窗口客户区的部分将被裁减掉；父窗口被隐藏时，它的所有子窗口也被隐藏；最小化父窗口不影响子窗口的可见状态，子窗口会随着父窗口被最小化，但是它的WS_VISIBLE属性不变。父窗口被销毁的时候，它的所有子窗口都会被销毁。 由此可见，下拉列表框 必须不能 是程序主窗口的子窗口！ 因为它的显示区域很可能需要超出主窗口客户区！  3.2、桌面窗口与所有顶层窗口（“弹出式”窗口）也是父/子关系，但又有其特殊性：对桌面窗口调用GetWindow函数（指定GW_CHILD标志）可得到第一个顶层窗口的句柄，但对某一个顶层窗口调用GetParent却会返回NULL，不会返回桌面窗口的句柄。  额外说下 拥有/被拥有（owner-owned）关系的窗口表现： 4.1、顶层窗口（“弹出式”窗口）之间可以存在owner-owned关系。owner-owned关系对窗口可见性的影响为：owned窗口永远显示在owner窗口的前面；当owner窗口最小化的时候，它所拥有的窗口都会被隐藏；但不影响它所拥有的窗口的可见状态。（根据这一点，如果窗口A 拥有窗口B,窗口B拥有窗口C,则当窗口A最小化的时候，窗口B被隐藏，但是窗口 C还是可见的）；当owner窗口被销毁的时候，它所拥有的窗口都会被销毁。

user: user-1
content: 1. 所有顶级窗口都是桌面窗口的子窗口， 组合框的下拉列表框这样设计，就是为了能够不改变它所在的窗口Z序的情况下，让列表框能显示在置顶窗口之前，如果把它设计为所在窗口的子窗口，那么必须把列表框的父窗口置顶，列表框才能显示在置顶窗口之前  2. 组合框的下拉列表框能像你说的那样“当窗口移动时，列表框跟着动”吗，能“当窗口Z序变化时，列表框也跟着变化”吗（这个其实可以做到，只要设置为TOPMOST即可，但是没有必要），不能，因为它的设计是列表框显示之后，只要在它之外有鼠标点击或者按键事件，列表框就自动隐藏，所以避免了跟随所在窗口移动、动态改变Z序之类的操作

user: user-0
content: 引用 12 楼 user-2 的回复:[quote=引用 10 楼 六道佩恩 的回复:] 不过有个问题，它为什么要设为桌面窗口的子窗口？设为顶级窗口不行吗？ 所有顶层窗口（我更愿意称“顶层“窗口为“弹出式”窗口）的父窗口都是桌面。  windows系统中的窗口之间的关系很容易理解错误，个人认为主要原因就在于它有两类窗口（带WS_CHILD风格和不带WS_CHILD风格），却用了同一套窗口之间父子关系建立的API（CreateWindow创建时指定父窗口、SetParent创建后修改父窗口等等）。  所以，你在调用任何有关父子关系的API时，要始终清楚你操作的窗口对象是“弹出式”窗口，还是“子”（带WS_CHILD风格）窗口（我更愿意称“子“窗口为“嵌入式”窗口）。 1.1、当CreateWindow创建“弹出式”窗口A并指定“父窗口”（hWndParent参数）也为“弹出式”窗口时，这个所谓的“父窗口”，在这时其实是指“拥有者”窗口（owner），窗口A的真正父窗口是桌面。  1.2、当CreateWindow创建“弹出式”窗口A并指定“父窗口”（hWndParent参数）为“嵌入式”窗口时，系统将找到容纳该“嵌入式”窗口的“弹出式”窗口，以该“弹出式”窗口作为owner窗口。  1.3、Owner窗口一旦指定不能更改。通过GetWindow函数（指定GW_OWNER标志）可获取owner窗口的句柄（如果存在的话）。  1.4、当CreateWindow创建“嵌入式”窗口B时，必须指定一个父窗口（hWndParent参数），父窗口类型不限。“嵌入式”窗口也可以作为其父窗口。   2.1、当调用SetParent修改“弹出式”窗口A的所谓“父窗口”（hWndNewParent）时，窗口A自动变为“嵌入式”窗口（也印证了1.3条所述），且程序员需要手动修正窗口A的风格（加上WS_CHILD,去掉WS_POPUP），否则窗口可能会表现不正常。  2.2、当调用SetParent修改“嵌入式”窗口B的父窗口（hWndNewParent）时，父窗口类型不限（参照1.4）。   拥有父子关系的窗口表现： 3.1、子窗口只能显示在它的父窗口的客户区中，超出父窗口客户区的部分将被裁减掉；父窗口被隐藏时，它的所有子窗口也被隐藏；最小化父窗口不影响子窗口的可见状态，子窗口会随着父窗口被最小化，但是它的WS_VISIBLE属性不变。父窗口被销毁的时候，它的所有子窗口都会被销毁。 由此可见，下拉列表框 必须不能 是程序主窗口的子窗口！ 因为它的显示区域很可能需要超出主窗口客户区！  3.2、桌面窗口与所有顶层窗口（“弹出式”窗口）也是父/子关系，但又有其特殊性：对桌面窗口调用GetWindow函数（指定GW_CHILD标志）可得到第一个顶层窗口的句柄，但对某一个顶层窗口调用GetParent却会返回NULL，不会返回桌面窗口的句柄。  额外说下 拥有/被拥有（owner-owned）关系的窗口表现： 4.1、顶层窗口（“弹出式”窗口）之间可以存在owner-owned关系。owner-owned关系对窗口可见性的影响为：owned窗口永远显示在owner窗口的前面；当owner窗口最小化的时候，它所拥有的窗口都会被隐藏；但不影响它所拥有的窗口的可见状态。（根据这一点，如果窗口A 拥有窗口B,窗口B拥有窗口C,则当窗口A最小化的时候，窗口B被隐藏，但是窗口 C还是可见的）；当owner窗口被销毁的时候，它所拥有的窗口都会被销毁。 [/quote] 又见大佬你了  感谢你把这些完整地说了一遍，修正了我一些理解上的缺失  但是我还是有几个疑问： 1. 顶级窗口的父窗口真的是桌面窗口吗？毕竟GetParent得到的是NULL，凭借对桌面窗口调用GetWindow函数得到第一个顶层窗口的句柄这一点应该也无法说明所有顶级窗口的父窗口是桌面窗口吧？主要是我一直也没看到微软文档说过顶级窗口的父窗口是桌面窗口，不过就“SetParent指定父窗口为NULL时设置桌面窗口为父窗口”，测试结果是设置后GetWindow仍然得到NULL，应该算佐证了“顶级窗口的父窗口真的是桌面窗口”吧。 2. 我测试了下，只有子窗口获取父窗口时，才会显示桌面窗口的句柄。这个列表框为什么一定要设置为WS_CHILD？同为桌面窗口的子窗口的话，顶级窗口不行吗？ 3. 你的4.1我觉得有个地方说错了，“当owner窗口最小化的时候，它所拥有的窗口都会被隐藏；但不影响它所拥有的窗口的可见状态”，可见与否不是看WS_VISIBLE吗，我测过了，拥有者窗口最小化后，拥有的窗口隐藏后，它已经没有WS_VISIBLE样式了，IsWindowVisible也返回0，这应该算是影响了其可见状态吧？

user: user-0
content: 引用 11 楼 早打大打打核战争 的回复:1. 所有顶级窗口都是桌面窗口的子窗口， 组合框的下拉列表框这样设计，就是为了能够不改变它所在的窗口Z序的情况下，让列表框能显示在置顶窗口之前，如果把它设计为所在窗口的子窗口，那么必须把列表框的父窗口置顶，列表框才能显示在置顶窗口之前  2. 组合框的下拉列表框能像你说的那样“当窗口移动时，列表框跟着动”吗，能“当窗口Z序变化时，列表框也跟着变化”吗（这个其实可以做到，只要设置为TOPMOST即可，但是没有必要），不能，因为它的设计是列表框显示之后，只要在它之外有鼠标点击或者按键事件，列表框就自动隐藏，所以避免了跟随所在窗口移动、动态改变Z序之类的操作 为什么你们都说顶级窗口是桌面窗口的子窗口？获取顶级窗口父窗口的句柄是NULL呀 还有就是，你还没说将列表框设计为顶级窗口这个方案，这个方案看起来应该也是可行的呀

user: user-0
content: 引用 9 楼 六道佩恩 的回复:[quote=引用 8 楼 早打大打打核战争 的回复:]实现很简单，就和组合框的下拉列表框一个套路就可以了，SetParent(LISTBOX_HWND, NULL); 然后显示的时候用SetWindowPos设置HWND_TOP标志把LISTBOX置顶即可 关键是移动窗口呢？窗口的Z序变化呢？[/quote] 我测试了一下，当窗口移动时，列表框没有跟着动，当窗口Z序变化时，列表框也没有跟着变化，看来没我想的那么好。 不过有个问题，它为什么要设为桌面窗口的子窗口？设为顶级窗口不行吗？

user: user-1
content: 实现很简单，就和组合框的下拉列表框一个套路就可以了，SetParent(LISTBOX_HWND, NULL); 然后显示的时候用SetWindowPos设置HWND_TOP标志把LISTBOX置顶即可

user: user-1
content: 组合框中的那个LISTBOX的父窗口确实是桌面窗口，所以会有这种效果：  估计这么设计的原因就是为了让它能够在不改变顶级窗口位置关系的情况下完整显示出来

user: user-0
content: 引用 6 楼 早打大打打核战争 的回复:组合框中的那个LISTBOX的父窗口确实是桌面窗口，所以会有这种效果：  估计这么设计的原因就是为了让它能够在不改变顶级窗口位置关系的情况下完整显示出来  那么这种效果是我们自己无法实现的是吗？

user: user-1
content: 可以突破父窗口的范围有什么奇怪，只要建立的窗口风格不是child，比如overlapped、popup之类的，都可以突破父窗口的范围 至于列表框其父窗口是桌面窗口这个还有待确认

user: user-0
content: 引用 4 楼 早打大打打核战争 的回复:可以突破父窗口的范围有什么奇怪，只要建立的窗口风格不是child，比如overlapped、popup之类的，都可以突破父窗口的范围 至于列表框其父窗口是桌面窗口这个还有待确认        你看看这些证据够不够，前三张是根据鼠标位置获取窗口句柄和相关信息，后四张是spy++的，用来佐证这里的获得的确是父窗口而不是拥有者窗口，鼠标用来提示获取的是哪个窗口 这里明显能看到，这里的组合框就是大窗口的子窗口，但它的列表框已经超出了组合框的父窗口了；还有就是，这里列表框的父窗口句柄对比GetDeskWindow获得的桌面窗口句柄，是一样的，它的父窗口是桌面窗口

user: user-0
content: 引用 1 楼 zgl7903 的回复:https://docs.microsoft.com/en-us/cpp/mfc/reference/ccombobox-class?view=msvc-160#remarks 组合框由与静态控件或编辑控件组合在一起的列表框组成 引用 2 楼 早打大打打核战争 的回复:顾名思义，名字都叫“组合框”啊，就是EDIT和LISTBOX组合呀~~~  你们没get到我的问题啊  组合框的列表框可以突破它（组合框）本身的窗口范围，甚至可以突破父窗口的范围，查该列表框发现其父窗口是桌面窗口  我的问题在于，它是怎么实现的（指该组合框的列表框）？如果由我们自己来实现的话，有办法实现吗？

user: user-1
content: 顾名思义，名字都叫“组合框”啊，就是EDIT和LISTBOX组合呀~~~