Wine-Devel
By thread
wine-devel@list.winehq.org
By month
Messages by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
September 2018
- 70 participants
- 1549 messages
[PATCH v7 1/7] shell32/autocomplete: Move the autocomplete processing and WM_KEYUP to separate functions
by Gabriel Ivăncescu
Signed-off-by: Gabriel Ivăncescu <gabrielopcode(a)gmail.com>
---
v7: Add a space after for/switch.
dlls/shell32/autocomplete.c | 336 ++++++++++++++++++++++++--------------------
1 file changed, 182 insertions(+), 154 deletions(-)
diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c
index 9f35ff7..874d831 100644
--- a/dlls/shell32/autocomplete.c
+++ b/dlls/shell32/autocomplete.c
@@ -119,6 +119,76 @@ static size_t format_quick_complete(WCHAR *dst, const WCHAR *qc, const WCHAR *st
return dst - base;
}
+static void autocomplete_text(IAutoCompleteImpl *ac, WCHAR *text, UINT len, HWND hwnd, BOOL displayall)
+{
+ HRESULT hr;
+ UINT cpt;
+
+ SendMessageW(ac->hwndListBox, LB_RESETCONTENT, 0, 0);
+
+ /* Set txtbackup to point to text itself (which must not be released) */
+ heap_free(ac->txtbackup);
+ ac->txtbackup = text;
+
+ if (!displayall && !len)
+ return;
+
+ IEnumString_Reset(ac->enumstr);
+ for (cpt = 0;;)
+ {
+ LPOLESTR strs = NULL;
+ ULONG fetched;
+
+ hr = IEnumString_Next(ac->enumstr, 1, &strs, &fetched);
+ if (hr != S_OK)
+ break;
+
+ if (!strncmpiW(text, strs, len))
+ {
+ if (cpt == 0 && (ac->options & ACO_AUTOAPPEND))
+ {
+ WCHAR buffW[255];
+
+ strcpyW(buffW, text);
+ strcatW(buffW, &strs[len]);
+ SetWindowTextW(hwnd, buffW);
+ SendMessageW(hwnd, EM_SETSEL, len, strlenW(strs));
+ if (!(ac->options & ACO_AUTOSUGGEST))
+ {
+ CoTaskMemFree(strs);
+ break;
+ }
+ }
+
+ if (ac->options & ACO_AUTOSUGGEST)
+ SendMessageW(ac->hwndListBox, LB_ADDSTRING, 0, (LPARAM)strs);
+
+ cpt++;
+ }
+
+ CoTaskMemFree(strs);
+ }
+
+ if (ac->options & ACO_AUTOSUGGEST)
+ {
+ if (cpt)
+ {
+ RECT r;
+ UINT height = SendMessageW(ac->hwndListBox, LB_GETITEMHEIGHT, 0, 0);
+ SendMessageW(ac->hwndListBox, LB_CARETOFF, 0, 0);
+ GetWindowRect(hwnd, &r);
+ SetParent(ac->hwndListBox, HWND_DESKTOP);
+ /* It seems that Windows XP displays 7 lines at most
+ and otherwise displays a vertical scroll bar */
+ SetWindowPos(ac->hwndListBox, HWND_TOP,
+ r.left, r.bottom + 1, r.right - r.left, min(height * 7, height*(cpt+1)),
+ SWP_SHOWWINDOW );
+ }
+ else
+ ShowWindow(ac->hwndListBox, SW_HIDE);
+ }
+}
+
static void destroy_autocomplete_object(IAutoCompleteImpl *ac)
{
ac->hwndEdit = NULL;
@@ -128,17 +198,122 @@ static void destroy_autocomplete_object(IAutoCompleteImpl *ac)
}
/*
+ Helper for ACEditSubclassProc
+*/
+static LRESULT ACEditSubclassProc_KeyUp(IAutoCompleteImpl *ac, HWND hwnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam)
+{
+ WCHAR *text;
+ UINT len, size;
+ BOOL displayall = FALSE;
+
+ len = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0);
+ size = len + 1;
+ if (!(text = heap_alloc(size * sizeof(WCHAR))))
+ return 0;
+ len = SendMessageW(hwnd, WM_GETTEXT, size, (LPARAM)text);
+
+ switch (wParam)
+ {
+ case VK_RETURN:
+ /* If quickComplete is set and control is pressed, replace the string */
+ if (ac->quickComplete && (GetKeyState(VK_CONTROL) & 0x8000))
+ {
+ WCHAR *buf;
+ size_t sz = strlenW(ac->quickComplete) + 1 + len;
+ if ((buf = heap_alloc(sz * sizeof(WCHAR))))
+ {
+ len = format_quick_complete(buf, ac->quickComplete, text, len);
+ SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)buf);
+ SendMessageW(hwnd, EM_SETSEL, 0, len);
+ heap_free(buf);
+ }
+ }
+
+ if (ac->options & ACO_AUTOSUGGEST)
+ ShowWindow(ac->hwndListBox, SW_HIDE);
+ heap_free(text);
+ return 0;
+ case VK_LEFT:
+ case VK_RIGHT:
+ heap_free(text);
+ return 0;
+ case VK_UP:
+ case VK_DOWN:
+ /* Two cases here:
+ - if the listbox is not visible and ACO_UPDOWNKEYDROPSLIST is
+ set, display it with all the entries, without selecting any
+ - if the listbox is visible, change the selection
+ */
+ if ( (ac->options & (ACO_AUTOSUGGEST | ACO_UPDOWNKEYDROPSLIST))
+ && (!IsWindowVisible(ac->hwndListBox) && (! *text)) )
+ {
+ /* We must display all the entries */
+ displayall = TRUE;
+ }
+ else
+ {
+ INT count, sel;
+ heap_free(text);
+ if (!IsWindowVisible(ac->hwndListBox))
+ return 0;
+
+ count = SendMessageW(ac->hwndListBox, LB_GETCOUNT, 0, 0);
+
+ /* Change the selection */
+ sel = SendMessageW(ac->hwndListBox, LB_GETCURSEL, 0, 0);
+ if (wParam == VK_UP)
+ sel = ((sel - 1) < 0) ? count - 1 : sel - 1;
+ else
+ sel = ((sel + 1) >= count) ? -1 : sel + 1;
+ SendMessageW(ac->hwndListBox, LB_SETCURSEL, sel, 0);
+ if (sel >= 0)
+ {
+ WCHAR *msg;
+ UINT len;
+
+ len = SendMessageW(ac->hwndListBox, LB_GETTEXTLEN, sel, 0);
+ if (!(msg = heap_alloc((len + 1) * sizeof(WCHAR))))
+ return 0;
+ len = SendMessageW(ac->hwndListBox, LB_GETTEXT, sel, (LPARAM)msg);
+ SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)msg);
+ SendMessageW(hwnd, EM_SETSEL, len, len);
+ heap_free(msg);
+ }
+ else
+ {
+ UINT len;
+ SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)ac->txtbackup);
+ len = strlenW(ac->txtbackup);
+ SendMessageW(hwnd, EM_SETSEL, len, len);
+ }
+ return 0;
+ }
+ break;
+ case VK_BACK:
+ case VK_DELETE:
+ if ((! *text) && (ac->options & ACO_AUTOSUGGEST))
+ {
+ heap_free(text);
+ ShowWindow(ac->hwndListBox, SW_HIDE);
+ return CallWindowProcW(ac->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
+ }
+ break;
+ }
+
+ if (len + 1 != size)
+ text = heap_realloc(text, (len + 1) * sizeof(WCHAR));
+
+ autocomplete_text(ac, text, len, hwnd, displayall);
+ return 0;
+}
+
+/*
Window procedure for autocompletion
*/
static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
IAutoCompleteImpl *This = GetPropW(hwnd, autocomplete_propertyW);
- HRESULT hr;
- WCHAR *hwndText;
- UINT len, size, cpt;
- RECT r;
- BOOL displayall = FALSE;
- int height, sel;
if (!This->enabled) return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
@@ -155,154 +330,7 @@ static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
}
return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
case WM_KEYUP:
- len = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0);
- size = len + 1;
- if (!(hwndText = heap_alloc(size * sizeof(WCHAR))))
- return 0;
- len = SendMessageW(hwnd, WM_GETTEXT, size, (LPARAM)hwndText);
-
- switch(wParam) {
- case VK_RETURN:
- /* If quickComplete is set and control is pressed, replace the string */
- if (This->quickComplete && (GetKeyState(VK_CONTROL) & 0x8000))
- {
- WCHAR *buf;
- size_t sz = strlenW(This->quickComplete) + 1 + len;
- if ((buf = heap_alloc(sz * sizeof(WCHAR))))
- {
- len = format_quick_complete(buf, This->quickComplete, hwndText, len);
- SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)buf);
- SendMessageW(hwnd, EM_SETSEL, 0, len);
- heap_free(buf);
- }
- }
-
- if (This->options & ACO_AUTOSUGGEST)
- ShowWindow(This->hwndListBox, SW_HIDE);
- heap_free(hwndText);
- return 0;
- case VK_LEFT:
- case VK_RIGHT:
- heap_free(hwndText);
- return 0;
- case VK_UP:
- case VK_DOWN:
- /* Two cases here :
- - if the listbox is not visible, displays it
- with all the entries if the style ACO_UPDOWNKEYDROPSLIST
- is present but does not select anything.
- - if the listbox is visible, change the selection
- */
- if ( (This->options & (ACO_AUTOSUGGEST | ACO_UPDOWNKEYDROPSLIST))
- && (!IsWindowVisible(This->hwndListBox) && (! *hwndText)) )
- {
- /* We must display all the entries */
- displayall = TRUE;
- } else {
- heap_free(hwndText);
- if (IsWindowVisible(This->hwndListBox)) {
- int count;
-
- count = SendMessageW(This->hwndListBox, LB_GETCOUNT, 0, 0);
- /* Change the selection */
- sel = SendMessageW(This->hwndListBox, LB_GETCURSEL, 0, 0);
- if (wParam == VK_UP)
- sel = ((sel-1) < 0) ? count-1 : sel-1;
- else
- sel = ((sel+1) >= count) ? -1 : sel+1;
- SendMessageW(This->hwndListBox, LB_SETCURSEL, sel, 0);
- if (sel != -1) {
- WCHAR *msg;
- int len;
-
- len = SendMessageW(This->hwndListBox, LB_GETTEXTLEN, sel, 0);
- if (!(msg = heap_alloc((len + 1) * sizeof(WCHAR))))
- return 0;
- len = SendMessageW(This->hwndListBox, LB_GETTEXT, sel, (LPARAM)msg);
- SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)msg);
- SendMessageW(hwnd, EM_SETSEL, len, len);
- heap_free(msg);
- } else {
- UINT len;
- SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)This->txtbackup);
- len = strlenW(This->txtbackup);
- SendMessageW(hwnd, EM_SETSEL, len, len);
- }
- }
- return 0;
- }
- break;
- case VK_BACK:
- case VK_DELETE:
- if ((! *hwndText) && (This->options & ACO_AUTOSUGGEST)) {
- heap_free(hwndText);
- ShowWindow(This->hwndListBox, SW_HIDE);
- return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
- }
- break;
- }
-
- if (len + 1 != size)
- hwndText = heap_realloc(hwndText, (len + 1) * sizeof(WCHAR));
-
- SendMessageW(This->hwndListBox, LB_RESETCONTENT, 0, 0);
-
- /* Set txtbackup to point to hwndText itself (which must not be released) */
- heap_free(This->txtbackup);
- This->txtbackup = hwndText;
-
- if (!displayall && !len)
- break;
-
- IEnumString_Reset(This->enumstr);
- for(cpt = 0;;) {
- LPOLESTR strs = NULL;
- ULONG fetched;
-
- hr = IEnumString_Next(This->enumstr, 1, &strs, &fetched);
- if (hr != S_OK)
- break;
-
- if (!strncmpiW(hwndText, strs, len)) {
- if (cpt == 0 && (This->options & ACO_AUTOAPPEND)) {
- WCHAR buffW[255];
-
- strcpyW(buffW, hwndText);
- strcatW(buffW, &strs[len]);
- SetWindowTextW(hwnd, buffW);
- SendMessageW(hwnd, EM_SETSEL, len, strlenW(strs));
- if (!(This->options & ACO_AUTOSUGGEST)) {
- CoTaskMemFree(strs);
- break;
- }
- }
-
- if (This->options & ACO_AUTOSUGGEST)
- SendMessageW(This->hwndListBox, LB_ADDSTRING, 0, (LPARAM)strs);
-
- cpt++;
- }
-
- CoTaskMemFree(strs);
- }
-
- if (This->options & ACO_AUTOSUGGEST) {
- if (cpt) {
- height = SendMessageW(This->hwndListBox, LB_GETITEMHEIGHT, 0, 0);
- SendMessageW(This->hwndListBox, LB_CARETOFF, 0, 0);
- GetWindowRect(hwnd, &r);
- SetParent(This->hwndListBox, HWND_DESKTOP);
- /* It seems that Windows XP displays 7 lines at most
- and otherwise displays a vertical scroll bar */
- SetWindowPos(This->hwndListBox, HWND_TOP,
- r.left, r.bottom + 1, r.right - r.left, min(height * 7, height*(cpt+1)),
- SWP_SHOWWINDOW );
- } else {
- ShowWindow(This->hwndListBox, SW_HIDE);
- }
- }
-
- break;
+ return ACEditSubclassProc_KeyUp(This, hwnd, uMsg, wParam, lParam);
case WM_DESTROY:
{
WNDPROC proc = This->wpOrigEditProc;
--
1.9.1
Sept. 17, 2018
Re: [PATCH 1/2] ntoskrnl.exe: Implement IoRegisterDeviceInterface
by Zebediah Figura
On 17/09/18 13:20, Aric Stewart wrote:
> On 9/17/18 11:55 AM, Zebediah Figura wrote:
>> On 17/09/18 11:44, Aric Stewart wrote:
>>>
>>>
>>> On 9/14/18 2:02 PM, Zebediah Figura wrote:
>>>> On 14/09/18 13:59, Aric Stewart wrote:
>>>>> Signed-off-by: Aric Stewart <aric(a)codeweavers.com>
>>>>> ---
>>>>> dlls/ntoskrnl.exe/ntoskrnl.c | 216 ++++++++++++++++++++++++++++++++++++
>>>>> dlls/ntoskrnl.exe/ntoskrnl.exe.spec | 2 +-
>>>>> include/ddk/wdm.h | 1 +
>>>>> 3 files changed, 218 insertions(+), 1 deletion(-)
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>
>>>> Can this go directly through setupapi instead?
>>>>
>>>>
>>>
>>> Sorry, I dont know if I understand your question...
>>>
>>> What do you mean go through setupapi? Not that we have or enforce this in any way, but technically setupapi is user level and IoRegisterDeviceInterface is kernel level.
>>>
>>> -aric
>>>
>>>
>>
>> I mean that we could implement this on top of setupapi routines instead
>> of essentially reimplementing them here. I know that it's
>> architecturally upside-down, but we call call user-mode functions
>> elsewhere in ntoskrnl, and as long as we're going to keep drivers in
>> user-mode I don't see any reason to avoid that.
>>
>>
>
> Yeah, Looking at the SetupDiCreateDeviceInterfaceW APIs in setupapi I can see how you think that. They where clearly the inspiration and base for my work. However the top level entry points vary quite a bit.
> BOOL WINAPI SetupDiCreateDeviceInterfaceW(HDEVINFO DeviceInfoSet,
> PSP_DEVINFO_DATA DeviceInfoData,
> const GUID *InterfaceClassGuid,
> PCWSTR ReferenceString,
> DWORD CreationFlags,
> SP_DEVICE_INTERFACE_DATA *iface_data)
> vs
> NTSTATUS WINAPI IoRegisterDeviceInterface(DEVICE_OBJECT *device,
> const GUID *class_guid,
> UNICODE_STRING *reference_string,
> UNICODE_STRING *symbolic_link)
>
> Because creating WINE custom entry points into existing dlls is frowned upon I felt it easier to reimplement instead of try to shoehorn. However if you are seeing something I did not then maybe it should be done differently!
>
> -aric
>
>
Hmm, right, I see that setupapi doesn't expose a way to specify the
symbolic link.
Another alternative is to implement some of setupapi on top of ntoskrnl,
though I'm not sure to what degree that's worth doing.
Sept. 17, 2018
Re: [PATCH 1/2] ntoskrnl.exe: Implement IoRegisterDeviceInterface
by Aric Stewart
On 9/17/18 11:55 AM, Zebediah Figura wrote:
> On 17/09/18 11:44, Aric Stewart wrote:
>>
>>
>> On 9/14/18 2:02 PM, Zebediah Figura wrote:
>>> On 14/09/18 13:59, Aric Stewart wrote:
>>>> Signed-off-by: Aric Stewart <aric(a)codeweavers.com>
>>>> ---
>>>> dlls/ntoskrnl.exe/ntoskrnl.c | 216 ++++++++++++++++++++++++++++++++++++
>>>> dlls/ntoskrnl.exe/ntoskrnl.exe.spec | 2 +-
>>>> include/ddk/wdm.h | 1 +
>>>> 3 files changed, 218 insertions(+), 1 deletion(-)
>>>>
>>>>
>>>>
>>>>
>>>>
>>>
>>> Can this go directly through setupapi instead?
>>>
>>>
>>
>> Sorry, I dont know if I understand your question...
>>
>> What do you mean go through setupapi? Not that we have or enforce this in any way, but technically setupapi is user level and IoRegisterDeviceInterface is kernel level.
>>
>> -aric
>>
>>
>
> I mean that we could implement this on top of setupapi routines instead
> of essentially reimplementing them here. I know that it's
> architecturally upside-down, but we call call user-mode functions
> elsewhere in ntoskrnl, and as long as we're going to keep drivers in
> user-mode I don't see any reason to avoid that.
>
>
Yeah, Looking at the SetupDiCreateDeviceInterfaceW APIs in setupapi I can see how you think that. They where clearly the inspiration and base for my work. However the top level entry points vary quite a bit.
BOOL WINAPI SetupDiCreateDeviceInterfaceW(HDEVINFO DeviceInfoSet,
PSP_DEVINFO_DATA DeviceInfoData,
const GUID *InterfaceClassGuid,
PCWSTR ReferenceString,
DWORD CreationFlags,
SP_DEVICE_INTERFACE_DATA *iface_data)
vs
NTSTATUS WINAPI IoRegisterDeviceInterface(DEVICE_OBJECT *device,
const GUID *class_guid,
UNICODE_STRING *reference_string,
UNICODE_STRING *symbolic_link)
Because creating WINE custom entry points into existing dlls is frowned upon I felt it easier to reimplement instead of try to shoehorn. However if you are seeing something I did not then maybe it should be done differently!
-aric
Sept. 17, 2018
Re: [PATCH 1/2] ntoskrnl.exe: Implement IoRegisterDeviceInterface
by Zebediah Figura
On 17/09/18 11:44, Aric Stewart wrote:
>
>
> On 9/14/18 2:02 PM, Zebediah Figura wrote:
>> On 14/09/18 13:59, Aric Stewart wrote:
>>> Signed-off-by: Aric Stewart <aric(a)codeweavers.com>
>>> ---
>>> dlls/ntoskrnl.exe/ntoskrnl.c | 216 ++++++++++++++++++++++++++++++++++++
>>> dlls/ntoskrnl.exe/ntoskrnl.exe.spec | 2 +-
>>> include/ddk/wdm.h | 1 +
>>> 3 files changed, 218 insertions(+), 1 deletion(-)
>>>
>>>
>>>
>>>
>>>
>>
>> Can this go directly through setupapi instead?
>>
>>
>
> Sorry, I dont know if I understand your question...
>
> What do you mean go through setupapi? Not that we have or enforce this in any way, but technically setupapi is user level and IoRegisterDeviceInterface is kernel level.
>
> -aric
>
>
I mean that we could implement this on top of setupapi routines instead
of essentially reimplementing them here. I know that it's
architecturally upside-down, but we call call user-mode functions
elsewhere in ntoskrnl, and as long as we're going to keep drivers in
user-mode I don't see any reason to avoid that.
Sept. 17, 2018
Re: [PATCH 1/2] ntoskrnl.exe: Implement IoRegisterDeviceInterface
by Aric Stewart
On 9/14/18 2:02 PM, Zebediah Figura wrote:
> On 14/09/18 13:59, Aric Stewart wrote:
>> Signed-off-by: Aric Stewart <aric(a)codeweavers.com>
>> ---
>> dlls/ntoskrnl.exe/ntoskrnl.c | 216 ++++++++++++++++++++++++++++++++++++
>> dlls/ntoskrnl.exe/ntoskrnl.exe.spec | 2 +-
>> include/ddk/wdm.h | 1 +
>> 3 files changed, 218 insertions(+), 1 deletion(-)
>>
>>
>>
>>
>>
>
> Can this go directly through setupapi instead?
>
>
Sorry, I dont know if I understand your question...
What do you mean go through setupapi? Not that we have or enforce this in any way, but technically setupapi is user level and IoRegisterDeviceInterface is kernel level.
-aric
Sept. 17, 2018
Re: [PATCH] user32/tests: Mark newer Win10 behaviour as broken
by Alexandre Julliard
André Hentschel <nerv(a)dawncrow.de> writes:
> Signed-off-by: André Hentschel <nerv(a)dawncrow.de>
> ---
> dlls/user32/tests/monitor.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/dlls/user32/tests/monitor.c b/dlls/user32/tests/monitor.c
> index f4c1a4b17c..8ba6cc267b 100644
> --- a/dlls/user32/tests/monitor.c
> +++ b/dlls/user32/tests/monitor.c
> @@ -585,7 +585,7 @@ static void test_display_config(void)
> paths = modes = 100;
> ret = pGetDisplayConfigBufferSizes(0, &paths, &modes);
> ok(ret == ERROR_INVALID_PARAMETER || ret == ERROR_NOT_SUPPORTED, "got %d\n", ret);
> - ok(modes == 0 && paths == 0, "got %u, %u\n", modes, paths);
> + ok((modes == 0 || broken(modes == 100)) && paths == 0, "got %u, %u\n", modes, paths);
This looks like a reasonable result at least for the
ERROR_INVALID_PARAMETER case, I'm not sure why you'd consider it broken.
--
Alexandre Julliard
julliard(a)winehq.org
Sept. 17, 2018
[PATCH] wineboot: Create user folders if they don't exist.
by Zhiyi Zhang
When creating volatile environment registry keys during first
wineprefix creation, none of the user folders are existent.
Create them before setting registry keys so that volatile
environment gets initialized and corresponding environment variables
such as %USERPROFILE% can be set. Otherwise, such environment variables
are set only after another wineboot, causing applications to fail if
they need them after first boot.
Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com>
---
programs/wineboot/wineboot.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c
index 17ef9cbcca..075b4c8b08 100644
--- a/programs/wineboot/wineboot.c
+++ b/programs/wineboot/wineboot.c
@@ -385,14 +385,14 @@ static void create_volatile_environment_registry_key(void)
KEY_ALL_ACCESS, NULL, &hkey, NULL ))
return;
- hr = SHGetFolderPathW( NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, path );
+ hr = SHGetFolderPathW( NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, path );
if (SUCCEEDED(hr)) set_reg_value( hkey, AppDataW, path );
set_reg_value( hkey, ClientNameW, ConsoleW );
/* Write the profile path's drive letter and directory components into
* HOMEDRIVE and HOMEPATH respectively. */
- hr = SHGetFolderPathW( NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, path );
+ hr = SHGetFolderPathW( NULL, CSIDL_PROFILE | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, path );
if (SUCCEEDED(hr))
{
set_reg_value( hkey, UserProfileW, path );
@@ -406,7 +406,7 @@ static void create_volatile_environment_registry_key(void)
set_reg_value( hkey, HomeShareW, EmptyW );
- hr = SHGetFolderPathW( NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, path );
+ hr = SHGetFolderPathW( NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, path );
if (SUCCEEDED(hr))
set_reg_value( hkey, LocalAppDataW, path );
--
2.18.0
Sept. 17, 2018
[PATCH v4 3/3] comctl32/pager: Support comboboxex notification conversion.
by Zhiyi Zhang
Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com>
---
dlls/comctl32/pager.c | 61 +++++++++++++++++++++++++++----
dlls/comctl32/tests/pager.c | 72 +++++++++++++++++++++++++++++++++++--
2 files changed, 125 insertions(+), 8 deletions(-)
diff --git a/dlls/comctl32/pager.c b/dlls/comctl32/pager.c
index a6ef2eeadd..7fc0544ee4 100644
--- a/dlls/comctl32/pager.c
+++ b/dlls/comctl32/pager.c
@@ -101,7 +101,11 @@ enum conversion_flags
/* Convert ANSI text from parent back to Unicode for children */
CONVERT_RECEIVE = 0x02,
/* Send empty text to parent if text is NULL. Original text pointer still remains NULL */
- SEND_EMPTY_IF_NULL = 0x04
+ SEND_EMPTY_IF_NULL = 0x04,
+ /* Set text to null after parent received the notification if the required mask is not set before sending notification */
+ SET_NULL_IF_NO_MASK = 0x08,
+ /* Zero out the text buffer before sending it to parent */
+ ZERO_SEND = 0x10
};
static void
@@ -1038,6 +1042,10 @@ static UINT PAGER_GetAnsiNtfCode(UINT code)
{
switch (code)
{
+ /* ComboxBoxEx */
+ case CBEN_DRAGBEGINW: return CBEN_DRAGBEGINA;
+ case CBEN_ENDEDITW: return CBEN_ENDEDITA;
+ case CBEN_GETDISPINFOW: return CBEN_GETDISPINFOA;
/* Toolbar */
case TBN_GETBUTTONINFOW: return TBN_GETBUTTONINFOA;
case TBN_GETINFOTIPW: return TBN_GETINFOTIPA;
@@ -1060,7 +1068,8 @@ static BOOL PAGER_AdjustBuffer(PAGER_INFO *infoPtr, INT size)
return TRUE;
}
-static LRESULT PAGER_SendConvertedNotify(PAGER_INFO *infoPtr, NMHDR *hdr, WCHAR **text, INT *textMax, DWORD flags)
+static LRESULT PAGER_SendConvertedNotify(PAGER_INFO *infoPtr, NMHDR *hdr, UINT *mask, UINT requiredMask, WCHAR **text,
+ INT *textMax, DWORD flags)
{
CHAR *sendBuffer = NULL;
CHAR *receiveBuffer;
@@ -1074,14 +1083,21 @@ static LRESULT PAGER_SendConvertedNotify(PAGER_INFO *infoPtr, NMHDR *hdr, WCHAR
hdr->code = PAGER_GetAnsiNtfCode(hdr->code);
+ if (mask && !(*mask & requiredMask))
+ {
+ ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
+ if (flags & SET_NULL_IF_NO_MASK) oldText = NULL;
+ goto done;
+ }
+
if (oldTextMax < 0) goto done;
- if ((*text && (flags & CONVERT_SEND)) || (!*text && (flags & SEND_EMPTY_IF_NULL)))
+ if ((*text && flags & (CONVERT_SEND | ZERO_SEND)) || (!*text && flags & SEND_EMPTY_IF_NULL))
{
bufferSize = textMax ? *textMax : lstrlenW(*text) + 1;
sendBuffer = heap_alloc_zero(bufferSize);
if (!sendBuffer) goto done;
- WideCharToMultiByte(CP_ACP, 0, *text, -1, sendBuffer, bufferSize, NULL, FALSE);
+ if (!(flags & ZERO_SEND)) WideCharToMultiByte(CP_ACP, 0, *text, -1, sendBuffer, bufferSize, NULL, FALSE);
*text = (WCHAR *)sendBuffer;
}
@@ -1117,17 +1133,50 @@ static LRESULT PAGER_Notify(PAGER_INFO *infoPtr, NMHDR *hdr)
switch (hdr->code)
{
+ /* ComboBoxEx */
+ case CBEN_GETDISPINFOW:
+ {
+ NMCOMBOBOXEXW *nmcbe = (NMCOMBOBOXEXW *)hdr;
+ return PAGER_SendConvertedNotify(infoPtr, hdr, &nmcbe->ceItem.mask, CBEIF_TEXT, &nmcbe->ceItem.pszText,
+ &nmcbe->ceItem.cchTextMax, ZERO_SEND | SET_NULL_IF_NO_MASK | CONVERT_RECEIVE);
+ }
+ case CBEN_DRAGBEGINW:
+ {
+ NMCBEDRAGBEGINW *nmdbW = (NMCBEDRAGBEGINW *)hdr;
+ NMCBEDRAGBEGINA nmdbA = {0};
+ nmdbA.hdr.code = PAGER_GetAnsiNtfCode(nmdbW->hdr.code);
+ nmdbA.hdr.hwndFrom = nmdbW->hdr.hwndFrom;
+ nmdbA.hdr.idFrom = nmdbW->hdr.idFrom;
+ nmdbA.iItemid = nmdbW->iItemid;
+ WideCharToMultiByte(CP_ACP, 0, nmdbW->szText, ARRAY_SIZE(nmdbW->szText), nmdbA.szText, ARRAY_SIZE(nmdbA.szText),
+ NULL, FALSE);
+ return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)&nmdbA);
+ }
+ case CBEN_ENDEDITW:
+ {
+ NMCBEENDEDITW *nmedW = (NMCBEENDEDITW *)hdr;
+ NMCBEENDEDITA nmedA = {0};
+ nmedA.hdr.code = PAGER_GetAnsiNtfCode(nmedW->hdr.code);
+ nmedA.hdr.hwndFrom = nmedW->hdr.hwndFrom;
+ nmedA.hdr.idFrom = nmedW->hdr.idFrom;
+ nmedA.fChanged = nmedW->fChanged;
+ nmedA.iNewSelection = nmedW->iNewSelection;
+ nmedA.iWhy = nmedW->iWhy;
+ WideCharToMultiByte(CP_ACP, 0, nmedW->szText, ARRAY_SIZE(nmedW->szText), nmedA.szText, ARRAY_SIZE(nmedA.szText),
+ NULL, FALSE);
+ return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)&nmedA);
+ }
/* Toolbar */
case TBN_GETBUTTONINFOW:
{
NMTOOLBARW *nmtb = (NMTOOLBARW *)hdr;
- return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtb->pszText, &nmtb->cchText,
+ return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, &nmtb->pszText, &nmtb->cchText,
SEND_EMPTY_IF_NULL | CONVERT_SEND | CONVERT_RECEIVE);
}
case TBN_GETINFOTIPW:
{
NMTBGETINFOTIPW *nmtbgit = (NMTBGETINFOTIPW *)hdr;
- return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtbgit->pszText, &nmtbgit->cchTextMax, CONVERT_RECEIVE);
+ return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, &nmtbgit->pszText, &nmtbgit->cchTextMax, CONVERT_RECEIVE);
}
/* Tooltip */
case TTN_GETDISPINFOW:
diff --git a/dlls/comctl32/tests/pager.c b/dlls/comctl32/tests/pager.c
index cd7b637b91..b689549aa5 100644
--- a/dlls/comctl32/tests/pager.c
+++ b/dlls/comctl32/tests/pager.c
@@ -65,7 +65,9 @@ enum test_conversion_flags
CONVERT_RECEIVE = 0x04,
DONT_CONVERT_RECEIVE = 0x08,
SEND_EMPTY_IF_NULL = 0x10,
- DONT_SEND_EMPTY_IF_NULL = 0x20
+ DONT_SEND_EMPTY_IF_NULL = 0x20,
+ SET_NULL_IF_NO_MASK = 0x40,
+ ZERO_SEND = 0x80
};
static struct notify_test_info
@@ -578,7 +580,10 @@ static void notify_generic_text_handler(CHAR **text, INT *text_max)
{
send_data = (notify_test_info.test_id == CONVERT_SEND ? test_convert_send_data : test_dont_convert_send_data)
+ notify_test_info.sub_test_id;
- if (notify_test_info.flags & CONVERT_SEND)
+ if (notify_test_info.flags & ZERO_SEND)
+ ok(!lstrcmpA(*text, empty_a), "Code 0x%08x test 0x%08x sub test %d expect empty text, got %s\n",
+ notify_test_info.unicode, notify_test_info.test_id, notify_test_info.sub_test_id, *text);
+ else if (notify_test_info.flags & CONVERT_SEND)
ok(!lstrcmpA(send_data->expect_text, *text), "Code 0x%08x test 0x%08x sub test %d expect %s, got %s\n",
notify_test_info.unicode, notify_test_info.test_id, notify_test_info.sub_test_id,
(CHAR *)send_data->expect_text, *text);
@@ -659,6 +664,32 @@ static LRESULT WINAPI test_notify_proc(HWND hwnd, UINT message, WPARAM wParam, L
}
switch (hdr->code)
{
+ /* ComboBoxEx */
+ case CBEN_INSERTITEM:
+ case CBEN_DELETEITEM:
+ {
+ NMCOMBOBOXEXW *nmcbe = (NMCOMBOBOXEXW *)hdr;
+ notify_generic_text_handler((CHAR **)&nmcbe->ceItem.pszText, NULL);
+ break;
+ }
+ case CBEN_DRAGBEGINA:
+ {
+ NMCBEDRAGBEGINA *nmcbedb = (NMCBEDRAGBEGINA *)hdr;
+ ok(!lstrcmpA(nmcbedb->szText, test_a), "Expect %s, got %s\n", nmcbedb->szText, test_a);
+ break;
+ }
+ case CBEN_ENDEDITA:
+ {
+ NMCBEENDEDITA *nmcbeed = (NMCBEENDEDITA *)hdr;
+ ok(!lstrcmpA(nmcbeed->szText, test_a), "Expect %s, got %s\n", nmcbeed->szText, test_a);
+ break;
+ }
+ case CBEN_GETDISPINFOA:
+ {
+ NMCOMBOBOXEXA *nmcbe = (NMCOMBOBOXEXA *)hdr;
+ notify_generic_text_handler(&nmcbe->ceItem.pszText, &nmcbe->ceItem.cchTextMax);
+ break;
+ }
/* Toolbar */
case TBN_SAVE:
{
@@ -836,6 +867,33 @@ static void test_notify_generic_text_helper(HWND pager, const struct generic_tex
else
notify_test_info.test_id = DONT_SEND_EMPTY_IF_NULL;
send_notify(pager, para->code_unicode, para->code_ansi, (LPARAM)para->ptr, TRUE);
+
+ notify_test_info.test_id = SET_NULL_IF_NO_MASK;
+ memset(para->ptr, 0, para->size);
+ memset(buffer, 0, sizeof(buffer));
+ *para->text = buffer;
+ if (para->text_max) *para->text_max = ARRAY_SIZE(buffer);
+ send_notify(pager, para->code_unicode, para->code_ansi, (LPARAM)para->ptr, TRUE);
+ if(para->flags & SET_NULL_IF_NO_MASK)
+ ok(!*para->text, "Expect null text\n");
+}
+
+static void test_wm_notify_comboboxex(HWND pager)
+{
+ static NMCBEDRAGBEGINW nmcbedb;
+ static NMCBEENDEDITW nmcbeed;
+
+ /* CBEN_DRAGBEGIN */
+ memset(&nmcbedb, 0, sizeof(nmcbedb));
+ memcpy(nmcbedb.szText, test_w, sizeof(test_w));
+ send_notify(pager, CBEN_DRAGBEGINW, CBEN_DRAGBEGINA, (LPARAM)&nmcbedb, FALSE);
+ ok(!lstrcmpW(nmcbedb.szText, test_w), "Expect %s, got %s\n", wine_dbgstr_w(test_w), wine_dbgstr_w(nmcbedb.szText));
+
+ /* CBEN_ENDEDIT */
+ memset(&nmcbeed, 0, sizeof(nmcbeed));
+ memcpy(nmcbeed.szText, test_w, sizeof(test_w));
+ send_notify(pager, CBEN_ENDEDITW, CBEN_ENDEDITA, (LPARAM)&nmcbeed, FALSE);
+ ok(!lstrcmpW(nmcbeed.szText, test_w), "Expect %s, got %s\n", wine_dbgstr_w(test_w), wine_dbgstr_w(nmcbeed.szText));
}
static void test_wm_notify_tooltip(HWND pager)
@@ -880,6 +938,8 @@ static void test_wm_notify(void)
{
static const CHAR *class = "Pager notify class";
HWND parent, pager;
+ /* Combo Box Ex */
+ static NMCOMBOBOXEXW nmcbe;
/* Tool Bar */
static NMTBRESTORE nmtbr;
static NMTBSAVE nmtbs;
@@ -888,6 +948,13 @@ static void test_wm_notify(void)
static NMTBGETINFOTIPW nmtbgit;
static const struct generic_text_helper_para paras[] =
{
+ /* Combo Box Ex */
+ {&nmcbe, sizeof(nmcbe), &nmcbe.ceItem.mask, CBEIF_TEXT, &nmcbe.ceItem.pszText, &nmcbe.ceItem.cchTextMax,
+ CBEN_INSERTITEM, CBEN_INSERTITEM, DONT_CONVERT_SEND | DONT_CONVERT_RECEIVE},
+ {&nmcbe, sizeof(nmcbe), &nmcbe.ceItem.mask, CBEIF_TEXT, &nmcbe.ceItem.pszText, &nmcbe.ceItem.cchTextMax,
+ CBEN_DELETEITEM, CBEN_DELETEITEM, DONT_CONVERT_SEND | DONT_CONVERT_RECEIVE},
+ {&nmcbe, sizeof(nmcbe), &nmcbe.ceItem.mask, CBEIF_TEXT, &nmcbe.ceItem.pszText, &nmcbe.ceItem.cchTextMax,
+ CBEN_GETDISPINFOW, CBEN_GETDISPINFOA, ZERO_SEND | SET_NULL_IF_NO_MASK | DONT_CONVERT_SEND | CONVERT_RECEIVE},
/* Tool Bar */
{&nmtbs, sizeof(nmtbs), NULL, 0, (WCHAR **)&nmtbs.tbButton.iString, NULL, TBN_SAVE, TBN_SAVE,
DONT_CONVERT_SEND | DONT_CONVERT_RECEIVE},
@@ -916,6 +983,7 @@ static void test_wm_notify(void)
test_notify_generic_text_helper(pager, paras + i);
/* Tests for those that can't be covered by generic text test helper */
+ test_wm_notify_comboboxex(pager);
test_wm_notify_tooltip(pager);
DestroyWindow(parent);
--
2.18.0
Sept. 17, 2018
[PATCH v4 2/3] comctl32/pager: Support tooltip notification conversion.
by Zhiyi Zhang
Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com>
---
dlls/comctl32/pager.c | 72 ++++++++++++++++++++++++
dlls/comctl32/tests/pager.c | 106 ++++++++++++++++++++++++++++++++++++
2 files changed, 178 insertions(+)
diff --git a/dlls/comctl32/pager.c b/dlls/comctl32/pager.c
index ac63f1c07d..a6ef2eeadd 100644
--- a/dlls/comctl32/pager.c
+++ b/dlls/comctl32/pager.c
@@ -84,6 +84,8 @@ typedef struct
INT TLbtnState; /* state of top or left btn */
INT BRbtnState; /* state of bottom or right btn */
INT direction; /* direction of the scroll, (e.g. PGF_SCROLLUP) */
+ WCHAR *pwszBuffer;/* text buffer for converted notifications */
+ INT nBufferSize;/* size of the above buffer */
} PAGER_INFO;
#define TIMERID1 1
@@ -605,6 +607,7 @@ static LRESULT
PAGER_Destroy (PAGER_INFO *infoPtr)
{
SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0);
+ heap_free (infoPtr->pwszBuffer);
heap_free (infoPtr);
return 0;
}
@@ -1038,10 +1041,25 @@ static UINT PAGER_GetAnsiNtfCode(UINT code)
/* Toolbar */
case TBN_GETBUTTONINFOW: return TBN_GETBUTTONINFOA;
case TBN_GETINFOTIPW: return TBN_GETINFOTIPA;
+ /* Tooltip */
+ case TTN_GETDISPINFOW: return TTN_GETDISPINFOA;
}
return code;
}
+static BOOL PAGER_AdjustBuffer(PAGER_INFO *infoPtr, INT size)
+{
+ if (!infoPtr->pwszBuffer)
+ infoPtr->pwszBuffer = heap_alloc(size);
+ else if (infoPtr->nBufferSize < size)
+ infoPtr->pwszBuffer = heap_realloc(infoPtr->pwszBuffer, size);
+
+ if (!infoPtr->pwszBuffer) return FALSE;
+ if (infoPtr->nBufferSize < size) infoPtr->nBufferSize = size;
+
+ return TRUE;
+}
+
static LRESULT PAGER_SendConvertedNotify(PAGER_INFO *infoPtr, NMHDR *hdr, WCHAR **text, INT *textMax, DWORD flags)
{
CHAR *sendBuffer = NULL;
@@ -1093,6 +1111,8 @@ done:
static LRESULT PAGER_Notify(PAGER_INFO *infoPtr, NMHDR *hdr)
{
+ LRESULT ret;
+
if (infoPtr->bUnicode) return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
switch (hdr->code)
@@ -1109,6 +1129,58 @@ static LRESULT PAGER_Notify(PAGER_INFO *infoPtr, NMHDR *hdr)
NMTBGETINFOTIPW *nmtbgit = (NMTBGETINFOTIPW *)hdr;
return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtbgit->pszText, &nmtbgit->cchTextMax, CONVERT_RECEIVE);
}
+ /* Tooltip */
+ case TTN_GETDISPINFOW:
+ {
+ NMTTDISPINFOW *nmttdiW = (NMTTDISPINFOW *)hdr;
+ NMTTDISPINFOA nmttdiA = {0};
+ INT size;
+
+ nmttdiA.hdr.code = PAGER_GetAnsiNtfCode(nmttdiW->hdr.code);
+ nmttdiA.hdr.hwndFrom = nmttdiW->hdr.hwndFrom;
+ nmttdiA.hdr.idFrom = nmttdiW->hdr.idFrom;
+ nmttdiA.hinst = nmttdiW->hinst;
+ nmttdiA.uFlags = nmttdiW->uFlags;
+ nmttdiA.lParam = nmttdiW->lParam;
+ nmttdiA.lpszText = nmttdiA.szText;
+ WideCharToMultiByte(CP_ACP, 0, nmttdiW->szText, ARRAY_SIZE(nmttdiW->szText), nmttdiA.szText,
+ ARRAY_SIZE(nmttdiA.szText), NULL, FALSE);
+
+ ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)&nmttdiA);
+
+ nmttdiW->hinst = nmttdiA.hinst;
+ nmttdiW->uFlags = nmttdiA.uFlags;
+ nmttdiW->lParam = nmttdiA.lParam;
+
+ MultiByteToWideChar(CP_ACP, 0, nmttdiA.szText, ARRAY_SIZE(nmttdiA.szText), nmttdiW->szText,
+ ARRAY_SIZE(nmttdiW->szText));
+ if (!nmttdiA.lpszText)
+ nmttdiW->lpszText = nmttdiW->szText;
+ else if (!IS_INTRESOURCE(nmttdiA.lpszText))
+ {
+ size = MultiByteToWideChar(CP_ACP, 0, nmttdiA.lpszText, -1, 0, 0);
+ if (size > ARRAY_SIZE(nmttdiW->szText))
+ {
+ if (!PAGER_AdjustBuffer(infoPtr, size * sizeof(WCHAR))) return ret;
+ MultiByteToWideChar(CP_ACP, 0, nmttdiA.lpszText, -1, infoPtr->pwszBuffer, size);
+ nmttdiW->lpszText = infoPtr->pwszBuffer;
+ /* Override content in szText */
+ memcpy(nmttdiW->szText, nmttdiW->lpszText, min(sizeof(nmttdiW->szText), size * sizeof(WCHAR)));
+ }
+ else
+ {
+ MultiByteToWideChar(CP_ACP, 0, nmttdiA.lpszText, -1, nmttdiW->szText, ARRAY_SIZE(nmttdiW->szText));
+ nmttdiW->lpszText = nmttdiW->szText;
+ }
+ }
+ else
+ {
+ nmttdiW->szText[0] = 0;
+ nmttdiW->lpszText = (WCHAR *)nmttdiA.lpszText;
+ }
+
+ return ret;
+ }
}
/* Other notifications, no need to convert */
return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
diff --git a/dlls/comctl32/tests/pager.c b/dlls/comctl32/tests/pager.c
index 6ff4e259ca..cd7b637b91 100644
--- a/dlls/comctl32/tests/pager.c
+++ b/dlls/comctl32/tests/pager.c
@@ -35,12 +35,26 @@ static CHAR test_a[] = {'t', 'e', 's', 't', 0};
static WCHAR empty_w[] = {0};
static CHAR empty_a[] = {0};
static CHAR large_a[] = "You should have received a copy of the GNU Lesser General Public License along with this ...";
+static WCHAR large_w[] =
+{
+ 'Y', 'o', 'u', ' ', 's', 'h', 'o', 'u', 'l', 'd', ' ', 'h', 'a', 'v', 'e', ' ', 'r', 'e', 'c', 'e', 'i', 'v', 'e',
+ 'd', ' ', 'a', ' ', 'c', 'o', 'p', 'y', ' ', 'o', 'f', ' ', 't', 'h', 'e', ' ', 'G', 'N', 'U', ' ', 'L', 'e', 's',
+ 's', 'e', 'r', ' ', 'G', 'e', 'n', 'e', 'r', 'a', 'l', ' ', 'P', 'u', 'b', 'l', 'i', 'c', ' ', 'L', 'i', 'c', 'e',
+ 'n', 's', 'e', ' ', 'a', 'l', 'o', 'n', 'g', ' ', 'w', 'i', 't', 'h', ' ', 't', 'h', 'i', 's', ' ', '.', '.', '.', 0
+};
static WCHAR large_truncated_65_w[65] =
{
'Y', 'o', 'u', ' ', 's', 'h', 'o', 'u', 'l', 'd', ' ', 'h', 'a', 'v', 'e', ' ', 'r', 'e', 'c', 'e', 'i', 'v',
'e', 'd', ' ', 'a', ' ', 'c', 'o', 'p', 'y', ' ', 'o', 'f', ' ', 't', 'h', 'e', ' ', 'G', 'N', 'U', ' ', 'L',
'e', 's', 's', 'e', 'r', ' ', 'G', 'e', 'n', 'e', 'r', 'a', 'l', ' ', 'P', 'u', 'b', 'l', 'i', 'c', 0
};
+static WCHAR large_truncated_80_w[80] =
+{
+ 'Y', 'o', 'u', ' ', 's', 'h', 'o', 'u', 'l', 'd', ' ', 'h', 'a', 'v', 'e', ' ', 'r', 'e', 'c', 'e',
+ 'i', 'v', 'e', 'd', ' ', 'a', ' ', 'c', 'o', 'p', 'y', ' ', 'o', 'f', ' ', 't', 'h', 'e', ' ', 'G',
+ 'N', 'U', ' ', 'L', 'e', 's', 's', 'e', 'r', ' ', 'G', 'e', 'n', 'e', 'r', 'a', 'l', ' ', 'P', 'u',
+ 'b', 'l', 'i', 'c', ' ', 'L', 'i', 'c', 'e', 'n', 's', 'e', ' ', 'a', 'l', 'o', 'n', 'g', ' ', 'w'
+};
static WCHAR buffer[64];
/* Text field conversion test behavior flags. */
@@ -132,6 +146,38 @@ static const struct notify_test_receive test_dont_convert_receive_data[] =
{empty_w, sizeof(empty_w), ARRAY_SIZE(buffer), test_a, NULL, 0, -1, test_a, ARRAY_SIZE(buffer)},
};
+static const struct notify_test_tooltip
+{
+ /* Data for parent to write */
+ CHAR *write_sztext;
+ INT write_sztext_size;
+ CHAR *write_lpsztext;
+ HMODULE write_hinst;
+ /* Data when message returned */
+ WCHAR *return_sztext;
+ INT return_sztext_size;
+ WCHAR *return_lpsztext;
+ HMODULE return_hinst;
+ /* Data expected by parent */
+ CHAR *expect_sztext;
+ /* Data send to parent */
+ WCHAR *send_sztext;
+ INT send_sztext_size;
+ WCHAR *send_lpsztext;
+} test_tooltip_data[] =
+{
+ {NULL, 0, NULL, NULL, empty_w, -1, empty_w},
+ {test_a, sizeof(test_a), NULL, NULL, test_w, -1, test_w},
+ {test_a, sizeof(test_a), test_a, NULL, test_w, -1, test_w},
+ {test_a, sizeof(test_a), (CHAR *)1, (HMODULE)0xdeadbeef, empty_w, -1, (WCHAR *)1, (HMODULE)0xdeadbeef},
+ {test_a, sizeof(test_a), test_a, (HMODULE)0xdeadbeef, test_w, -1, test_w, (HMODULE)0xdeadbeef},
+ {NULL, 0, test_a, NULL, test_w, -1, test_w},
+ {test_a, 2, test_a, NULL, test_w, -1, test_w},
+ {NULL, 0, NULL, NULL, test_w, -1, test_w, NULL, test_a, test_w, sizeof(test_w)},
+ {NULL, 0, NULL, NULL, empty_w, -1, empty_w, NULL, empty_a, NULL, 0, test_w},
+ {NULL, 0, large_a, NULL, large_truncated_80_w, sizeof(large_truncated_80_w), large_w}
+};
+
#define CHILD1_ID 1
#define CHILD2_ID 2
@@ -575,6 +621,19 @@ static void notify_generic_text_handler(CHAR **text, INT *text_max)
}
}
+static void notify_tooltip_handler(NMTTDISPINFOA *nm)
+{
+ const struct notify_test_tooltip *data = test_tooltip_data + notify_test_info.sub_test_id;
+ ok(nm->lpszText == nm->szText, "Sub test %d expect %p, got %p\n", notify_test_info.sub_test_id, nm->szText,
+ nm->lpszText);
+ if (data->expect_sztext)
+ ok(!lstrcmpA(data->expect_sztext, nm->szText), "Sub test %d expect %s, got %s\n", notify_test_info.sub_test_id,
+ data->expect_sztext, nm->szText);
+ if (data->write_sztext) memcpy(nm->szText, data->write_sztext, data->write_sztext_size);
+ if (data->write_lpsztext) nm->lpszText = data->write_lpsztext;
+ if (data->write_hinst) nm->hinst = data->write_hinst;
+}
+
static LRESULT WINAPI test_notify_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static const WCHAR test[] = {'t', 'e', 's', 't', 0};
@@ -631,6 +690,12 @@ static LRESULT WINAPI test_notify_proc(HWND hwnd, UINT message, WPARAM wParam, L
notify_generic_text_handler(&nmtbgit->pszText, &nmtbgit->cchTextMax);
break;
}
+ /* Tooltip */
+ case TTN_GETDISPINFOA:
+ {
+ notify_tooltip_handler((NMTTDISPINFOA *)hdr);
+ break;
+ }
default:
ok(0, "Unexpected message 0x%08x\n", hdr->code);
}
@@ -773,6 +838,44 @@ static void test_notify_generic_text_helper(HWND pager, const struct generic_tex
send_notify(pager, para->code_unicode, para->code_ansi, (LPARAM)para->ptr, TRUE);
}
+static void test_wm_notify_tooltip(HWND pager)
+{
+ NMTTDISPINFOW nmttdi;
+ const struct notify_test_tooltip *data;
+ INT i;
+
+ for (i = 0; i < ARRAY_SIZE(test_tooltip_data); i++)
+ {
+ data = test_tooltip_data + i;
+ notify_test_info.sub_test_id = i;
+
+ memset(&nmttdi, 0, sizeof(nmttdi));
+ if (data->send_sztext) memcpy(nmttdi.szText, data->send_sztext, data->send_sztext_size);
+ if (data->send_lpsztext) nmttdi.lpszText = data->send_lpsztext;
+ send_notify(pager, TTN_GETDISPINFOW, TTN_GETDISPINFOA, (LPARAM)&nmttdi, FALSE);
+ if (data->return_sztext)
+ {
+ if (data->return_sztext_size == -1)
+ ok(!lstrcmpW(nmttdi.szText, data->return_sztext), "Sub test %d expect %s, got %s\n", i,
+ wine_dbgstr_w(data->return_sztext), wine_dbgstr_w(nmttdi.szText));
+ else
+ ok(!memcmp(nmttdi.szText, data->return_sztext, data->return_sztext_size), "Wrong szText content\n");
+ }
+ if (data->return_lpsztext)
+ {
+ if (IS_INTRESOURCE(data->return_lpsztext))
+ ok(nmttdi.lpszText == data->return_lpsztext, "Sub test %d expect %s, got %s\n", i,
+ wine_dbgstr_w(data->return_lpsztext), wine_dbgstr_w(nmttdi.lpszText));
+ else
+ ok(!lstrcmpW(nmttdi.lpszText, data->return_lpsztext), "Test %d expect %s, got %s\n", i,
+ wine_dbgstr_w(data->return_lpsztext), wine_dbgstr_w(nmttdi.lpszText));
+ }
+ if (data->return_hinst)
+ ok(nmttdi.hinst == data->return_hinst, "Sub test %d expect %p, got %p\n", i, data->return_hinst,
+ nmttdi.hinst);
+ }
+}
+
static void test_wm_notify(void)
{
static const CHAR *class = "Pager notify class";
@@ -812,6 +915,9 @@ static void test_wm_notify(void)
for (i = 0; i < ARRAY_SIZE(paras); i++)
test_notify_generic_text_helper(pager, paras + i);
+ /* Tests for those that can't be covered by generic text test helper */
+ test_wm_notify_tooltip(pager);
+
DestroyWindow(parent);
UnregisterClassA(class, GetModuleHandleA(NULL));
}
--
2.18.0
Sept. 17, 2018
[PATCH v4 1/3] comctl32/pager: Support toolbar notification conversion.
by Zhiyi Zhang
Fix BibleWorks 10 not displaying toolbar.
Pager always wants Unicode notifications from its children.
And the parent of pager may want ANSI notifications. In this
case, pager need to convert Unicode notifications to ANSI ones
before sending them to parent and convert ANSI notifications
from parent to Unicode before returning the result to its children.
Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com>
---
v4: Supersede 150964,150965,150966. Minor changes as suggested by Nikolay.
dlls/comctl32/pager.c | 96 +++++++++
dlls/comctl32/tests/pager.c | 398 ++++++++++++++++++++++++++++++++++++
2 files changed, 494 insertions(+)
diff --git a/dlls/comctl32/pager.c b/dlls/comctl32/pager.c
index de63cad717..ac63f1c07d 100644
--- a/dlls/comctl32/pager.c
+++ b/dlls/comctl32/pager.c
@@ -91,6 +91,17 @@ typedef struct
#define INITIAL_DELAY 500
#define REPEAT_DELAY 50
+/* Text field conversion behavior flags for PAGER_SendConvertedNotify() */
+enum conversion_flags
+{
+ /* Convert Unicode text to ANSI for parent before sending. If not set, do nothing */
+ CONVERT_SEND = 0x01,
+ /* Convert ANSI text from parent back to Unicode for children */
+ CONVERT_RECEIVE = 0x02,
+ /* Send empty text to parent if text is NULL. Original text pointer still remains NULL */
+ SEND_EMPTY_IF_NULL = 0x04
+};
+
static void
PAGER_GetButtonRects(const PAGER_INFO* infoPtr, RECT* prcTopLeft, RECT* prcBottomRight, BOOL bClientCoords)
{
@@ -1020,6 +1031,89 @@ static LRESULT PAGER_NotifyFormat(PAGER_INFO *infoPtr, INT command)
}
}
+static UINT PAGER_GetAnsiNtfCode(UINT code)
+{
+ switch (code)
+ {
+ /* Toolbar */
+ case TBN_GETBUTTONINFOW: return TBN_GETBUTTONINFOA;
+ case TBN_GETINFOTIPW: return TBN_GETINFOTIPA;
+ }
+ return code;
+}
+
+static LRESULT PAGER_SendConvertedNotify(PAGER_INFO *infoPtr, NMHDR *hdr, WCHAR **text, INT *textMax, DWORD flags)
+{
+ CHAR *sendBuffer = NULL;
+ CHAR *receiveBuffer;
+ INT bufferSize;
+ WCHAR *oldText;
+ INT oldTextMax;
+ LRESULT ret = NO_ERROR;
+
+ oldText = *text;
+ oldTextMax = textMax ? *textMax : 0;
+
+ hdr->code = PAGER_GetAnsiNtfCode(hdr->code);
+
+ if (oldTextMax < 0) goto done;
+
+ if ((*text && (flags & CONVERT_SEND)) || (!*text && (flags & SEND_EMPTY_IF_NULL)))
+ {
+ bufferSize = textMax ? *textMax : lstrlenW(*text) + 1;
+ sendBuffer = heap_alloc_zero(bufferSize);
+ if (!sendBuffer) goto done;
+ WideCharToMultiByte(CP_ACP, 0, *text, -1, sendBuffer, bufferSize, NULL, FALSE);
+ *text = (WCHAR *)sendBuffer;
+ }
+
+ ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
+
+ if (*text && oldText && (flags & CONVERT_RECEIVE))
+ {
+ /* MultiByteToWideChar requires that source and destination are not the same buffer */
+ if (*text == oldText)
+ {
+ bufferSize = lstrlenA((CHAR *)*text) + 1;
+ receiveBuffer = heap_alloc(bufferSize);
+ if (!receiveBuffer) goto done;
+ memcpy(receiveBuffer, *text, bufferSize);
+ MultiByteToWideChar(CP_ACP, 0, receiveBuffer, bufferSize, oldText, oldTextMax);
+ heap_free(receiveBuffer);
+ }
+ else
+ MultiByteToWideChar(CP_ACP, 0, (CHAR *)*text, -1, oldText, oldTextMax);
+ }
+
+done:
+ heap_free(sendBuffer);
+ *text = oldText;
+ return ret;
+}
+
+static LRESULT PAGER_Notify(PAGER_INFO *infoPtr, NMHDR *hdr)
+{
+ if (infoPtr->bUnicode) return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
+
+ switch (hdr->code)
+ {
+ /* Toolbar */
+ case TBN_GETBUTTONINFOW:
+ {
+ NMTOOLBARW *nmtb = (NMTOOLBARW *)hdr;
+ return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtb->pszText, &nmtb->cchText,
+ SEND_EMPTY_IF_NULL | CONVERT_SEND | CONVERT_RECEIVE);
+ }
+ case TBN_GETINFOTIPW:
+ {
+ NMTBGETINFOTIPW *nmtbgit = (NMTBGETINFOTIPW *)hdr;
+ return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtbgit->pszText, &nmtbgit->cchTextMax, CONVERT_RECEIVE);
+ }
+ }
+ /* Other notifications, no need to convert */
+ return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
+}
+
static LRESULT WINAPI
PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
@@ -1115,6 +1209,8 @@ PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
return PAGER_NotifyFormat (infoPtr, lParam);
case WM_NOTIFY:
+ return PAGER_Notify (infoPtr, (NMHDR *)lParam);
+
case WM_COMMAND:
return SendMessageW (infoPtr->hwndNotify, uMsg, wParam, lParam);
diff --git a/dlls/comctl32/tests/pager.c b/dlls/comctl32/tests/pager.c
index a1f6683a57..6ff4e259ca 100644
--- a/dlls/comctl32/tests/pager.c
+++ b/dlls/comctl32/tests/pager.c
@@ -30,6 +30,107 @@
static HWND parent_wnd, child1_wnd, child2_wnd;
static INT notify_format;
static BOOL notify_query_received;
+static WCHAR test_w[] = {'t', 'e', 's', 't', 0};
+static CHAR test_a[] = {'t', 'e', 's', 't', 0};
+static WCHAR empty_w[] = {0};
+static CHAR empty_a[] = {0};
+static CHAR large_a[] = "You should have received a copy of the GNU Lesser General Public License along with this ...";
+static WCHAR large_truncated_65_w[65] =
+{
+ 'Y', 'o', 'u', ' ', 's', 'h', 'o', 'u', 'l', 'd', ' ', 'h', 'a', 'v', 'e', ' ', 'r', 'e', 'c', 'e', 'i', 'v',
+ 'e', 'd', ' ', 'a', ' ', 'c', 'o', 'p', 'y', ' ', 'o', 'f', ' ', 't', 'h', 'e', ' ', 'G', 'N', 'U', ' ', 'L',
+ 'e', 's', 's', 'e', 'r', ' ', 'G', 'e', 'n', 'e', 'r', 'a', 'l', ' ', 'P', 'u', 'b', 'l', 'i', 'c', 0
+};
+static WCHAR buffer[64];
+
+/* Text field conversion test behavior flags. */
+enum test_conversion_flags
+{
+ CONVERT_SEND = 0x01,
+ DONT_CONVERT_SEND = 0x02,
+ CONVERT_RECEIVE = 0x04,
+ DONT_CONVERT_RECEIVE = 0x08,
+ SEND_EMPTY_IF_NULL = 0x10,
+ DONT_SEND_EMPTY_IF_NULL = 0x20
+};
+
+static struct notify_test_info
+{
+ UINT unicode;
+ UINT ansi;
+ UINT_PTR id_from;
+ HWND hwnd_from;
+ /* Whether parent received notification */
+ BOOL received;
+ UINT test_id;
+ UINT sub_test_id;
+ /* Text field conversion test behavior flag */
+ DWORD flags;
+} notify_test_info;
+
+struct notify_test_send
+{
+ /* Data sent to pager */
+ WCHAR *send_text;
+ INT send_text_size;
+ INT send_text_max;
+ /* Data expected by parent of pager */
+ void *expect_text;
+};
+
+struct notify_test_receive
+{
+ /* Data sent to pager */
+ WCHAR *send_text;
+ INT send_text_size;
+ INT send_text_max;
+ /* Data for parent to write */
+ CHAR *write_pointer;
+ CHAR *write_text;
+ INT write_text_size;
+ INT write_text_max;
+ /* Data when message returned */
+ void *return_text;
+ INT return_text_max;
+};
+
+struct generic_text_helper_para
+{
+ void *ptr;
+ size_t size;
+ UINT *mask;
+ UINT required_mask;
+ WCHAR **text;
+ INT *text_max;
+ UINT code_unicode;
+ UINT code_ansi;
+ DWORD flags;
+};
+
+static const struct notify_test_send test_convert_send_data[] =
+{
+ {test_w, sizeof(test_w), ARRAY_SIZE(buffer), test_a}
+};
+
+static const struct notify_test_send test_dont_convert_send_data[] =
+{
+ {test_w, sizeof(test_w), ARRAY_SIZE(buffer), test_w}
+};
+
+static const struct notify_test_receive test_convert_receive_data[] =
+{
+ {empty_w, sizeof(empty_w), ARRAY_SIZE(buffer), NULL, test_a, sizeof(test_a), -1, test_w, ARRAY_SIZE(buffer)},
+ {empty_w, sizeof(empty_w), ARRAY_SIZE(buffer), test_a, NULL, 0, -1, test_w, ARRAY_SIZE(buffer)},
+ {NULL, sizeof(empty_w), ARRAY_SIZE(buffer), test_a, NULL, 0, -1, NULL, ARRAY_SIZE(buffer)},
+ {empty_w, sizeof(empty_w), ARRAY_SIZE(buffer), large_a, NULL, 0, -1, large_truncated_65_w, ARRAY_SIZE(buffer)},
+ {empty_w, sizeof(empty_w), ARRAY_SIZE(buffer), empty_a, 0, 0, 1, empty_w, 1},
+};
+
+static const struct notify_test_receive test_dont_convert_receive_data[] =
+{
+ {empty_w, sizeof(empty_w), ARRAY_SIZE(buffer), NULL, test_a, sizeof(test_a), -1, test_a, ARRAY_SIZE(buffer)},
+ {empty_w, sizeof(empty_w), ARRAY_SIZE(buffer), test_a, NULL, 0, -1, test_a, ARRAY_SIZE(buffer)},
+};
#define CHILD1_ID 1
#define CHILD2_ID 2
@@ -419,6 +520,302 @@ static void test_wm_notifyformat(void)
UnregisterClassW(class_w, GetModuleHandleW(NULL));
}
+static void notify_generic_text_handler(CHAR **text, INT *text_max)
+{
+ const struct notify_test_send *send_data;
+ const struct notify_test_receive *receive_data;
+
+ switch (notify_test_info.test_id)
+ {
+ case CONVERT_SEND:
+ case DONT_CONVERT_SEND:
+ {
+ send_data = (notify_test_info.test_id == CONVERT_SEND ? test_convert_send_data : test_dont_convert_send_data)
+ + notify_test_info.sub_test_id;
+ if (notify_test_info.flags & CONVERT_SEND)
+ ok(!lstrcmpA(send_data->expect_text, *text), "Code 0x%08x test 0x%08x sub test %d expect %s, got %s\n",
+ notify_test_info.unicode, notify_test_info.test_id, notify_test_info.sub_test_id,
+ (CHAR *)send_data->expect_text, *text);
+ else
+ ok(!lstrcmpW((WCHAR *)send_data->expect_text, (WCHAR *)*text),
+ "Code 0x%08x test 0x%08x sub test %d expect %s, got %s\n", notify_test_info.unicode,
+ notify_test_info.test_id, notify_test_info.sub_test_id, wine_dbgstr_w((WCHAR *)send_data->expect_text),
+ wine_dbgstr_w((WCHAR *)*text));
+ if (text_max)
+ ok(*text_max == send_data->send_text_max, "Code 0x%08x test 0x%08x sub test %d expect %d, got %d\n",
+ notify_test_info.unicode, notify_test_info.test_id, notify_test_info.sub_test_id,
+ send_data->send_text_max, *text_max);
+ break;
+ }
+ case CONVERT_RECEIVE:
+ case DONT_CONVERT_RECEIVE:
+ {
+ receive_data = (notify_test_info.test_id == CONVERT_RECEIVE ? test_convert_receive_data : test_dont_convert_receive_data)
+ + notify_test_info.sub_test_id;
+ if (text_max)
+ ok(*text_max == receive_data->send_text_max, "Code 0x%08x test 0x%08x sub test %d expect %d, got %d\n",
+ notify_test_info.unicode, notify_test_info.test_id, notify_test_info.sub_test_id,
+ receive_data->send_text_max, *text_max);
+
+ if (receive_data->write_text)
+ memcpy(*text, receive_data->write_text, receive_data->write_text_size);
+ else
+ *text = receive_data->write_pointer;
+ if (text_max && receive_data->write_text_max != -1) *text_max = receive_data->write_text_max;
+ break;
+ }
+ case SEND_EMPTY_IF_NULL:
+ ok(!lstrcmpA(*text, empty_a), "Code 0x%08x test 0x%08x sub test %d expect empty text, got %s\n",
+ notify_test_info.unicode, notify_test_info.test_id, notify_test_info.sub_test_id, *text);
+ break;
+ case DONT_SEND_EMPTY_IF_NULL:
+ ok(!*text, "Code 0x%08x test 0x%08x sub test %d expect null text\n", notify_test_info.unicode,
+ notify_test_info.test_id, notify_test_info.sub_test_id);
+ break;
+ }
+}
+
+static LRESULT WINAPI test_notify_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ static const WCHAR test[] = {'t', 'e', 's', 't', 0};
+ switch (message)
+ {
+ case WM_NOTIFY:
+ {
+ NMHDR *hdr = (NMHDR *)lParam;
+
+ /* Not notifications we want to test */
+ if (!notify_test_info.unicode) break;
+ ok(!notify_test_info.received, "Extra notification received\n");
+
+ ok(wParam == notify_test_info.id_from, "Expect %ld, got %ld\n", notify_test_info.id_from, wParam);
+ ok(hdr->code == notify_test_info.ansi, "Expect 0x%08x, got 0x%08x\n", notify_test_info.ansi, hdr->code);
+ ok(hdr->idFrom == notify_test_info.id_from, "Expect %ld, got %ld\n", notify_test_info.id_from, wParam);
+ ok(hdr->hwndFrom == notify_test_info.hwnd_from, "Expect %p, got %p\n", notify_test_info.hwnd_from, hdr->hwndFrom);
+
+ if (hdr->code != notify_test_info.ansi)
+ {
+ skip("Notification code mismatch, skipping lParam check\n");
+ return 0;
+ }
+ switch (hdr->code)
+ {
+ /* Toolbar */
+ case TBN_SAVE:
+ {
+ NMTBSAVE *nmtbs = (NMTBSAVE *)hdr;
+ notify_generic_text_handler((CHAR **)&nmtbs->tbButton.iString, NULL);
+ break;
+ }
+ case TBN_RESTORE:
+ {
+ NMTBRESTORE *nmtbr = (NMTBRESTORE *)hdr;
+ notify_generic_text_handler((CHAR **)&nmtbr->tbButton.iString, NULL);
+ break;
+ }
+ case TBN_GETBUTTONINFOA:
+ {
+ NMTOOLBARA *nmtb = (NMTOOLBARA *)hdr;
+ notify_generic_text_handler(&nmtb->pszText, &nmtb->cchText);
+ break;
+ }
+ case TBN_GETDISPINFOW:
+ {
+ NMTBDISPINFOW *nmtbdi = (NMTBDISPINFOW *)hdr;
+ notify_generic_text_handler((CHAR **)&nmtbdi->pszText, &nmtbdi->cchText);
+ break;
+ }
+ case TBN_GETINFOTIPA:
+ {
+ NMTBGETINFOTIPA *nmtbgit = (NMTBGETINFOTIPA *)hdr;
+ notify_generic_text_handler(&nmtbgit->pszText, &nmtbgit->cchTextMax);
+ break;
+ }
+ default:
+ ok(0, "Unexpected message 0x%08x\n", hdr->code);
+ }
+ notify_test_info.received = TRUE;
+ ok(!lstrcmpA(test_a, "test"), "test_a got modified\n");
+ ok(!lstrcmpW(test_w, test), "test_w got modified\n");
+ return 0;
+ }
+ case WM_NOTIFYFORMAT:
+ if (lParam == NF_QUERY) return NFR_ANSI;
+ break;
+ }
+ return DefWindowProcA(hwnd, message, wParam, lParam);
+}
+
+static BOOL register_test_notify_class(void)
+{
+ WNDCLASSA cls = {0};
+
+ cls.lpfnWndProc = test_notify_proc;
+ cls.hInstance = GetModuleHandleA(NULL);
+ cls.lpszClassName = "Pager notify class";
+ return RegisterClassA(&cls);
+}
+
+static void send_notify(HWND pager, UINT unicode, UINT ansi, LPARAM lParam, BOOL code_change)
+{
+ NMHDR *hdr = (NMHDR *)lParam;
+
+ notify_test_info.unicode = unicode;
+ notify_test_info.id_from = 1;
+ notify_test_info.hwnd_from = child1_wnd;
+ notify_test_info.ansi = ansi;
+ notify_test_info.received = FALSE;
+
+ hdr->code = unicode;
+ hdr->idFrom = 1;
+ hdr->hwndFrom = child1_wnd;
+
+ SendMessageW(pager, WM_NOTIFY, hdr->idFrom, lParam);
+ ok(notify_test_info.received, "Expect notification received\n");
+ ok(hdr->code == code_change ? ansi : unicode, "Expect 0x%08x, got 0x%08x\n", hdr->code,
+ code_change ? ansi : unicode);
+}
+
+/* Send notify to test text field conversion. In parent proc notify_generic_text_handler() handles these messages */
+static void test_notify_generic_text_helper(HWND pager, const struct generic_text_helper_para *para)
+{
+ const struct notify_test_send *send_data;
+ const struct notify_test_receive *receive_data;
+ INT array_size;
+ INT i;
+
+ notify_test_info.flags = para->flags;
+
+ if (para->flags & (CONVERT_SEND | DONT_CONVERT_SEND))
+ {
+ if (para->flags & CONVERT_SEND)
+ {
+ notify_test_info.test_id = CONVERT_SEND;
+ send_data = test_convert_send_data;
+ array_size = ARRAY_SIZE(test_convert_send_data);
+ }
+ else
+ {
+ notify_test_info.test_id = DONT_CONVERT_SEND;
+ send_data = test_dont_convert_send_data;
+ array_size = ARRAY_SIZE(test_dont_convert_send_data);
+ }
+
+ for (i = 0; i < array_size; i++)
+ {
+ const struct notify_test_send *data = send_data + i;
+ notify_test_info.sub_test_id = i;
+
+ memset(para->ptr, 0, para->size);
+ if (para->mask) *para->mask = para->required_mask;
+ if (data->send_text)
+ {
+ memcpy(buffer, data->send_text, data->send_text_size);
+ *para->text = buffer;
+ }
+ if (para->text_max) *para->text_max = data->send_text_max;
+ send_notify(pager, para->code_unicode, para->code_ansi, (LPARAM)para->ptr, TRUE);
+ }
+ }
+
+ if (para->flags & (CONVERT_RECEIVE | DONT_CONVERT_RECEIVE))
+ {
+ if (para->flags & CONVERT_RECEIVE)
+ {
+ notify_test_info.test_id = CONVERT_RECEIVE;
+ receive_data = test_convert_receive_data;
+ array_size = ARRAY_SIZE(test_convert_receive_data);
+ }
+ else
+ {
+ notify_test_info.test_id = DONT_CONVERT_RECEIVE;
+ receive_data = test_dont_convert_receive_data;
+ array_size = ARRAY_SIZE(test_dont_convert_receive_data);
+ }
+
+ for (i = 0; i < array_size; i++)
+ {
+ const struct notify_test_receive *data = receive_data + i;
+ notify_test_info.sub_test_id = i;
+
+ memset(para->ptr, 0, para->size);
+ if (para->mask) *para->mask = para->required_mask;
+ if (data->send_text)
+ {
+ memcpy(buffer, data->send_text, data->send_text_size);
+ *para->text = buffer;
+ }
+ if (para->text_max) *para->text_max = data->send_text_max;
+ send_notify(pager, para->code_unicode, para->code_ansi, (LPARAM)para->ptr, TRUE);
+ if (data->return_text)
+ {
+ if (para->flags & CONVERT_RECEIVE)
+ ok(!lstrcmpW(data->return_text, *para->text), "Code 0x%08x sub test %d expect %s, got %s\n",
+ para->code_unicode, i, wine_dbgstr_w((WCHAR *)data->return_text), wine_dbgstr_w(*para->text));
+ else
+ ok(!lstrcmpA(data->return_text, (CHAR *)*para->text), "Code 0x%08x sub test %d expect %s, got %s\n",
+ para->code_unicode, i, (CHAR *)data->return_text, (CHAR *)*para->text);
+ }
+ if (para->text_max)
+ ok(data->return_text_max == *para->text_max, "Code 0x%08x sub test %d expect %d, got %d\n",
+ para->code_unicode, i, data->return_text_max, *para->text_max);
+ }
+ }
+
+ /* Extra tests for other behavior flags that are not worth it to create their own test arrays */
+ memset(para->ptr, 0, para->size);
+ if (para->mask) *para->mask = para->required_mask;
+ if (para->text_max) *para->text_max = 1;
+ if (para->flags & SEND_EMPTY_IF_NULL)
+ notify_test_info.test_id = SEND_EMPTY_IF_NULL;
+ else
+ notify_test_info.test_id = DONT_SEND_EMPTY_IF_NULL;
+ send_notify(pager, para->code_unicode, para->code_ansi, (LPARAM)para->ptr, TRUE);
+}
+
+static void test_wm_notify(void)
+{
+ static const CHAR *class = "Pager notify class";
+ HWND parent, pager;
+ /* Tool Bar */
+ static NMTBRESTORE nmtbr;
+ static NMTBSAVE nmtbs;
+ static NMTOOLBARW nmtb;
+ static NMTBDISPINFOW nmtbdi;
+ static NMTBGETINFOTIPW nmtbgit;
+ static const struct generic_text_helper_para paras[] =
+ {
+ /* Tool Bar */
+ {&nmtbs, sizeof(nmtbs), NULL, 0, (WCHAR **)&nmtbs.tbButton.iString, NULL, TBN_SAVE, TBN_SAVE,
+ DONT_CONVERT_SEND | DONT_CONVERT_RECEIVE},
+ {&nmtbr, sizeof(nmtbr), NULL, 0, (WCHAR **)&nmtbr.tbButton.iString, NULL, TBN_RESTORE, TBN_RESTORE,
+ DONT_CONVERT_SEND | DONT_CONVERT_RECEIVE},
+ {&nmtbdi, sizeof(nmtbdi), &nmtbdi.dwMask, TBNF_TEXT, &nmtbdi.pszText, &nmtbdi.cchText, TBN_GETDISPINFOW,
+ TBN_GETDISPINFOW, DONT_CONVERT_SEND | DONT_CONVERT_RECEIVE},
+ {&nmtb, sizeof(nmtb), NULL, 0, &nmtb.pszText, &nmtb.cchText, TBN_GETBUTTONINFOW, TBN_GETBUTTONINFOA,
+ SEND_EMPTY_IF_NULL | CONVERT_SEND | CONVERT_RECEIVE},
+ {&nmtbgit, sizeof(nmtbgit), NULL, 0, &nmtbgit.pszText, &nmtbgit.cchTextMax, TBN_GETINFOTIPW, TBN_GETINFOTIPA,
+ DONT_CONVERT_SEND | CONVERT_RECEIVE}
+ };
+ INT i;
+
+ ok(register_test_notify_class(), "Register test class failed, error 0x%08x\n", GetLastError());
+
+ parent = CreateWindowA(class, "parent", WS_OVERLAPPED, 0, 0, 100, 100, 0, 0, GetModuleHandleA(0), 0);
+ ok(parent != NULL, "CreateWindow failed\n");
+ pager = CreateWindowA(WC_PAGESCROLLERA, "pager", WS_CHILD, 0, 0, 100, 100, parent, 0, GetModuleHandleA(0), 0);
+ ok(pager != NULL, "CreateWindow failed\n");
+ child1_wnd = CreateWindowA(class, "child", WS_CHILD, 0, 0, 100, 100, pager, (HMENU)1, GetModuleHandleA(0), 0);
+ ok(child1_wnd != NULL, "CreateWindow failed\n");
+ SendMessageW(pager, PGM_SETCHILD, 0, (LPARAM)child1_wnd);
+
+ for (i = 0; i < ARRAY_SIZE(paras); i++)
+ test_notify_generic_text_helper(pager, paras + i);
+
+ DestroyWindow(parent);
+ UnregisterClassA(class, GetModuleHandleA(NULL));
+}
+
static void init_functions(void)
{
HMODULE mod = LoadLibraryA("comctl32.dll");
@@ -447,6 +844,7 @@ START_TEST(pager)
test_pager();
test_wm_notifyformat();
+ test_wm_notify();
DestroyWindow(parent_wnd);
}
--
2.18.0
Sept. 17, 2018