ReactOS.org
Sign In
Sign Up
Sign In
Sign Up
Manage this list
×
Keyboard Shortcuts
Thread View
j
: Next unread message
k
: Previous unread message
j a
: Jump to all threads
j l
: Jump to MailingList overview
2025
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
List overview
Download
Ros-diffs
January 2019
----- 2025 -----
January 2025
----- 2024 -----
December 2024
November 2024
October 2024
September 2024
August 2024
July 2024
June 2024
May 2024
April 2024
March 2024
February 2024
January 2024
----- 2023 -----
December 2023
November 2023
October 2023
September 2023
August 2023
July 2023
June 2023
May 2023
April 2023
March 2023
February 2023
January 2023
----- 2022 -----
December 2022
November 2022
October 2022
September 2022
August 2022
July 2022
June 2022
May 2022
April 2022
March 2022
February 2022
January 2022
----- 2021 -----
December 2021
November 2021
October 2021
September 2021
August 2021
July 2021
June 2021
May 2021
April 2021
March 2021
February 2021
January 2021
----- 2020 -----
December 2020
November 2020
October 2020
September 2020
August 2020
July 2020
June 2020
May 2020
April 2020
March 2020
February 2020
January 2020
----- 2019 -----
December 2019
November 2019
October 2019
September 2019
August 2019
July 2019
June 2019
May 2019
April 2019
March 2019
February 2019
January 2019
----- 2018 -----
December 2018
November 2018
October 2018
September 2018
August 2018
July 2018
June 2018
May 2018
April 2018
March 2018
February 2018
January 2018
----- 2017 -----
December 2017
November 2017
October 2017
September 2017
August 2017
July 2017
June 2017
May 2017
April 2017
March 2017
February 2017
January 2017
----- 2016 -----
December 2016
November 2016
October 2016
September 2016
August 2016
July 2016
June 2016
May 2016
April 2016
March 2016
February 2016
January 2016
----- 2015 -----
December 2015
November 2015
October 2015
September 2015
August 2015
July 2015
June 2015
May 2015
April 2015
March 2015
February 2015
January 2015
----- 2014 -----
December 2014
November 2014
October 2014
September 2014
August 2014
July 2014
June 2014
May 2014
April 2014
March 2014
February 2014
January 2014
----- 2013 -----
December 2013
November 2013
October 2013
September 2013
August 2013
July 2013
June 2013
May 2013
April 2013
March 2013
February 2013
January 2013
----- 2012 -----
December 2012
November 2012
October 2012
September 2012
August 2012
July 2012
June 2012
May 2012
April 2012
March 2012
February 2012
January 2012
----- 2011 -----
December 2011
November 2011
October 2011
September 2011
August 2011
July 2011
June 2011
May 2011
April 2011
March 2011
February 2011
January 2011
----- 2010 -----
December 2010
November 2010
October 2010
September 2010
August 2010
July 2010
June 2010
May 2010
April 2010
March 2010
February 2010
January 2010
----- 2009 -----
December 2009
November 2009
October 2009
September 2009
August 2009
July 2009
June 2009
May 2009
April 2009
March 2009
February 2009
January 2009
----- 2008 -----
December 2008
November 2008
October 2008
September 2008
August 2008
July 2008
June 2008
May 2008
April 2008
March 2008
February 2008
January 2008
----- 2007 -----
December 2007
November 2007
October 2007
September 2007
August 2007
July 2007
June 2007
May 2007
April 2007
March 2007
February 2007
January 2007
----- 2006 -----
December 2006
November 2006
October 2006
September 2006
August 2006
July 2006
June 2006
May 2006
April 2006
March 2006
February 2006
January 2006
----- 2005 -----
December 2005
November 2005
October 2005
September 2005
August 2005
July 2005
June 2005
May 2005
April 2005
March 2005
February 2005
January 2005
----- 2004 -----
December 2004
November 2004
October 2004
September 2004
August 2004
July 2004
June 2004
May 2004
April 2004
March 2004
February 2004
ros-diffs@reactos.org
31 participants
300 discussions
Start a n
N
ew thread
[reactos] 01/01: [CRYPT32_WINETEST] Sync with Wine Staging 4.0. CORE-15682
by Amine Khaldi
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=b807af385e14fd55f4758…
commit b807af385e14fd55f475838464e23fe2a8dad68c Author: Amine Khaldi <amine.khaldi(a)reactos.org> AuthorDate: Fri Jan 25 13:19:36 2019 +0100 Commit: Amine Khaldi <amine.khaldi(a)reactos.org> CommitDate: Fri Jan 25 13:19:36 2019 +0100 [CRYPT32_WINETEST] Sync with Wine Staging 4.0. CORE-15682 --- modules/rostests/winetests/crypt32/cert.c | 65 +++++++++++++++++--- modules/rostests/winetests/crypt32/encode.c | 6 +- modules/rostests/winetests/crypt32/main.c | 52 ++++++++++++++++ modules/rostests/winetests/crypt32/oid.c | 93 +++++++++++++++++++++++++++++ modules/rostests/winetests/crypt32/store.c | 5 +- 5 files changed, 208 insertions(+), 13 deletions(-) diff --git a/modules/rostests/winetests/crypt32/cert.c b/modules/rostests/winetests/crypt32/cert.c index bb0cc525e7..f653741ea7 100644 --- a/modules/rostests/winetests/crypt32/cert.c +++ b/modules/rostests/winetests/crypt32/cert.c @@ -3196,9 +3196,12 @@ static void testComparePublicKeyInfo(void) static BYTE bits1[] = { 1, 0 }; static BYTE bits2[] = { 0 }; static BYTE bits3[] = { 1 }; - static BYTE bits4[] = { 0x30,8, 2,1,0x81, 2,3,1,0,1 }; - static BYTE bits5[] = { 0x30,9, 2,2,0,0x81, 2,3,1,0,1 }; - static BYTE bits6[] = { 0x30,9, 2,2,0,0x82, 2,3,1,0,1 }; + static BYTE bits4[] = { 0x30,8, 2,1,0x81, 2,3,1,0,1 }; /* ASN_SEQUENCE */ + static BYTE bits5[] = { 0x30,9, 2,2,0,0x81, 2,3,1,0,1 }; /* ASN_SEQUENCE */ + static BYTE bits6[] = { 0x30,9, 2,2,0,0x82, 2,3,1,0,1 }; /* ASN_SEQUENCE */ + static BYTE bits7[] = { 0x04,8, 2,1,0x81, 2,3,1,0,1 }; /* ASN_OCTETSTRING */ + static BYTE bits8[] = { 0x04,9, 2,2,0,0x81, 2,3,1,0,1 }; /* ASN_OCTETSTRING */ + static BYTE bits9[] = { 0x04,9, 2,2,0,0x82, 2,3,1,0,1 }; /* ASN_OCTETSTRING */ /* crashes ret = CertComparePublicKeyInfo(0, NULL, NULL); @@ -3206,14 +3209,23 @@ static void testComparePublicKeyInfo(void) /* Empty public keys compare */ ret = CertComparePublicKeyInfo(0, &info1, &info2); ok(ret, "CertComparePublicKeyInfo failed: %08x\n", GetLastError()); + ret = CertComparePublicKeyInfo(X509_ASN_ENCODING, &info1, &info2); + ok(ret, "CertComparePublicKeyInfo failed: %08x\n", GetLastError()); + /* Different OIDs appear to compare */ info1.Algorithm.pszObjId = oid_rsa_rsa; info2.Algorithm.pszObjId = oid_rsa_sha1rsa; ret = CertComparePublicKeyInfo(0, &info1, &info2); ok(ret, "CertComparePublicKeyInfo failed: %08x\n", GetLastError()); + ret = CertComparePublicKeyInfo(X509_ASN_ENCODING, &info1, &info2); + ok(ret, "CertComparePublicKeyInfo failed: %08x\n", GetLastError()); + info2.Algorithm.pszObjId = oid_x957_dsa; ret = CertComparePublicKeyInfo(0, &info1, &info2); ok(ret, "CertComparePublicKeyInfo failed: %08x\n", GetLastError()); + ret = CertComparePublicKeyInfo(X509_ASN_ENCODING, &info1, &info2); + ok(ret, "CertComparePublicKeyInfo failed: %08x\n", GetLastError()); + info1.PublicKey.cbData = sizeof(bits1); info1.PublicKey.pbData = bits1; info1.PublicKey.cUnusedBits = 0; @@ -3222,6 +3234,9 @@ static void testComparePublicKeyInfo(void) info2.PublicKey.cUnusedBits = 0; ret = CertComparePublicKeyInfo(0, &info1, &info2); ok(ret, "CertComparePublicKeyInfo failed: %08x\n", GetLastError()); + ret = CertComparePublicKeyInfo(X509_ASN_ENCODING, &info1, &info2); + ok(ret, "CertComparePublicKeyInfo failed: %08x\n", GetLastError()); + info2.Algorithm.pszObjId = oid_rsa_rsa; info1.PublicKey.cbData = sizeof(bits4); info1.PublicKey.pbData = bits4; @@ -3232,15 +3247,12 @@ static void testComparePublicKeyInfo(void) ret = CertComparePublicKeyInfo(0, &info1, &info2); ok(!ret, "CertComparePublicKeyInfo: as raw binary: keys should be unequal\n"); ret = CertComparePublicKeyInfo(X509_ASN_ENCODING, &info1, &info2); - ok(ret || - broken(!ret), /* win9x */ - "CertComparePublicKeyInfo: as ASN.1 encoded: keys should be equal\n"); + ok(ret, "CertComparePublicKeyInfo: as ASN.1 encoded: keys should be equal\n"); + info1.PublicKey.cUnusedBits = 1; info2.PublicKey.cUnusedBits = 5; ret = CertComparePublicKeyInfo(X509_ASN_ENCODING, &info1, &info2); - ok(ret || - broken(!ret), /* win9x */ - "CertComparePublicKeyInfo: ASN.1 encoding should ignore cUnusedBits\n"); + ok(ret, "CertComparePublicKeyInfo: ASN.1 encoding should ignore cUnusedBits\n"); info1.PublicKey.cUnusedBits = 0; info2.PublicKey.cUnusedBits = 0; info1.PublicKey.cbData--; /* kill one byte, make ASN.1 encoded data invalid */ @@ -3256,11 +3268,36 @@ static void testComparePublicKeyInfo(void) ret = CertComparePublicKeyInfo(0, &info1, &info2); /* Simple (non-comparing) case */ ok(!ret, "Expected keys not to compare\n"); + ret = CertComparePublicKeyInfo(X509_ASN_ENCODING, &info1, &info2); + ok(!ret, "Expected keys not to compare\n"); + info2.PublicKey.cbData = sizeof(bits1); info2.PublicKey.pbData = bits1; info2.PublicKey.cUnusedBits = 0; ret = CertComparePublicKeyInfo(0, &info1, &info2); ok(!ret, "Expected keys not to compare\n"); + ret = CertComparePublicKeyInfo(X509_ASN_ENCODING, &info1, &info2); + ok(!ret, "Expected keys not to compare\n"); + + info1.PublicKey.cbData = sizeof(bits7); + info1.PublicKey.pbData = bits7; + info1.PublicKey.cUnusedBits = 0; + info2.PublicKey.cbData = sizeof(bits8); + info2.PublicKey.pbData = bits8; + info2.PublicKey.cUnusedBits = 0; + ret = CertComparePublicKeyInfo(0, &info1, &info2); + ok(!ret, "CertComparePublicKeyInfo: as raw binary: keys should be unequal\n"); + ret = CertComparePublicKeyInfo(X509_ASN_ENCODING, &info1, &info2); + ok(!ret, "CertComparePublicKeyInfo: as ASN.1 encoded: keys should be unequal\n"); + + ret = CertComparePublicKeyInfo(0, &info1, &info1); + ok(ret, "CertComparePublicKeyInfo: as raw binary: keys should be equal\n"); + ret = CertComparePublicKeyInfo(X509_ASN_ENCODING, &info1, &info1); + ok(ret, "CertComparePublicKeyInfo: as ASN.1 encoded: keys should be equal\n"); + info1.PublicKey.cbData--; /* kill one byte, make ASN.1 encoded data invalid */ + ret = CertComparePublicKeyInfo(X509_ASN_ENCODING, &info1, &info1); + ok(ret, "CertComparePublicKeyInfo: as ASN.1 encoded: keys should be equal\n"); + /* ASN.1 encoded non-comparing case */ info1.PublicKey.cbData = sizeof(bits5); info1.PublicKey.pbData = bits5; @@ -3270,6 +3307,16 @@ static void testComparePublicKeyInfo(void) info2.PublicKey.cUnusedBits = 0; ret = CertComparePublicKeyInfo(X509_ASN_ENCODING, &info1, &info2); ok(!ret, "CertComparePublicKeyInfo: different keys should be unequal\n"); + + /* ASN.1 encoded non-comparing case */ + info1.PublicKey.cbData = sizeof(bits8); + info1.PublicKey.pbData = bits8; + info1.PublicKey.cUnusedBits = 0; + info2.PublicKey.cbData = sizeof(bits9); + info2.PublicKey.pbData = bits9; + info2.PublicKey.cUnusedBits = 0; + ret = CertComparePublicKeyInfo(X509_ASN_ENCODING, &info1, &info2); + ok(!ret, "CertComparePublicKeyInfo: different keys should be unequal\n"); } static void testHashPublicKeyInfo(void) diff --git a/modules/rostests/winetests/crypt32/encode.c b/modules/rostests/winetests/crypt32/encode.c index 4a6626ab00..cea948151c 100644 --- a/modules/rostests/winetests/crypt32/encode.c +++ b/modules/rostests/winetests/crypt32/encode.c @@ -1017,7 +1017,7 @@ static void compareNameValues(const CERT_NAME_VALUE *expected, static void compareRDNAttrs(const CERT_RDN_ATTR *expected, const CERT_RDN_ATTR *got) { - if (expected->pszObjId && strlen(expected->pszObjId)) + if (expected->pszObjId && *expected->pszObjId) { ok(got->pszObjId != NULL, "Expected OID %s, got NULL\n", expected->pszObjId); @@ -8435,7 +8435,7 @@ static void testImportPublicKey(HCRYPTPROV csp, PCERT_PUBLIC_KEY_INFO info) ok(ret, "CryptImportPublicKeyInfoEx failed: %08x\n", GetLastError()); dwSize = sizeof(ai); - CryptGetKeyParam(key, KP_ALGID, (LPVOID)&ai, &dwSize, 0); + ret = CryptGetKeyParam(key, KP_ALGID, (LPVOID)&ai, &dwSize, 0); ok(ret, "CryptGetKeyParam failed: %08x\n", GetLastError()); if(ret) { @@ -8451,7 +8451,7 @@ static void testImportPublicKey(HCRYPTPROV csp, PCERT_PUBLIC_KEY_INFO info) ok(ret, "CryptImportPublicKeyInfoEx failed: %08x\n", GetLastError()); dwSize = sizeof(ai); - CryptGetKeyParam(key, KP_ALGID, (LPVOID)&ai, &dwSize, 0); + ret = CryptGetKeyParam(key, KP_ALGID, (LPVOID)&ai, &dwSize, 0); ok(ret, "CryptGetKeyParam failed: %08x\n", GetLastError()); if(ret) { diff --git a/modules/rostests/winetests/crypt32/main.c b/modules/rostests/winetests/crypt32/main.c index 11beff11c4..6a031527d8 100644 --- a/modules/rostests/winetests/crypt32/main.c +++ b/modules/rostests/winetests/crypt32/main.c @@ -305,8 +305,32 @@ static void test_readTrustedPublisherDWORD(void) static void test_getDefaultCryptProv(void) { +#define ALG(id) id, #id + static const struct + { + ALG_ID algid; + const char *name; + BOOL optional; + } test_prov[] = + { + { ALG(CALG_MD2), TRUE }, + { ALG(CALG_MD4), TRUE }, + { ALG(CALG_MD5), TRUE }, + { ALG(CALG_SHA), TRUE }, + { ALG(CALG_RSA_SIGN) }, + { ALG(CALG_DSS_SIGN) }, + { ALG(CALG_NO_SIGN) }, + { ALG(CALG_ECDSA), TRUE }, + { ALG(CALG_ECDH), TRUE }, + { ALG(CALG_RSA_KEYX) }, + { ALG(CALG_RSA_KEYX) }, + }; +#undef ALG HCRYPTPROV (WINAPI *pI_CryptGetDefaultCryptProv)(DWORD w); HCRYPTPROV prov; + BOOL ret; + DWORD size, i; + LPSTR name; pI_CryptGetDefaultCryptProv = (void *)GetProcAddress(hCrypt, "I_CryptGetDefaultCryptProv"); if (!pI_CryptGetDefaultCryptProv) return; @@ -323,6 +347,34 @@ static void test_getDefaultCryptProv(void) prov = pI_CryptGetDefaultCryptProv(0); ok(prov != 0, "I_CryptGetDefaultCryptProv failed: %08x\n", GetLastError()); CryptReleaseContext(prov, 0); + + for (i = 0; i < ARRAY_SIZE(test_prov); i++) + { + if (winetest_debug > 1) + trace("%u: algid %#x (%s): class %u, type %u, sid %u\n", i, test_prov[i].algid, test_prov[i].name, + GET_ALG_CLASS(test_prov[i].algid) >> 13, GET_ALG_TYPE(test_prov[i].algid) >> 9, GET_ALG_SID(test_prov[i].algid)); + + prov = pI_CryptGetDefaultCryptProv(test_prov[i].algid); + if (!prov) + { +todo_wine_if(test_prov[i].algid == CALG_DSS_SIGN || test_prov[i].algid == CALG_NO_SIGN) + ok(test_prov[i].optional, "%u: I_CryptGetDefaultCryptProv(%#x) failed\n", i, test_prov[i].algid); + continue; + } + + ret = CryptGetProvParam(prov, PP_NAME, NULL, &size, 0); + if (ret) /* some provders don't support PP_NAME */ + { + name = CryptMemAlloc(size); + ret = CryptGetProvParam(prov, PP_NAME, (BYTE *)name, &size, 0); + ok(ret, "%u: CryptGetProvParam failed %#x\n", i, GetLastError()); + if (winetest_debug > 1) + trace("%u: algid %#x, name %s\n", i, test_prov[i].algid, name); + CryptMemFree(name); + } + + CryptReleaseContext(prov, 0); + } } static void test_CryptInstallOssGlobal(void) diff --git a/modules/rostests/winetests/crypt32/oid.c b/modules/rostests/winetests/crypt32/oid.c index d6a03c5d1d..ae03bba90e 100644 --- a/modules/rostests/winetests/crypt32/oid.c +++ b/modules/rostests/winetests/crypt32/oid.c @@ -2,6 +2,7 @@ * Unit test suite for crypt32.dll's OID support functions. * * Copyright 2005 Juan Lang + * Copyright 2018 Dmitry Timoshkov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -614,6 +615,97 @@ static void test_findOIDInfo(void) win_skip("Host does not support ECDSA_SHA256, skipping test\n"); } +static void test_registerOIDInfo(void) +{ + static const WCHAR winetestW[] = { 'w','i','n','e','t','e','s','t',0 }; + static char test_oid[] = "1.2.3.4.5.6.7.8.9.10"; + CRYPT_OID_INFO info1; + const CRYPT_OID_INFO *info2; + HKEY key; + DWORD ret, size, type, value; + char buf[256]; + + SetLastError(0xdeadbeef); + ret = CryptUnregisterOIDInfo(NULL); + ok(!ret, "should fail\n"); + ok(GetLastError() == E_INVALIDARG, "got %#x\n", GetLastError()); + + memset(&info1, 0, sizeof(info1)); + SetLastError(0xdeadbeef); + ret = CryptUnregisterOIDInfo(&info1); + ok(!ret, "should fail\n"); + ok(GetLastError() == E_INVALIDARG, "got %#x\n", GetLastError()); + + info1.cbSize = sizeof(info1); + SetLastError(0xdeadbeef); + ret = CryptUnregisterOIDInfo(&info1); + ok(!ret, "should fail\n"); + ok(GetLastError() == E_INVALIDARG, "got %#x\n", GetLastError()); + + info1.pszOID = test_oid; + SetLastError(0xdeadbeef); + ret = CryptUnregisterOIDInfo(&info1); + ok(!ret, "should fail\n"); + ok(GetLastError() == ERROR_FILE_NOT_FOUND, "got %u\n", GetLastError()); + + info2 = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, (void *)test_oid, 0); + ok(!info2, "should fail\n"); + + SetLastError(0xdeadbeef); + /* While it succeeds, the next call does not write anything to the + * registry on Windows because dwGroupId == 0. + */ + ret = CryptRegisterOIDInfo(&info1, 0); + ok(ret, "got %u\n", GetLastError()); + + ret = RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Cryptography\\OID\\EncodingType 0\\CryptDllFindOIDInfo\\1.2.3.4.5.6.7.8.9.10!1", &key); + ok(ret == ERROR_FILE_NOT_FOUND, "got %u\n", ret); + + info2 = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, (void *)test_oid, 0); + ok(!info2, "should fail\n"); + + info1.pwszName = winetestW; + info1.dwGroupId = CRYPT_HASH_ALG_OID_GROUP_ID; + SetLastError(0xdeadbeef); + ret = CryptRegisterOIDInfo(&info1, CRYPT_INSTALL_OID_INFO_BEFORE_FLAG); + if (!ret && GetLastError() == ERROR_ACCESS_DENIED) + { + skip("Need admin rights\n"); + return; + } + ok(ret, "got %u\n", GetLastError()); + + /* It looks like crypt32 reads the OID info from registry only on load, + * and CryptFindOIDInfo will find the registered OID on next run + */ + info2 = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, (void *)test_oid, 0); + ok(!info2, "should fail\n"); + + ret = RegCreateKeyA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Cryptography\\OID\\EncodingType 0\\CryptDllFindOIDInfo\\1.2.3.4.5.6.7.8.9.10!1", &key); + ok(!ret, "got %u\n", ret); + + memset(buf, 0, sizeof(buf)); + size = sizeof(buf); + ret = RegQueryValueExA(key, "Name", NULL, &type, (BYTE *)buf, &size); + ok(!ret, "got %u\n", ret); + ok(type == REG_SZ, "got %u\n", type); + ok(!strcmp(buf, "winetest"), "got %s\n", buf); + + value = 0xdeadbeef; + size = sizeof(value); + ret = RegQueryValueExA(key, "Flags", NULL, &type, (BYTE *)&value, &size); + ok(!ret, "got %u\n", ret); + ok(type == REG_DWORD, "got %u\n", type); + ok(value == 1, "got %u\n", value); + + RegCloseKey(key); + + CryptUnregisterOIDInfo(&info1); + + ret = RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Cryptography\\OID\\EncodingType 0\\CryptDllFindOIDInfo\\1.2.3.4.5.6.7.8.9.10!1", &key); + ok(ret == ERROR_FILE_NOT_FOUND, "got %u\n", ret); +} + START_TEST(oid) { HMODULE hCrypt32 = GetModuleHandleA("crypt32.dll"); @@ -623,6 +715,7 @@ START_TEST(oid) testAlgIDToOID(); test_enumOIDInfo(); test_findOIDInfo(); + test_registerOIDInfo(); test_oidFunctionSet(); test_installOIDFunctionAddress(); test_registerOIDFunction(); diff --git a/modules/rostests/winetests/crypt32/store.c b/modules/rostests/winetests/crypt32/store.c index 9915482277..d862902c88 100644 --- a/modules/rostests/winetests/crypt32/store.c +++ b/modules/rostests/winetests/crypt32/store.c @@ -541,7 +541,7 @@ static void testStoresInCollection(void) cert2 = CertCreateCertificateContext(X509_ASN_ENCODING, signedBigCert, sizeof(signedBigCert)); ok (cert2 != NULL, "Failed to create cert context %x\n", GetLastError()); ret = CertAddCertificateContextToStore(collection, cert2, CERT_STORE_ADD_REPLACE_EXISTING, NULL); - ok (ret, "Failed to add cert3 to the store %x\n",GetLastError()); + ok (ret, "Failed to add cert2 to the store %x\n",GetLastError()); /** checking certificates in the stores */ tcert1 = CertEnumCertificatesInStore(ro_store, 0); @@ -2834,6 +2834,8 @@ static void testEmptyStore(void) ok(res, "CertDeleteCertificateContextFromStore failed\n"); ok(cert3->hCertStore == cert->hCertStore, "Unexpected hCertStore\n"); + CertFreeCertificateContext(cert3); + store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, CERT_STORE_CREATE_NEW_FLAG, NULL); ok(store != NULL, "CertOpenStore failed\n"); @@ -2847,6 +2849,7 @@ static void testEmptyStore(void) ok(cert3->hCertStore == store, "Unexpected hCertStore\n"); CertCloseStore(store, 0); + CertFreeCertificateContext(cert3); res = CertCloseStore(cert->hCertStore, CERT_CLOSE_STORE_CHECK_FLAG); ok(!res && GetLastError() == E_UNEXPECTED, "CertCloseStore returned: %x(%x)\n", res, GetLastError());
6 years
1
0
0
0
[reactos] 01/01: [CRYPT32] Sync with Wine Staging 4.0. CORE-15682
by Amine Khaldi
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=b1b70ef9edbf71facc1c4…
commit b1b70ef9edbf71facc1c430a6e84a71e01d94559 Author: Amine Khaldi <amine.khaldi(a)reactos.org> AuthorDate: Fri Jan 25 13:19:03 2019 +0100 Commit: Amine Khaldi <amine.khaldi(a)reactos.org> CommitDate: Fri Jan 25 13:19:03 2019 +0100 [CRYPT32] Sync with Wine Staging 4.0. CORE-15682 --- dll/win32/crypt32/cert.c | 100 +++++++----- dll/win32/crypt32/crypt32.spec | 3 +- dll/win32/crypt32/crypt32_private.h | 3 +- dll/win32/crypt32/decode.c | 48 +----- dll/win32/crypt32/encode.c | 125 +++++++++++++-- dll/win32/crypt32/main.c | 75 ++++++++- dll/win32/crypt32/msg.c | 58 ++++--- dll/win32/crypt32/oid.c | 297 +++++++++++++++++++++++++++++++++++- media/doc/README.WINE | 2 +- 9 files changed, 584 insertions(+), 127 deletions(-) diff --git a/dll/win32/crypt32/cert.c b/dll/win32/crypt32/cert.c index 0088f006d3..0065ba69d4 100644 --- a/dll/win32/crypt32/cert.c +++ b/dll/win32/crypt32/cert.c @@ -28,6 +28,7 @@ #include "wine/winternl.h" #define CRYPT_OID_INFO_HAS_EXTRA_FIELDS #include "wincrypt.h" +#include "snmp.h" #include "bcrypt.h" #include "winnls.h" #include "rpc.h" @@ -532,14 +533,19 @@ void CRYPT_FixKeyProvInfoPointers(PCRYPT_KEY_PROV_INFO info) provNameLen = (lstrlenW(info->pwszProvName) + 1) * sizeof(WCHAR); data += provNameLen; - info->rgProvParam = (PCRYPT_KEY_PROV_PARAM)data; - data += info->cProvParam * sizeof(CRYPT_KEY_PROV_PARAM); - - for (i = 0; i < info->cProvParam; i++) + if (info->cProvParam) { - info->rgProvParam[i].pbData = data; - data += info->rgProvParam[i].cbData; + info->rgProvParam = (PCRYPT_KEY_PROV_PARAM)data; + data += info->cProvParam * sizeof(CRYPT_KEY_PROV_PARAM); + + for (i = 0; i < info->cProvParam; i++) + { + info->rgProvParam[i].pbData = data; + data += info->rgProvParam[i].cbData; + } } + else + info->rgProvParam = NULL; } BOOL WINAPI CertGetCertificateContextProperty(PCCERT_CONTEXT pCertContext, @@ -1242,6 +1248,12 @@ BOOL WINAPI CertComparePublicKeyInfo(DWORD dwCertEncodingType, TRACE("(%08x, %p, %p)\n", dwCertEncodingType, pPublicKey1, pPublicKey2); + /* RSA public key data should start with ASN_SEQUENCE, + * otherwise it's not a RSA_CSP_PUBLICKEYBLOB. + */ + if (!pPublicKey1->PublicKey.cbData || pPublicKey1->PublicKey.pbData[0] != ASN_SEQUENCE) + dwCertEncodingType = 0; + switch (GET_CERT_ENCODING_TYPE(dwCertEncodingType)) { case 0: /* Seems to mean "raw binary bits" */ @@ -1267,32 +1279,21 @@ BOOL WINAPI CertComparePublicKeyInfo(DWORD dwCertEncodingType, ret = FALSE; if (CryptDecodeObject(dwCertEncodingType, RSA_CSP_PUBLICKEYBLOB, pPublicKey1->PublicKey.pbData, pPublicKey1->PublicKey.cbData, - 0, NULL, &length)) + CRYPT_DECODE_ALLOC_FLAG, &pblob1, &length)) { - pblob1 = CryptMemAlloc(length); if (CryptDecodeObject(dwCertEncodingType, RSA_CSP_PUBLICKEYBLOB, - pPublicKey1->PublicKey.pbData, pPublicKey1->PublicKey.cbData, - 0, pblob1, &length)) + pPublicKey2->PublicKey.pbData, pPublicKey2->PublicKey.cbData, + CRYPT_DECODE_ALLOC_FLAG, &pblob2, &length)) { - if (CryptDecodeObject(dwCertEncodingType, RSA_CSP_PUBLICKEYBLOB, - pPublicKey2->PublicKey.pbData, pPublicKey2->PublicKey.cbData, - 0, NULL, &length)) - { - pblob2 = CryptMemAlloc(length); - if (CryptDecodeObject(dwCertEncodingType, RSA_CSP_PUBLICKEYBLOB, - pPublicKey2->PublicKey.pbData, pPublicKey2->PublicKey.cbData, - 0, pblob2, &length)) - { - /* The RSAPUBKEY structure directly follows the BLOBHEADER */ - RSAPUBKEY *pk1 = (LPVOID)(pblob1 + 1), - *pk2 = (LPVOID)(pblob2 + 1); - ret = (pk1->bitlen == pk2->bitlen) && (pk1->pubexp == pk2->pubexp) - && !memcmp(pk1 + 1, pk2 + 1, pk1->bitlen/8); - } - CryptMemFree(pblob2); - } + /* The RSAPUBKEY structure directly follows the BLOBHEADER */ + RSAPUBKEY *pk1 = (LPVOID)(pblob1 + 1), + *pk2 = (LPVOID)(pblob2 + 1); + ret = (pk1->bitlen == pk2->bitlen) && (pk1->pubexp == pk2->pubexp) + && !memcmp(pk1 + 1, pk2 + 1, pk1->bitlen/8); + + LocalFree(pblob2); } - CryptMemFree(pblob1); + LocalFree(pblob1); } break; @@ -1321,9 +1322,30 @@ DWORD WINAPI CertGetPublicKeyLength(DWORD dwCertEncodingType, } else { + PCCRYPT_OID_INFO info; DWORD size; PBYTE buf; - BOOL ret = CryptDecodeObjectEx(dwCertEncodingType, + BOOL ret; + + info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, pPublicKey->Algorithm.pszObjId, 0); + if (info) + { + HCRYPTKEY key; + + TRACE("public key algid %#x (%s)\n", info->u.Algid, debugstr_a(pPublicKey->Algorithm.pszObjId)); + + ret = CryptImportPublicKeyInfo(I_CryptGetDefaultCryptProv(info->u.Algid), dwCertEncodingType, pPublicKey, &key); + if (ret) + { + size = sizeof(len); + ret = CryptGetKeyParam(key, KP_KEYLEN, (BYTE *)&len, &size, 0); + CryptDestroyKey(key); + return len; + } + /* fallback to RSA */ + } + + ret = CryptDecodeObjectEx(dwCertEncodingType, RSA_CSP_PUBLICKEYBLOB, pPublicKey->PublicKey.pbData, pPublicKey->PublicKey.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &buf, &size); @@ -1794,7 +1816,7 @@ PCCERT_CONTEXT WINAPI CertFindCertificateInStore(HCERTSTORE hCertStore, } if (find) - ret = find(hCertStore, dwFlags, dwType, pvPara, pPrevCertContext); + ret = find(hCertStore, dwType, dwFlags, pvPara, pPrevCertContext); else if (compare) ret = cert_compare_certs_in_store(hCertStore, pPrevCertContext, compare, dwType, dwFlags, pvPara); @@ -2173,7 +2195,7 @@ BOOL WINAPI CryptHashCertificate(HCRYPTPROV_LEGACY hCryptProv, ALG_ID Algid, pbEncoded, cbEncoded, pbComputedHash, pcbComputedHash); if (!hCryptProv) - hCryptProv = CRYPT_GetDefaultProvider(); + hCryptProv = I_CryptGetDefaultCryptProv(0); if (!Algid) Algid = CALG_SHA1; if (ret) @@ -2202,7 +2224,7 @@ BOOL WINAPI CryptHashPublicKeyInfo(HCRYPTPROV_LEGACY hCryptProv, ALG_ID Algid, dwCertEncodingType, pInfo, pbComputedHash, pcbComputedHash); if (!hCryptProv) - hCryptProv = CRYPT_GetDefaultProvider(); + hCryptProv = I_CryptGetDefaultCryptProv(0); if (!Algid) Algid = CALG_MD5; if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) != X509_ASN_ENCODING) @@ -2254,7 +2276,7 @@ BOOL WINAPI CryptHashToBeSigned(HCRYPTPROV_LEGACY hCryptProv, HCRYPTHASH hHash; if (!hCryptProv) - hCryptProv = CRYPT_GetDefaultProvider(); + hCryptProv = I_CryptGetDefaultCryptProv(0); oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, info->SignatureAlgorithm.pszObjId, 0); if (!oidInfo) @@ -2303,7 +2325,7 @@ BOOL WINAPI CryptSignCertificate(HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCryptProv, if (info->dwGroupId == CRYPT_HASH_ALG_OID_GROUP_ID) { if (!hCryptProv) - hCryptProv = CRYPT_GetDefaultProvider(); + hCryptProv = I_CryptGetDefaultCryptProv(0); ret = CryptCreateHash(hCryptProv, info->u.Algid, 0, 0, &hHash); if (ret) { @@ -2427,7 +2449,7 @@ static BOOL CRYPT_VerifySignature(HCRYPTPROV_LEGACY hCryptProv, DWORD dwCertEnco pubKeyID = hashID; /* Load the default provider if necessary */ if (!hCryptProv) - hCryptProv = CRYPT_GetDefaultProvider(); + hCryptProv = I_CryptGetDefaultCryptProv(0); ret = CryptImportPublicKeyInfoEx(hCryptProv, dwCertEncodingType, pubKeyInfo, pubKeyID, 0, NULL, &key); if (ret) @@ -3685,3 +3707,11 @@ const void * WINAPI CertCreateContext(DWORD dwContextType, DWORD dwEncodingType, return NULL; } } + +BOOL WINAPI CryptSetKeyIdentifierProperty(const CRYPT_HASH_BLOB *pKeyIdentifier, DWORD dwPropId, + DWORD dwFlags, LPCWSTR pwszComputerName, void *pvReserved, const void *pvData) +{ + FIXME("(%p, 0x%x, 0x%x, %s, %p, %p): stub\n", pKeyIdentifier, dwPropId, dwFlags, + debugstr_w(pwszComputerName), pvReserved, pvData); + return FALSE; +} diff --git a/dll/win32/crypt32/crypt32.spec b/dll/win32/crypt32/crypt32.spec index 0bc78a158d..fc32570b52 100644 --- a/dll/win32/crypt32/crypt32.spec +++ b/dll/win32/crypt32/crypt32.spec @@ -180,6 +180,7 @@ @ stdcall CryptSIPRetrieveSubjectGuidForCatalogFile(wstr long ptr) @ stdcall CryptSIPVerifyIndirectData(ptr ptr) @ stub CryptSetAsyncParam +@ stdcall CryptSetKeyIdentifierProperty(ptr long long wstr ptr ptr) @ stdcall CryptSetOIDFunctionValue(long str str wstr long ptr long) @ stub CryptSetProviderU @ stdcall CryptSignAndEncodeCertificate(long long long str ptr ptr ptr ptr ptr) @@ -194,7 +195,7 @@ @ stdcall CryptUnprotectMemory(ptr long long) @ stdcall CryptUnregisterDefaultOIDFunction(long str wstr) @ stdcall CryptUnregisterOIDFunction(long str str) -@ stub CryptUnregisterOIDInfo +@ stdcall CryptUnregisterOIDInfo(ptr) @ stdcall CryptVerifyCertificateSignature(long long ptr long ptr) @ stdcall CryptVerifyCertificateSignatureEx(long long long ptr long ptr long ptr) @ stdcall CryptVerifyDetachedMessageHash(ptr ptr long long ptr ptr ptr ptr) diff --git a/dll/win32/crypt32/crypt32_private.h b/dll/win32/crypt32/crypt32_private.h index e58db989b7..ac70fe04f8 100644 --- a/dll/win32/crypt32/crypt32_private.h +++ b/dll/win32/crypt32/crypt32_private.h @@ -116,6 +116,7 @@ typedef struct _CRYPT_SIGNED_INFO CRYPT_CONTENT_INFO content; DWORD cSignerInfo; PCMSG_CMS_SIGNER_INFO rgSignerInfo; + PDWORD signerKeySpec; } CRYPT_SIGNED_INFO; BOOL CRYPT_AsnEncodeCMSSignedInfo(CRYPT_SIGNED_INFO *, void *pvData, @@ -149,7 +150,7 @@ BOOL WINAPI CRYPT_AsnEncodePubKeyInfoNoNull(DWORD dwCertEncodingType, /* Returns a handle to the default crypto provider; loads it if necessary. * Returns NULL on failure. */ -HCRYPTPROV CRYPT_GetDefaultProvider(void) DECLSPEC_HIDDEN; +HCRYPTPROV WINAPI I_CryptGetDefaultCryptProv(ALG_ID); HINSTANCE hInstance DECLSPEC_HIDDEN; diff --git a/dll/win32/crypt32/decode.c b/dll/win32/crypt32/decode.c index f77393eced..6f51632dce 100644 --- a/dll/win32/crypt32/decode.c +++ b/dll/win32/crypt32/decode.c @@ -215,7 +215,7 @@ static BOOL CRYPT_DecodeEnsureSpace(DWORD dwFlags, if (pDecodePara && pDecodePara->pfnAlloc) *(BYTE **)pvStructInfo = pDecodePara->pfnAlloc(bytesNeeded); else - *(BYTE **)pvStructInfo = LocalAlloc(0, bytesNeeded); + *(BYTE **)pvStructInfo = LocalAlloc(LPTR, bytesNeeded); if (!*(BYTE **)pvStructInfo) ret = FALSE; else @@ -1550,7 +1550,7 @@ static BOOL CRYPT_AsnDecodeNameValueInternal(const BYTE *pbEncoded, case ASN_UTF8STRING: valueType = CERT_RDN_UTF8_STRING; bytesNeeded += MultiByteToWideChar(CP_UTF8, 0, - (LPCSTR)pbEncoded + 1 + lenBytes, dataLen, NULL, 0) * 2; + (LPCSTR)pbEncoded + 1 + lenBytes, dataLen, NULL, 0) * sizeof(WCHAR); break; default: SetLastError(CRYPT_E_ASN1_BADTAG); @@ -6279,48 +6279,8 @@ BOOL WINAPI CryptDecodeObject(DWORD dwCertEncodingType, LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo) { - BOOL ret = FALSE; - CryptDecodeObjectFunc pCryptDecodeObject = NULL; - CryptDecodeObjectExFunc pCryptDecodeObjectEx = NULL; - HCRYPTOIDFUNCADDR hFunc = NULL; - - TRACE_(crypt)("(0x%08x, %s, %p, %d, 0x%08x, %p, %p)\n", dwCertEncodingType, - debugstr_a(lpszStructType), pbEncoded, cbEncoded, dwFlags, - pvStructInfo, pcbStructInfo); - - if (!pvStructInfo && !pcbStructInfo) - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - if (cbEncoded > MAX_ENCODED_LEN) - { - SetLastError(CRYPT_E_ASN1_LARGE); - return FALSE; - } - - if (!(pCryptDecodeObjectEx = CRYPT_GetBuiltinDecoder(dwCertEncodingType, - lpszStructType))) - { - TRACE_(crypt)("OID %s not found or unimplemented, looking for DLL\n", - debugstr_a(lpszStructType)); - pCryptDecodeObject = CRYPT_LoadDecoderFunc(dwCertEncodingType, - lpszStructType, &hFunc); - if (!pCryptDecodeObject) - pCryptDecodeObjectEx = CRYPT_LoadDecoderExFunc(dwCertEncodingType, - lpszStructType, &hFunc); - } - if (pCryptDecodeObject) - ret = pCryptDecodeObject(dwCertEncodingType, lpszStructType, - pbEncoded, cbEncoded, dwFlags, pvStructInfo, pcbStructInfo); - else if (pCryptDecodeObjectEx) - ret = pCryptDecodeObjectEx(dwCertEncodingType, lpszStructType, - pbEncoded, cbEncoded, dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, - pvStructInfo, pcbStructInfo); - if (hFunc) - CryptFreeOIDFunctionAddress(hFunc, 0); - TRACE_(crypt)("returning %d\n", ret); - return ret; + return CryptDecodeObjectEx(dwCertEncodingType, lpszStructType, + pbEncoded, cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo); } BOOL WINAPI CryptDecodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType, diff --git a/dll/win32/crypt32/encode.c b/dll/win32/crypt32/encode.c index 62d2bc91f7..9d052b61c1 100644 --- a/dll/win32/crypt32/encode.c +++ b/dll/win32/crypt32/encode.c @@ -4779,20 +4779,22 @@ BOOL WINAPI CryptExportPublicKeyInfo(HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCryptProv, NULL, 0, NULL, pInfo, pcbInfo); } -static BOOL WINAPI CRYPT_ExportRsaPublicKeyInfoEx(HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCryptProv, +typedef BOOL (WINAPI *EncodePublicKeyAndParametersFunc)(DWORD dwCertEncodingType, + LPSTR pszPublicKeyObjId, BYTE *pbPubKey, DWORD cbPubKey, DWORD dwFlags, void *pvAuxInfo, + BYTE **ppPublicKey, DWORD *pcbPublicKey, BYTE **ppbParams, DWORD *pcbParams); + +static BOOL WINAPI CRYPT_ExportPublicKeyInfoEx(HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCryptProv, DWORD dwKeySpec, DWORD dwCertEncodingType, LPSTR pszPublicKeyObjId, DWORD dwFlags, void *pvAuxInfo, PCERT_PUBLIC_KEY_INFO pInfo, DWORD *pcbInfo) { BOOL ret; HCRYPTKEY key; - static CHAR oid[] = szOID_RSA_RSA; + static CHAR rsa_oid[] = szOID_RSA_RSA; TRACE_(crypt)("(%08lx, %d, %08x, %s, %08x, %p, %p, %d)\n", hCryptProv, dwKeySpec, dwCertEncodingType, debugstr_a(pszPublicKeyObjId), dwFlags, pvAuxInfo, pInfo, pInfo ? *pcbInfo : 0); - if (!pszPublicKeyObjId) - pszPublicKeyObjId = oid; if ((ret = CryptGetUserKey(hCryptProv, dwKeySpec, &key))) { DWORD keySize = 0; @@ -4800,16 +4802,86 @@ static BOOL WINAPI CRYPT_ExportRsaPublicKeyInfoEx(HCRYPTPROV_OR_NCRYPT_KEY_HANDL ret = CryptExportKey(key, 0, PUBLICKEYBLOB, 0, NULL, &keySize); if (ret) { - LPBYTE pubKey = CryptMemAlloc(keySize); + PUBLICKEYSTRUC *pubKey = CryptMemAlloc(keySize); if (pubKey) { - ret = CryptExportKey(key, 0, PUBLICKEYBLOB, 0, pubKey, - &keySize); + ret = CryptExportKey(key, 0, PUBLICKEYBLOB, 0, (BYTE *)pubKey, &keySize); if (ret) { - DWORD encodedLen = 0; + DWORD encodedLen; + + if (!pszPublicKeyObjId) + { + static HCRYPTOIDFUNCSET set; + EncodePublicKeyAndParametersFunc encodeFunc = NULL; + HCRYPTOIDFUNCADDR hFunc = NULL; + + pszPublicKeyObjId = (LPSTR)CertAlgIdToOID(pubKey->aiKeyAlg); + TRACE("public key algid %#x (%s)\n", pubKey->aiKeyAlg, debugstr_a(pszPublicKeyObjId)); + + if (!set) /* FIXME: there is no a public macro */ + set = CryptInitOIDFunctionSet("CryptDllEncodePublicKeyAndParameters", 0); + + CryptGetOIDFunctionAddress(set, dwCertEncodingType, pszPublicKeyObjId, 0, (void **)&encodeFunc, &hFunc); + if (encodeFunc) + { + BYTE *key_data = NULL; + DWORD key_size = 0; + BYTE *params = NULL; + DWORD params_size = 0; + ret = encodeFunc(dwCertEncodingType, pszPublicKeyObjId, (BYTE *)pubKey, keySize, + dwFlags, pvAuxInfo, &key_data, &key_size, ¶ms, ¶ms_size); + if (ret) + { + DWORD oid_size = strlen(pszPublicKeyObjId) + 1; + DWORD size_needed = sizeof(*pInfo) + oid_size + key_size + params_size; + + if (!pInfo) + *pcbInfo = size_needed; + else if (*pcbInfo < size_needed) + { + *pcbInfo = size_needed; + SetLastError(ERROR_MORE_DATA); + ret = FALSE; + } + else + { + *pcbInfo = size_needed; + pInfo->Algorithm.pszObjId = (char *)(pInfo + 1); + lstrcpyA(pInfo->Algorithm.pszObjId, pszPublicKeyObjId); + if (params) + { + pInfo->Algorithm.Parameters.cbData = params_size; + pInfo->Algorithm.Parameters.pbData = (BYTE *)pInfo->Algorithm.pszObjId + oid_size; + memcpy(pInfo->Algorithm.Parameters.pbData, params, params_size); + } + else + { + pInfo->Algorithm.Parameters.cbData = 0; + pInfo->Algorithm.Parameters.pbData = NULL; + } + pInfo->PublicKey.pbData = (BYTE *)pInfo->Algorithm.pszObjId + oid_size + params_size; + pInfo->PublicKey.cbData = key_size; + memcpy(pInfo->PublicKey.pbData, key_data, key_size); + pInfo->PublicKey.cUnusedBits = 0; + } + + CryptMemFree(key_data); + CryptMemFree(params); + } + + CryptMemFree(pubKey); + CryptFreeOIDFunctionAddress(hFunc, 0); + return ret; + } + + /* fallback to RSA */ + pszPublicKeyObjId = rsa_oid; + } + + encodedLen = 0; ret = CryptEncodeObject(dwCertEncodingType, RSA_CSP_PUBLICKEYBLOB, pubKey, NULL, &encodedLen); if (ret) @@ -4887,7 +4959,7 @@ BOOL WINAPI CryptExportPublicKeyInfoEx(HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCryptPro 0, (void **)&exportFunc, &hFunc); } if (!exportFunc) - exportFunc = CRYPT_ExportRsaPublicKeyInfoEx; + exportFunc = CRYPT_ExportPublicKeyInfoEx; ret = exportFunc(hCryptProv, dwKeySpec, dwCertEncodingType, pszPublicKeyObjId, dwFlags, pvAuxInfo, pInfo, pcbInfo); if (hFunc) @@ -4902,21 +4974,48 @@ BOOL WINAPI CryptImportPublicKeyInfo(HCRYPTPROV hCryptProv, 0, 0, NULL, phKey); } -static BOOL WINAPI CRYPT_ImportRsaPublicKeyInfoEx(HCRYPTPROV hCryptProv, +typedef BOOL (WINAPI *ConvertPublicKeyInfoFunc)(DWORD dwCertEncodingType, + PCERT_PUBLIC_KEY_INFO pInfo, ALG_ID aiKeyAlg, DWORD dwFlags, + BYTE **ppbData, DWORD *dwDataLen); + +static BOOL WINAPI CRYPT_ImportPublicKeyInfoEx(HCRYPTPROV hCryptProv, DWORD dwCertEncodingType, PCERT_PUBLIC_KEY_INFO pInfo, ALG_ID aiKeyAlg, DWORD dwFlags, void *pvAuxInfo, HCRYPTKEY *phKey) { + static HCRYPTOIDFUNCSET set = NULL; + ConvertPublicKeyInfoFunc convertFunc = NULL; + HCRYPTOIDFUNCADDR hFunc = NULL; BOOL ret; - DWORD pubKeySize = 0; + DWORD pubKeySize; + LPBYTE pubKey; TRACE_(crypt)("(%08lx, %08x, %p, %08x, %08x, %p, %p)\n", hCryptProv, dwCertEncodingType, pInfo, aiKeyAlg, dwFlags, pvAuxInfo, phKey); + if (!set) + set = CryptInitOIDFunctionSet(CRYPT_OID_CONVERT_PUBLIC_KEY_INFO_FUNC, 0); + CryptGetOIDFunctionAddress(set, dwCertEncodingType, pInfo->Algorithm.pszObjId, + 0, (void **)&convertFunc, &hFunc); + if (convertFunc) + { + pubKey = NULL; + pubKeySize = 0; + ret = convertFunc(dwCertEncodingType, pInfo, aiKeyAlg, dwFlags, &pubKey, &pubKeySize); + if (ret) + { + ret = CryptImportKey(hCryptProv, pubKey, pubKeySize, 0, 0, phKey); + CryptMemFree(pubKey); + } + + CryptFreeOIDFunctionAddress(hFunc, 0); + return ret; + } + ret = CryptDecodeObject(dwCertEncodingType, RSA_CSP_PUBLICKEYBLOB, pInfo->PublicKey.pbData, pInfo->PublicKey.cbData, 0, NULL, &pubKeySize); if (ret) { - LPBYTE pubKey = CryptMemAlloc(pubKeySize); + pubKey = CryptMemAlloc(pubKeySize); if (pubKey) { @@ -4959,7 +5058,7 @@ BOOL WINAPI CryptImportPublicKeyInfoEx(HCRYPTPROV hCryptProv, CryptGetOIDFunctionAddress(set, dwCertEncodingType, pInfo->Algorithm.pszObjId, 0, (void **)&importFunc, &hFunc); if (!importFunc) - importFunc = CRYPT_ImportRsaPublicKeyInfoEx; + importFunc = CRYPT_ImportPublicKeyInfoEx; ret = importFunc(hCryptProv, dwCertEncodingType, pInfo, aiKeyAlg, dwFlags, pvAuxInfo, phKey); if (hFunc) diff --git a/dll/win32/crypt32/main.c b/dll/win32/crypt32/main.c index 79a0ce1fd3..d0643f4107 100644 --- a/dll/win32/crypt32/main.c +++ b/dll/win32/crypt32/main.c @@ -35,6 +35,16 @@ WINE_DEFAULT_DEBUG_CHANNEL(crypt); static HCRYPTPROV hDefProv; HINSTANCE hInstance; +static CRITICAL_SECTION prov_param_cs; +static CRITICAL_SECTION_DEBUG prov_param_cs_debug = +{ + 0, 0, &prov_param_cs, + { &prov_param_cs_debug.ProcessLocksList, + &prov_param_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": prov_param_cs") } +}; +static CRITICAL_SECTION prov_param_cs = { &prov_param_cs_debug, -1, 0, 0, 0, 0 }; + BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, PVOID pvReserved) { switch (fdwReason) @@ -56,7 +66,7 @@ BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, PVOID pvReserved) return TRUE; } -HCRYPTPROV CRYPT_GetDefaultProvider(void) +static HCRYPTPROV CRYPT_GetDefaultProvider(void) { if (!hDefProv) { @@ -174,20 +184,69 @@ BOOL WINAPI I_CryptGetOssGlobal(DWORD x) return FALSE; } -HCRYPTPROV WINAPI I_CryptGetDefaultCryptProv(DWORD reserved) +static BOOL is_supported_algid(HCRYPTPROV prov, ALG_ID algid) +{ + PROV_ENUMALGS prov_algs; + DWORD size = sizeof(prov_algs); + BOOL ret = FALSE; + + /* This enumeration is not thread safe */ + EnterCriticalSection(&prov_param_cs); + if (CryptGetProvParam(prov, PP_ENUMALGS, (BYTE *)&prov_algs, &size, CRYPT_FIRST)) + { + do + { + if (prov_algs.aiAlgid == algid) + { + ret = TRUE; + break; + } + } while (CryptGetProvParam(prov, PP_ENUMALGS, (BYTE *)&prov_algs, &size, CRYPT_NEXT)); + } + LeaveCriticalSection(&prov_param_cs); + return ret; +} + +HCRYPTPROV WINAPI DECLSPEC_HOTPATCH I_CryptGetDefaultCryptProv(ALG_ID algid) { - HCRYPTPROV ret; + HCRYPTPROV prov, defprov; + + TRACE("(%08x)\n", algid); - TRACE("(%08x)\n", reserved); + defprov = CRYPT_GetDefaultProvider(); - if (reserved) + if (algid && !is_supported_algid(defprov, algid)) { + DWORD i = 0, type, size; + + while (CryptEnumProvidersW(i, NULL, 0, &type, NULL, &size)) + { + WCHAR *name = CryptMemAlloc(size); + if (name) + { + if (CryptEnumProvidersW(i, NULL, 0, &type, name, &size)) + { + if (CryptAcquireContextW(&prov, NULL, name, type, CRYPT_VERIFYCONTEXT)) + { + if (is_supported_algid(prov, algid)) + { + CryptMemFree(name); + return prov; + } + CryptReleaseContext(prov, 0); + } + } + CryptMemFree(name); + } + i++; + } + SetLastError(E_INVALIDARG); return 0; } - ret = CRYPT_GetDefaultProvider(); - CryptContextAddRef(ret, NULL, 0); - return ret; + + CryptContextAddRef(defprov, NULL, 0); + return defprov; } BOOL WINAPI I_CryptReadTrustedPublisherDWORDValueFromRegistry(LPCWSTR name, diff --git a/dll/win32/crypt32/msg.c b/dll/win32/crypt32/msg.c index 005fbf2c1f..4132390855 100644 --- a/dll/win32/crypt32/msg.c +++ b/dll/win32/crypt32/msg.c @@ -567,7 +567,12 @@ static HCRYPTMSG CHashEncodeMsg_Open(DWORD dwFlags, const void *pvMsgEncodeInfo, prov = info->hCryptProv; else { - prov = CRYPT_GetDefaultProvider(); + prov = I_CryptGetDefaultCryptProv(algID); + if (!prov) + { + SetLastError(E_INVALIDARG); + return NULL; + } dwFlags &= ~CMSG_CRYPT_RELEASE_CONTEXT_FLAG; } msg = CryptMemAlloc(sizeof(CHashEncodeMsg)); @@ -921,17 +926,25 @@ typedef struct _CSignedMsgData * been constructed. */ static BOOL CSignedMsgData_ConstructSignerHandles(CSignedMsgData *msg_data, - DWORD signerIndex, HCRYPTPROV crypt_prov) + DWORD signerIndex, HCRYPTPROV *crypt_prov, DWORD *flags) { ALG_ID algID; BOOL ret; algID = CertOIDToAlgId( msg_data->info->rgSignerInfo[signerIndex].HashAlgorithm.pszObjId); - ret = CryptCreateHash(crypt_prov, algID, 0, 0, + + if (!*crypt_prov) + { + *crypt_prov = I_CryptGetDefaultCryptProv(algID); + if (!*crypt_prov) return FALSE; + *flags &= ~CMSG_CRYPT_RELEASE_CONTEXT_FLAG; + } + + ret = CryptCreateHash(*crypt_prov, algID, 0, 0, &msg_data->signerHandles->contentHash); if (ret && msg_data->info->rgSignerInfo[signerIndex].AuthAttrs.cAttr > 0) - ret = CryptCreateHash(crypt_prov, algID, 0, 0, + ret = CryptCreateHash(*crypt_prov, algID, 0, 0, &msg_data->signerHandles->authAttrHash); return ret; } @@ -1125,12 +1138,15 @@ static BOOL CSignedMsgData_Sign(CSignedMsgData *msg_data) for (i = 0; ret && i < msg_data->info->cSignerInfo; i++) { HCRYPTHASH hash; + DWORD keySpec = msg_data->info->signerKeySpec[i]; + if (!keySpec) + keySpec = AT_SIGNATURE; if (msg_data->info->rgSignerInfo[i].AuthAttrs.cAttr) hash = msg_data->signerHandles[i].authAttrHash; else hash = msg_data->signerHandles[i].contentHash; - ret = CryptSignHashW(hash, AT_SIGNATURE, NULL, 0, NULL, + ret = CryptSignHashW(hash, keySpec, NULL, 0, NULL, &msg_data->info->rgSignerInfo[i].EncryptedHash.cbData); if (ret) { @@ -1139,7 +1155,7 @@ static BOOL CSignedMsgData_Sign(CSignedMsgData *msg_data) msg_data->info->rgSignerInfo[i].EncryptedHash.cbData); if (msg_data->info->rgSignerInfo[i].EncryptedHash.pbData) { - ret = CryptSignHashW(hash, AT_SIGNATURE, NULL, 0, + ret = CryptSignHashW(hash, keySpec, NULL, 0, msg_data->info->rgSignerInfo[i].EncryptedHash.pbData, &msg_data->info->rgSignerInfo[i].EncryptedHash.cbData); if (ret) @@ -1189,6 +1205,7 @@ static void CSignedEncodeMsg_Close(HCRYPTMSG hCryptMsg) for (i = 0; i < msg->msg_data.info->cSignerInfo; i++) CSignerInfo_Free(&msg->msg_data.info->rgSignerInfo[i]); CSignedMsgData_CloseHandles(&msg->msg_data); + CryptMemFree(msg->msg_data.info->signerKeySpec); CryptMemFree(msg->msg_data.info->rgSignerInfo); CryptMemFree(msg->msg_data.info); } @@ -1411,6 +1428,9 @@ static HCRYPTMSG CSignedEncodeMsg_Open(DWORD dwFlags, msg->msg_data.info->cSignerInfo * sizeof(CMSG_CMS_SIGNER_INFO)); ret = CSignedMsgData_AllocateHandles(&msg->msg_data); + msg->msg_data.info->signerKeySpec = CryptMemAlloc(info->cSigners * sizeof(DWORD)); + if (!msg->msg_data.info->signerKeySpec) + ret = FALSE; for (i = 0; ret && i < msg->msg_data.info->cSignerInfo; i++) { if (info->rgSigners[i].SignerId.dwIdChoice == @@ -1422,11 +1442,13 @@ static HCRYPTMSG CSignedEncodeMsg_Open(DWORD dwFlags, if (ret) { ret = CSignedMsgData_ConstructSignerHandles( - &msg->msg_data, i, info->rgSigners[i].hCryptProv); + &msg->msg_data, i, &info->rgSigners[i].hCryptProv, &dwFlags); if (dwFlags & CMSG_CRYPT_RELEASE_CONTEXT_FLAG) CryptReleaseContext(info->rgSigners[i].hCryptProv, 0); } + msg->msg_data.info->signerKeySpec[i] = + info->rgSigners[i].dwKeySpec; } } else @@ -1956,7 +1978,7 @@ static HCRYPTMSG CEnvelopedEncodeMsg_Open(DWORD dwFlags, prov = info->hCryptProv; else { - prov = CRYPT_GetDefaultProvider(); + prov = I_CryptGetDefaultCryptProv(0); dwFlags &= ~CMSG_CRYPT_RELEASE_CONTEXT_FLAG; } msg = CryptMemAlloc(sizeof(CEnvelopedEncodeMsg)); @@ -2078,7 +2100,7 @@ static void CDecodeMsg_Close(HCRYPTMSG hCryptMsg) { CDecodeMsg *msg = hCryptMsg; - if (msg->base.open_flags & CMSG_CRYPT_RELEASE_CONTEXT_FLAG) + if (msg->crypt_prov && msg->base.open_flags & CMSG_CRYPT_RELEASE_CONTEXT_FLAG) CryptReleaseContext(msg->crypt_prov, 0); switch (msg->type) { @@ -2329,6 +2351,14 @@ static BOOL CDecodeMsg_FinalizeHashedContent(CDecodeMsg *msg, &size); if (ret) algID = CertOIDToAlgId(hashAlgoID->pszObjId); + + if (!msg->crypt_prov) + { + msg->crypt_prov = I_CryptGetDefaultCryptProv(algID); + if (msg->crypt_prov) + msg->base.open_flags &= ~CMSG_CRYPT_RELEASE_CONTEXT_FLAG; + } + ret = CryptCreateHash(msg->crypt_prov, algID, 0, 0, &msg->u.hash); if (ret) { @@ -2375,7 +2405,7 @@ static BOOL CDecodeMsg_FinalizeSignedContent(CDecodeMsg *msg, ret = CSignedMsgData_AllocateHandles(&msg->u.signed_data); for (i = 0; ret && i < msg->u.signed_data.info->cSignerInfo; i++) ret = CSignedMsgData_ConstructSignerHandles(&msg->u.signed_data, i, - msg->crypt_prov); + &msg->crypt_prov, &msg->base.open_flags); if (ret) { CRYPT_DATA_BLOB *content; @@ -3541,13 +3571,7 @@ HCRYPTMSG WINAPI CryptMsgOpenToDecode(DWORD dwMsgEncodingType, DWORD dwFlags, CDecodeMsg_Close, CDecodeMsg_GetParam, CDecodeMsg_Update, CDecodeMsg_Control); msg->type = dwMsgType; - if (hCryptProv) - msg->crypt_prov = hCryptProv; - else - { - msg->crypt_prov = CRYPT_GetDefaultProvider(); - msg->base.open_flags &= ~CMSG_CRYPT_RELEASE_CONTEXT_FLAG; - } + msg->crypt_prov = hCryptProv; memset(&msg->u, 0, sizeof(msg->u)); msg->msg_data.cbData = 0; msg->msg_data.pbData = NULL; diff --git a/dll/win32/crypt32/oid.c b/dll/win32/crypt32/oid.c index c9b0b412cf..773264d399 100644 --- a/dll/win32/crypt32/oid.c +++ b/dll/win32/crypt32/oid.c @@ -1,6 +1,7 @@ /* * Copyright 2002 Mike McCormack for CodeWeavers * Copyright 2005-2006 Juan Lang + * Copyright 2018 Dmitry Timoshkov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,6 +23,9 @@ #include <stdio.h> #include <stdarg.h> +#ifdef __REACTOS__ +#include <stdlib.h> +#endif #define NONAMELESSUNION #include "windef.h" #include "winbase.h" @@ -58,6 +62,7 @@ struct OIDFunctionSet struct OIDFunction { + HMODULE hModule; DWORD encoding; CRYPT_OID_FUNC_ENTRY entry; struct list next; @@ -72,6 +77,13 @@ static const WCHAR DISALLOWED[] = {'D','i','s','a','l','l','o','w','e','d',0}; static const LPCWSTR LocalizedKeys[] = {ROOT,MY,CA,ADDRESSBOOK,TRUSTEDPUBLISHER,DISALLOWED}; static WCHAR LocalizedNames[ARRAY_SIZE(LocalizedKeys)][256]; +static const WCHAR nameW[] = { 'N','a','m','e',0 }; +static const WCHAR algidW[] = { 'A','l','g','i','d',0 }; +static const WCHAR extraW[] = { 'E','x','t','r','a','I','n','f','o',0 }; +static const WCHAR cngalgidW[] = { 'C','N','G','A','l','g','i','d',0 }; +static const WCHAR cngextraalgidW[] = { 'C','N','G','E','x','t','r','a','A','l','g','i','d',0 }; +static const WCHAR flagsW[] = { 'F','l','a','g','s',0 }; + static void free_function_sets(void) { struct OIDFunctionSet *setCursor, *setNext; @@ -242,6 +254,8 @@ BOOL WINAPI CryptInstallOIDFunctionAddress(HMODULE hModule, { struct OIDFunction *func; + TRACE("OID %s, func %p\n", debugstr_a(rgFuncEntry[i].pszOID), rgFuncEntry[i].pvFuncAddr); + if (!IS_INTOID(rgFuncEntry[i].pszOID)) func = CryptMemAlloc(sizeof(struct OIDFunction) + strlen(rgFuncEntry[i].pszOID) + 1); @@ -261,6 +275,7 @@ BOOL WINAPI CryptInstallOIDFunctionAddress(HMODULE hModule, else func->entry.pszOID = rgFuncEntry[i].pszOID; func->entry.pvFuncAddr = rgFuncEntry[i].pvFuncAddr; + func->hModule = hModule; list_add_tail(&set->functions, &func->next); } else @@ -418,6 +433,38 @@ BOOL WINAPI CryptGetOIDFunctionAddress(HCRYPTOIDFUNCSET hFuncSet, return ret; } +static BOOL is_module_registered(HMODULE hModule) +{ + struct OIDFunctionSet *set; + BOOL ret = FALSE; + + EnterCriticalSection(&funcSetCS); + + LIST_FOR_EACH_ENTRY(set, &funcSets, struct OIDFunctionSet, next) + { + struct OIDFunction *function; + + EnterCriticalSection(&set->cs); + + LIST_FOR_EACH_ENTRY(function, &set->functions, struct OIDFunction, next) + { + if (function->hModule == hModule) + { + ret = TRUE; + break; + } + } + + LeaveCriticalSection(&set->cs); + + if (ret) break; + } + + LeaveCriticalSection(&funcSetCS); + + return ret; +} + BOOL WINAPI CryptFreeOIDFunctionAddress(HCRYPTOIDFUNCADDR hFuncAddr, DWORD dwFlags) { @@ -431,9 +478,12 @@ BOOL WINAPI CryptFreeOIDFunctionAddress(HCRYPTOIDFUNCADDR hFuncAddr, { struct FuncAddr *addr = hFuncAddr; - CryptMemFree(addr->dllList); - FreeLibrary(addr->lib); - CryptMemFree(addr); + if (!is_module_registered(addr->lib)) + { + CryptMemFree(addr->dllList); + FreeLibrary(addr->lib); + CryptMemFree(addr); + } } return TRUE; } @@ -652,14 +702,127 @@ error_close_key: return TRUE; } +/*********************************************************************** + * CryptUnregisterOIDInfo (CRYPT32.@) + */ +BOOL WINAPI CryptUnregisterOIDInfo(PCCRYPT_OID_INFO info) +{ + char *key_name; + HKEY root; + DWORD err; + + TRACE("(%p)\n", info); + + if (!info || info->cbSize != sizeof(*info) || !info->pszOID) + { + SetLastError(E_INVALIDARG); + return FALSE; + } + + err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Cryptography\\OID\\EncodingType 0\\CryptDllFindOIDInfo", 0, KEY_ALL_ACCESS, &root); + if (err != ERROR_SUCCESS) + { + SetLastError(err); + return FALSE; + } + + key_name = CryptMemAlloc(strlen(info->pszOID) + 16); + if (key_name) + { + sprintf(key_name, "%s!%u", info->pszOID, info->dwGroupId); + err = RegDeleteKeyA(root, key_name); + } + else + err = ERROR_OUTOFMEMORY; + + CryptMemFree(key_name); + RegCloseKey(root); + + if (err) + SetLastError(err); + + return !err; +} + /*********************************************************************** * CryptRegisterOIDInfo (CRYPT32.@) */ -BOOL WINAPI CryptRegisterOIDInfo(PCCRYPT_OID_INFO pInfo, DWORD dwFlags) +BOOL WINAPI CryptRegisterOIDInfo(PCCRYPT_OID_INFO info, DWORD flags) { - FIXME("(%p, %x): stub\n", pInfo, dwFlags ); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + char *key_name; + HKEY root = 0, key = 0; + DWORD err; + + TRACE("(%p, %x)\n", info, flags ); + + if (!info || info->cbSize != sizeof(*info) || !info->pszOID) + { + SetLastError(E_INVALIDARG); + return FALSE; + } + + if (!info->dwGroupId) return TRUE; + + key_name = CryptMemAlloc(strlen(info->pszOID) + 16); + if (!key_name) + { + err = ERROR_OUTOFMEMORY; + goto done; + } + + err = RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Cryptography\\OID\\EncodingType 0\\CryptDllFindOIDInfo", + 0, NULL, 0, KEY_ALL_ACCESS, NULL, &root, NULL); + if (err != ERROR_SUCCESS) goto done; + + sprintf(key_name, "%s!%u", info->pszOID, info->dwGroupId); + err = RegCreateKeyA(root, key_name, &key); + if (err != ERROR_SUCCESS) goto done; + + if (flags) + { + err = RegSetValueExW(key, flagsW, 0, REG_DWORD, (const BYTE *)&flags, sizeof(flags)); + if (err != ERROR_SUCCESS) goto done; + } + + if (info->pwszName) + { + err = RegSetValueExW(key, nameW, 0, REG_SZ, (const BYTE *)info->pwszName, (lstrlenW(info->pwszName) + 1) * sizeof(WCHAR)); + if (err != ERROR_SUCCESS) goto done; + } + + if (info->u.Algid) + { + err = RegSetValueExW(key, algidW, 0, REG_DWORD, (const BYTE *)&info->u.Algid, sizeof(info->u.Algid)); + if (err != ERROR_SUCCESS) goto done; + } + + if (info->ExtraInfo.cbData && info->ExtraInfo.pbData) + { + err = RegSetValueExW(key, extraW, 0, REG_BINARY, info->ExtraInfo.pbData, info->ExtraInfo.cbData); + if (err != ERROR_SUCCESS) goto done; + } + + if (info->pwszCNGAlgid) + { + err = RegSetValueExW(key, cngalgidW, 0, REG_SZ, (const BYTE *)info->pwszCNGAlgid, (lstrlenW(info->pwszCNGAlgid) + 1) * sizeof(WCHAR)); + if (err != ERROR_SUCCESS) goto done; + } + + if (info->pwszCNGExtraAlgid) + { + err = RegSetValueExW(key, cngextraalgidW, 0, REG_SZ, (const BYTE *)info->pwszCNGExtraAlgid, (lstrlenW(info->pwszCNGExtraAlgid) + 1) * sizeof(WCHAR)); + if (err != ERROR_SUCCESS) goto done; + } + +done: + CryptMemFree(key_name); + if (key) RegCloseKey(key); + if (root) RegCloseKey(root); + + if (err) + SetLastError(err); + + return !err; } /*********************************************************************** @@ -1405,6 +1568,125 @@ struct OIDInfo { struct list entry; }; +static struct OIDInfo *read_oid_info(HKEY root, char *key_name, DWORD *flags) +{ + HKEY key; + DWORD len, oid_len, name_len = 0, extra_len = 0, cngalgid_len = 0, cngextra_len = 0, group_id = 0; + struct OIDInfo *info; + char *p; + + if (RegOpenKeyExA(root, key_name, 0, KEY_READ, &key)) + return NULL; + + p = strchr(key_name, '!'); + if (p) + { + group_id = strtol(p + 1, NULL, 10); + *p = 0; + } + + oid_len = strlen(key_name) + 1; + + RegQueryValueExW(key, nameW, NULL, NULL, NULL, &name_len); + RegQueryValueExW(key, extraW, NULL, NULL, NULL, &extra_len); + RegQueryValueExW(key, cngalgidW, NULL, NULL, NULL, &cngalgid_len); + RegQueryValueExW(key, cngextraalgidW, NULL, NULL, NULL, &cngextra_len); + + info = CryptMemAlloc(sizeof(*info) + oid_len + name_len + extra_len + cngalgid_len + cngextra_len); + if (info) + { + *flags = 0; + len = sizeof(*flags); + RegQueryValueExW(key, flagsW, NULL, NULL, (BYTE *)flags, &len); + + memset(info, 0, sizeof(*info)); + info->info.cbSize = sizeof(info->info); + + p = (char *)(info + 1); + + info->info.pszOID = p; + strcpy((char *)info->info.pszOID, key_name); + p += oid_len; + + if (name_len) + { + info->info.pwszName = (WCHAR *)p; + RegQueryValueExW(key, nameW, NULL, NULL, (BYTE *)info->info.pwszName, &name_len); + p += name_len; + } + + info->info.dwGroupId = group_id; + + len = sizeof(info->info.u.Algid); + RegQueryValueExW(key, algidW, NULL, NULL, (BYTE *)&info->info.u.Algid, &len); + + if (extra_len) + { + info->info.ExtraInfo.cbData = extra_len; + info->info.ExtraInfo.pbData = (BYTE *)p; + RegQueryValueExW(key, extraW, NULL, NULL, info->info.ExtraInfo.pbData, &extra_len); + p += extra_len; + } + + if (cngalgid_len) + { + info->info.pwszCNGAlgid = (WCHAR *)p; + RegQueryValueExW(key, cngalgidW, NULL, NULL, (BYTE *)info->info.pwszCNGAlgid, &cngalgid_len); + p += cngalgid_len; + } + + if (cngextra_len) + { + info->info.pwszCNGExtraAlgid = (WCHAR *)p; + RegQueryValueExW(key, cngextraalgidW, NULL, NULL, (BYTE *)info->info.pwszCNGExtraAlgid, &cngalgid_len); + } + } + + RegCloseKey(key); + + return info; +} + +static void init_registered_oid_info(void) +{ + DWORD err, idx; + HKEY root; + + err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Cryptography\\OID\\EncodingType 0\\CryptDllFindOIDInfo", + 0, KEY_ALL_ACCESS, &root); + if (err != ERROR_SUCCESS) return; + + idx = 0; + for (;;) + { + char key_name[MAX_PATH]; + struct OIDInfo *info; + DWORD flags; + + err = RegEnumKeyA(root, idx++, key_name, MAX_PATH); + if (err == ERROR_NO_MORE_ITEMS) + break; + + if (err == ERROR_SUCCESS) + { + if ((info = read_oid_info(root, key_name, &flags))) + { + TRACE("adding oid %s, name %s, groupid %u, algid %u, extra %u, CNG algid %s, CNG extra %s\n", + debugstr_a(info->info.pszOID), debugstr_w(info->info.pwszName), + info->info.dwGroupId, info->info.u.Algid, info->info.ExtraInfo.cbData, + debugstr_w(info->info.pwszCNGAlgid), debugstr_w(info->info.pwszCNGExtraAlgid)); + + if (flags & CRYPT_INSTALL_OID_INFO_BEFORE_FLAG) + list_add_head(&oidInfo, &info->entry); + else + list_add_tail(&oidInfo, &info->entry); + } + } + } + + RegCloseKey(root); +} + static void init_oid_info(void) { DWORD i; @@ -1632,6 +1914,7 @@ DWORD WINAPI CertOIDToAlgId(LPCSTR pszObjId) void crypt_oid_init(void) { init_oid_info(); + init_registered_oid_info(); } void crypt_oid_free(void) diff --git a/media/doc/README.WINE b/media/doc/README.WINE index 532d520a7e..cdf4cda9c6 100644 --- a/media/doc/README.WINE +++ b/media/doc/README.WINE @@ -58,7 +58,7 @@ reactos/dll/win32/comctl32 # Synced to Wine-3.0 reactos/dll/win32/comdlg32 # Synced to WineStaging-4.0 reactos/dll/win32/compstui # Synced to WineStaging-3.3 reactos/dll/win32/credui # Synced to WineStaging-4.0 -reactos/dll/win32/crypt32 # Synced to WineStaging-3.17 +reactos/dll/win32/crypt32 # Synced to WineStaging-4.0 reactos/dll/win32/cryptdlg # Synced to WineStaging-3.3 reactos/dll/win32/cryptdll # Synced to WineStaging-3.3 reactos/dll/win32/cryptnet # Synced to WineStaging-3.3
6 years
1
0
0
0
[reactos] 01/01: [PSDK] Update wincrypt.h. CORE-15682
by Amine Khaldi
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=9755a82226954c50d5c92…
commit 9755a82226954c50d5c92b53892e86c88f6f54ba Author: Amine Khaldi <amine.khaldi(a)reactos.org> AuthorDate: Fri Jan 25 13:18:23 2019 +0100 Commit: Amine Khaldi <amine.khaldi(a)reactos.org> CommitDate: Fri Jan 25 13:18:23 2019 +0100 [PSDK] Update wincrypt.h. CORE-15682 --- sdk/include/psdk/wincrypt.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/include/psdk/wincrypt.h b/sdk/include/psdk/wincrypt.h index 789a7b0f83..f0c2f5f253 100644 --- a/sdk/include/psdk/wincrypt.h +++ b/sdk/include/psdk/wincrypt.h @@ -2505,6 +2505,7 @@ static const WCHAR CERT_TRUST_PUB_AUTHENTICODE_FLAGS_VALUE_NAME[] = #define CRYPT_OID_IMPORT_PRIVATE_KEY_INFO_FUNC "CryptDllImportPrivateKeyInfoEx" #define CRYPT_OID_VERIFY_CERTIFICATE_CHAIN_POLICY_FUNC \ "CertDllVerifyCertificateChainPolicy" +#define CRYPT_OID_CONVERT_PUBLIC_KEY_INFO_FUNC "CryptDllConvertPublicKeyInfo" #define URL_OID_GET_OBJECT_URL_FUNC "UrlDllGetObjectUrl" #define TIME_VALID_OID_GET_OBJECT_FUNC "TimeValidDllGetObject" #define CMSG_OID_GEN_CONTENT_ENCRYPT_KEY_FUNC "CryptMsgDllGenContentEncryptKey" @@ -2534,6 +2535,7 @@ static const WCHAR CRYPT_OID_REG_FLAGS_VALUE_NAME[] = #define CRYPT_DEFAULT_OID "DEFAULT" #define CRYPT_INSTALL_OID_FUNC_BEFORE_FLAG 1 +#define CRYPT_INSTALL_OID_INFO_BEFORE_FLAG 1 #define CRYPT_GET_INSTALLED_OID_FUNC_FLAG 0x1
6 years
1
0
0
0
[reactos] 01/01: [CREDUI] Sync with Wine Staging 4.0. CORE-15682
by Amine Khaldi
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=876711581630c42f9f696…
commit 876711581630c42f9f6962cedd2d79c7fa1f7546 Author: Amine Khaldi <amine.khaldi(a)reactos.org> AuthorDate: Fri Jan 25 13:17:45 2019 +0100 Commit: Amine Khaldi <amine.khaldi(a)reactos.org> CommitDate: Fri Jan 25 13:17:45 2019 +0100 [CREDUI] Sync with Wine Staging 4.0. CORE-15682 --- dll/win32/credui/credui_main.c | 23 +++++++++++++++-------- media/doc/README.WINE | 2 +- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/dll/win32/credui/credui_main.c b/dll/win32/credui/credui_main.c index bf9f7d186e..99aa9a12dd 100644 --- a/dll/win32/credui/credui_main.c +++ b/dll/win32/credui/credui_main.c @@ -89,7 +89,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) HeapFree(GetProcessHeap(), 0, entry->pszTargetName); HeapFree(GetProcessHeap(), 0, entry->pszUsername); - ZeroMemory(entry->pszPassword, (strlenW(entry->pszPassword) + 1) * sizeof(WCHAR)); + SecureZeroMemory(entry->pszPassword, strlenW(entry->pszPassword) * sizeof(WCHAR)); HeapFree(GetProcessHeap(), 0, entry->pszPassword); HeapFree(GetProcessHeap(), 0, entry); } @@ -160,20 +160,27 @@ static void CredDialogFillUsernameCombo(HWND hwndUsername, const struct cred_dia DWORD j; BOOL duplicate = FALSE; + if (!credentials[i]->UserName) + continue; + if (params->dwFlags & CREDUI_FLAGS_GENERIC_CREDENTIALS) { - if ((credentials[i]->Type != CRED_TYPE_GENERIC) || !credentials[i]->UserName) + if (credentials[i]->Type != CRED_TYPE_GENERIC) + { + credentials[i]->UserName = NULL; continue; + } } - else + else if (credentials[i]->Type == CRED_TYPE_GENERIC) { - if (credentials[i]->Type == CRED_TYPE_GENERIC) - continue; + credentials[i]->UserName = NULL; + continue; } /* don't add another item with the same name if we've already added it */ for (j = 0; j < i; j++) - if (!strcmpW(credentials[i]->UserName, credentials[j]->UserName)) + if (credentials[j]->UserName + && !strcmpW(credentials[i]->UserName, credentials[j]->UserName)) { duplicate = TRUE; break; @@ -673,7 +680,7 @@ DWORD WINAPI CredUIPromptForCredentialsW(PCREDUI_INFOW pUIInfo, { found = TRUE; HeapFree(GetProcessHeap(), 0, entry->pszUsername); - ZeroMemory(entry->pszPassword, (strlenW(entry->pszPassword) + 1) * sizeof(WCHAR)); + SecureZeroMemory(entry->pszPassword, strlenW(entry->pszPassword) * sizeof(WCHAR)); HeapFree(GetProcessHeap(), 0, entry->pszPassword); } @@ -733,7 +740,7 @@ DWORD WINAPI CredUIConfirmCredentialsW(PCWSTR pszTargetName, BOOL bConfirm) HeapFree(GetProcessHeap(), 0, entry->pszTargetName); HeapFree(GetProcessHeap(), 0, entry->pszUsername); - ZeroMemory(entry->pszPassword, (strlenW(entry->pszPassword) + 1) * sizeof(WCHAR)); + SecureZeroMemory(entry->pszPassword, strlenW(entry->pszPassword) * sizeof(WCHAR)); HeapFree(GetProcessHeap(), 0, entry->pszPassword); HeapFree(GetProcessHeap(), 0, entry); diff --git a/media/doc/README.WINE b/media/doc/README.WINE index 577d4748c2..532d520a7e 100644 --- a/media/doc/README.WINE +++ b/media/doc/README.WINE @@ -57,7 +57,7 @@ reactos/dll/win32/comcat # Synced to WineStaging-3.3 reactos/dll/win32/comctl32 # Synced to Wine-3.0 reactos/dll/win32/comdlg32 # Synced to WineStaging-4.0 reactos/dll/win32/compstui # Synced to WineStaging-3.3 -reactos/dll/win32/credui # Synced to WineStaging-3.17 +reactos/dll/win32/credui # Synced to WineStaging-4.0 reactos/dll/win32/crypt32 # Synced to WineStaging-3.17 reactos/dll/win32/cryptdlg # Synced to WineStaging-3.3 reactos/dll/win32/cryptdll # Synced to WineStaging-3.3
6 years
1
0
0
0
[reactos] 01/01: [COMDLG32] Sync with Wine Staging 4.0. CORE-15682
by Amine Khaldi
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=90f41ebb2898e72e5d4c1…
commit 90f41ebb2898e72e5d4c142d7ccfb5cade8af641 Author: Amine Khaldi <amine.khaldi(a)reactos.org> AuthorDate: Fri Jan 25 13:17:07 2019 +0100 Commit: Amine Khaldi <amine.khaldi(a)reactos.org> CommitDate: Fri Jan 25 13:17:07 2019 +0100 [COMDLG32] Sync with Wine Staging 4.0. CORE-15682 --- dll/win32/comdlg32/filedlg.c | 2 +- dll/win32/comdlg32/itemdlg.c | 2 +- dll/win32/comdlg32/printdlg.c | 6 +++--- media/doc/README.WINE | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dll/win32/comdlg32/filedlg.c b/dll/win32/comdlg32/filedlg.c index 33cbd5e436..e3d6c284e2 100644 --- a/dll/win32/comdlg32/filedlg.c +++ b/dll/win32/comdlg32/filedlg.c @@ -661,7 +661,7 @@ void COMDLG32_GetCanonicalPath(PCIDLIST_ABSOLUTE pidlAbsCurrent, } PathAddBackslashW(lpstrPathAndFile); - TRACE("current directory=%s\n", debugstr_w(lpstrPathAndFile)); + TRACE("current directory=%s, file=%s\n", debugstr_w(lpstrPathAndFile), debugstr_w(lpstrFile)); /* if the user specified a fully qualified path use it */ if(PathIsRelativeW(lpstrFile)) diff --git a/dll/win32/comdlg32/itemdlg.c b/dll/win32/comdlg32/itemdlg.c index f306aaf8d8..09f5be5d50 100644 --- a/dll/win32/comdlg32/itemdlg.c +++ b/dll/win32/comdlg32/itemdlg.c @@ -426,7 +426,7 @@ static void fill_filename_from_selection(FileDialogImpl *This) (!(This->options & FOS_PICKFOLDERS) && (attr & SFGAO_FOLDER)))) continue; - hr = IShellItem_GetDisplayName(psi, SIGDN_PARENTRELATIVEPARSING, &names[valid_count]); + hr = IShellItem_GetDisplayName(psi, (This->options & FOS_PICKFOLDERS) ? SIGDN_FILESYSPATH : SIGDN_PARENTRELATIVEPARSING, &names[valid_count]); if(SUCCEEDED(hr)) { len_total += lstrlenW(names[valid_count]) + 3; diff --git a/dll/win32/comdlg32/printdlg.c b/dll/win32/comdlg32/printdlg.c index 429903b84d..69e9aeb08d 100644 --- a/dll/win32/comdlg32/printdlg.c +++ b/dll/win32/comdlg32/printdlg.c @@ -2618,9 +2618,9 @@ static WCHAR get_decimal_sep(void) static void size2str(const pagesetup_data *data, DWORD size, LPWSTR strout) { - WCHAR integer_fmt[] = {'%','d',0}; - WCHAR hundredths_fmt[] = {'%','d','%','c','%','0','2','d',0}; - WCHAR thousandths_fmt[] = {'%','d','%','c','%','0','3','d',0}; + static const WCHAR integer_fmt[] = {'%','d',0}; + static const WCHAR hundredths_fmt[] = {'%','d','%','c','%','0','2','d',0}; + static const WCHAR thousandths_fmt[] = {'%','d','%','c','%','0','3','d',0}; /* FIXME use LOCALE_SDECIMAL when the edit parsing code can cope */ diff --git a/media/doc/README.WINE b/media/doc/README.WINE index e17ef7cf84..577d4748c2 100644 --- a/media/doc/README.WINE +++ b/media/doc/README.WINE @@ -55,7 +55,7 @@ reactos/dll/win32/cabinet # Synced to WineStaging-3.3 reactos/dll/win32/clusapi # Synced to WineStaging-3.3 reactos/dll/win32/comcat # Synced to WineStaging-3.3 reactos/dll/win32/comctl32 # Synced to Wine-3.0 -reactos/dll/win32/comdlg32 # Synced to WineStaging-3.17 +reactos/dll/win32/comdlg32 # Synced to WineStaging-4.0 reactos/dll/win32/compstui # Synced to WineStaging-3.3 reactos/dll/win32/credui # Synced to WineStaging-3.17 reactos/dll/win32/crypt32 # Synced to WineStaging-3.17
6 years
1
0
0
0
[reactos] 01/01: [COMCTL32_WINETEST] Sync with Wine Staging 4.0. CORE-15682
by Amine Khaldi
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=5f7243b5772eb2341e312…
commit 5f7243b5772eb2341e31263f1a1a5934fea6568b Author: Amine Khaldi <amine.khaldi(a)reactos.org> AuthorDate: Fri Jan 25 13:16:18 2019 +0100 Commit: Amine Khaldi <amine.khaldi(a)reactos.org> CommitDate: Fri Jan 25 13:16:18 2019 +0100 [COMCTL32_WINETEST] Sync with Wine Staging 4.0. CORE-15682 --- modules/rostests/winetests/comctl32/button.c | 913 +++++++++++++++++++- modules/rostests/winetests/comctl32/combo.c | 11 +- modules/rostests/winetests/comctl32/datetime.c | 41 + modules/rostests/winetests/comctl32/edit.c | 84 +- modules/rostests/winetests/comctl32/header.c | 4 +- modules/rostests/winetests/comctl32/imagelist.c | 15 +- modules/rostests/winetests/comctl32/ipaddress.c | 4 +- modules/rostests/winetests/comctl32/listbox.c | 713 ++++++++++++--- modules/rostests/winetests/comctl32/listview.c | 184 +++- modules/rostests/winetests/comctl32/misc.c | 70 +- modules/rostests/winetests/comctl32/monthcal.c | 13 +- modules/rostests/winetests/comctl32/mru.c | 4 +- modules/rostests/winetests/comctl32/pager.c | 1005 +++++++++++++++++++++- modules/rostests/winetests/comctl32/progress.c | 2 +- modules/rostests/winetests/comctl32/propsheet.c | 42 +- modules/rostests/winetests/comctl32/rebar.c | 4 +- modules/rostests/winetests/comctl32/resources.h | 1 + modules/rostests/winetests/comctl32/rsrc.rc | 7 + modules/rostests/winetests/comctl32/status.c | 128 ++- modules/rostests/winetests/comctl32/subclass.c | 31 +- modules/rostests/winetests/comctl32/taskdialog.c | 649 +++++++++++++- modules/rostests/winetests/comctl32/toolbar.c | 19 +- modules/rostests/winetests/comctl32/tooltips.c | 4 +- modules/rostests/winetests/comctl32/trackbar.c | 52 ++ modules/rostests/winetests/comctl32/treeview.c | 143 ++- modules/rostests/winetests/comctl32/updown.c | 12 +- 26 files changed, 3894 insertions(+), 261 deletions(-) diff --git a/modules/rostests/winetests/comctl32/button.c b/modules/rostests/winetests/comctl32/button.c index d73fc1da9b..edfa447bab 100644 --- a/modules/rostests/winetests/comctl32/button.c +++ b/modules/rostests/winetests/comctl32/button.c @@ -20,7 +20,9 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#ifdef __REACTOS__ #undef USE_WINE_TODOS +#endif #include <windows.h> #include <commctrl.h> @@ -29,11 +31,18 @@ #include "v6util.h" #include "msg.h" +#ifdef __REACTOS__ +#define BS_PUSHBOX 0x0000000AL +#endif + #define IS_WNDPROC_HANDLE(x) (((ULONG_PTR)(x) >> 16) == (~0u >> 16)) static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR); static BOOL (WINAPI *pRemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR); static LRESULT (WINAPI *pDefSubclassProc)(HWND, UINT, WPARAM, LPARAM); +static HIMAGELIST (WINAPI *pImageList_Create)(int, int, UINT, int, int); +static int (WINAPI *pImageList_Add)(HIMAGELIST, HBITMAP, HBITMAP); +static BOOL (WINAPI *pImageList_Destroy)(HIMAGELIST); /****************** button message test *************************/ #define ID_BUTTON 0x000e @@ -78,6 +87,12 @@ static void init_functions(void) MAKEFUNC_ORD(RemoveWindowSubclass, 412); MAKEFUNC_ORD(DefSubclassProc, 413); #undef MAKEFUNC_ORD + +#define X(f) p##f = (void *)GetProcAddress(hmod, #f); + X(ImageList_Create); + X(ImageList_Add); + X(ImageList_Destroy); +#undef X } /* try to make sure pending X events have been processed before continuing */ @@ -550,15 +565,15 @@ static void test_button_messages(void) hfont2 = CreateFontIndirectA(&logfont); ok(hfont2 != NULL, "Failed to create Tahoma font\n"); - for (i = 0; i < sizeof(button)/sizeof(button[0]); i++) + for (i = 0; i < ARRAY_SIZE(button); i++) { HFONT prevfont, hfont; MSG msg; DWORD style, state; HDC hdc; - trace("%d: button test sequence\n", i); hwnd = create_button(button[i].style, parent); + ok(hwnd != NULL, "Failed to create a button.\n"); style = GetWindowLongA(hwnd, GWL_STYLE); style &= ~(WS_CHILD | BS_NOTIFY); @@ -807,6 +822,395 @@ static void test_button_class(void) DestroyWindow(hwnd); } +static void test_note(void) +{ + HWND hwnd; + BOOL ret; + WCHAR test_w[] = {'t', 'e', 's', 't', 0}; + WCHAR tes_w[] = {'t', 'e', 's', 0}; + WCHAR deadbeef_w[] = {'d', 'e', 'a', 'd', 'b', 'e', 'e', 'f', 0}; + WCHAR buffer_w[10]; + DWORD size; + DWORD error; + INT type; + + hwnd = create_button(BS_COMMANDLINK, NULL); + ok(hwnd != NULL, "Expect hwnd not null\n"); + SetLastError(0xdeadbeef); + size = ARRAY_SIZE(buffer_w); + ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w); + error = GetLastError(); + if (!ret && error == 0xdeadbeef) + { + win_skip("BCM_GETNOTE message is unavailable. Skipping note tests\n"); /* xp or 2003 */ + DestroyWindow(hwnd); + return; + } + DestroyWindow(hwnd); + + for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++) + { + if (type == BS_DEFCOMMANDLINK || type == BS_COMMANDLINK) + { + hwnd = create_button(type, NULL); + ok(hwnd != NULL, "Expect hwnd not null\n"); + + /* Get note when note hasn't been not set yet */ + SetLastError(0xdeadbeef); + lstrcpyW(buffer_w, deadbeef_w); + size = ARRAY_SIZE(buffer_w); + ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w); + error = GetLastError(); + ok(!ret, "Expect BCM_GETNOTE return false\n"); + ok(!lstrcmpW(buffer_w, deadbeef_w), "Expect note: %s, got: %s\n", + wine_dbgstr_w(deadbeef_w), wine_dbgstr_w(buffer_w)); + ok(size == ARRAY_SIZE(buffer_w), "Got: %d\n", size); + ok(error == ERROR_INVALID_PARAMETER, "Expect last error: 0x%08x, got: 0x%08x\n", + ERROR_INVALID_PARAMETER, error); + + /* Get note length when note is not set */ + ret = SendMessageA(hwnd, BCM_GETNOTELENGTH, 0, 0); + ok(ret == 0, "Expect note length: %d, got: %d\n", 0, ret); + + /* Successful set note, get note and get note length */ + SetLastError(0xdeadbeef); + ret = SendMessageA(hwnd, BCM_SETNOTE, 0, (LPARAM)test_w); + ok(ret, "Expect BCM_SETNOTE return true\n"); + error = GetLastError(); + ok(error == NO_ERROR, "Expect last error: 0x%08x, got: 0x%08x\n", NO_ERROR, error); + + SetLastError(0xdeadbeef); + lstrcpyW(buffer_w, deadbeef_w); + size = ARRAY_SIZE(buffer_w); + ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w); + ok(ret, "Expect BCM_GETNOTE return true\n"); + ok(!lstrcmpW(buffer_w, test_w), "Expect note: %s, got: %s\n", wine_dbgstr_w(test_w), + wine_dbgstr_w(buffer_w)); + ok(size == ARRAY_SIZE(buffer_w), "Got: %d\n", size); + error = GetLastError(); + ok(error == NO_ERROR, "Expect last error: 0x%08x, got: 0x%08x\n", NO_ERROR, error); + + ret = SendMessageA(hwnd, BCM_GETNOTELENGTH, 0, 0); + ok(ret == ARRAY_SIZE(test_w) - 1, "Got: %d\n", ret); + + /* Insufficient buffer, return partial string */ + SetLastError(0xdeadbeef); + lstrcpyW(buffer_w, deadbeef_w); + size = ARRAY_SIZE(test_w) - 1; + ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w); + ok(!ret, "Expect BCM_GETNOTE return false\n"); + ok(!lstrcmpW(buffer_w, tes_w), "Expect note: %s, got: %s\n", wine_dbgstr_w(tes_w), + wine_dbgstr_w(buffer_w)); + ok(size == ARRAY_SIZE(test_w), "Got: %d\n", size); + error = GetLastError(); + ok(error == ERROR_INSUFFICIENT_BUFFER, "Expect last error: 0x%08x, got: 0x%08x\n", + ERROR_INSUFFICIENT_BUFFER, error); + + /* Set note with NULL buffer */ + SetLastError(0xdeadbeef); + ret = SendMessageA(hwnd, BCM_SETNOTE, 0, 0); + ok(ret, "Expect BCM_SETNOTE return false\n"); + error = GetLastError(); + ok(error == NO_ERROR, "Expect last error: 0x%08x, got: 0x%08x\n", NO_ERROR, error); + + /* Check that set note with NULL buffer make note empty */ + SetLastError(0xdeadbeef); + lstrcpyW(buffer_w, deadbeef_w); + size = ARRAY_SIZE(buffer_w); + ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w); + ok(ret, "Expect BCM_GETNOTE return true\n"); + ok(lstrlenW(buffer_w) == 0, "Expect note length 0\n"); + ok(size == ARRAY_SIZE(buffer_w), "Got: %d\n", size); + error = GetLastError(); + ok(error == NO_ERROR, "Expect last error: 0x%08x, got: 0x%08x\n", NO_ERROR, error); + ret = SendMessageA(hwnd, BCM_GETNOTELENGTH, 0, 0); + ok(ret == 0, "Expect note length: %d, got: %d\n", 0, ret); + + /* Get note with NULL buffer */ + SetLastError(0xdeadbeef); + size = ARRAY_SIZE(buffer_w); + ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, 0); + ok(!ret, "Expect BCM_SETNOTE return false\n"); + ok(size == ARRAY_SIZE(buffer_w), "Got: %d\n", size); + error = GetLastError(); + ok(error == ERROR_INVALID_PARAMETER, "Expect last error: 0x%08x, got: 0x%08x\n", + ERROR_INVALID_PARAMETER, error); + + /* Get note with NULL size */ + SetLastError(0xdeadbeef); + lstrcpyW(buffer_w, deadbeef_w); + ret = SendMessageA(hwnd, BCM_GETNOTE, 0, (LPARAM)buffer_w); + ok(!ret, "Expect BCM_SETNOTE return false\n"); + ok(!lstrcmpW(buffer_w, deadbeef_w), "Expect note: %s, got: %s\n", + wine_dbgstr_w(deadbeef_w), wine_dbgstr_w(buffer_w)); + error = GetLastError(); + ok(error == ERROR_INVALID_PARAMETER, "Expect last error: 0x%08x, got: 0x%08x\n", + ERROR_INVALID_PARAMETER, error); + + /* Get note with zero size */ + SetLastError(0xdeadbeef); + size = 0; + lstrcpyW(buffer_w, deadbeef_w); + ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w); + ok(!ret, "Expect BCM_GETNOTE return false\n"); + ok(!lstrcmpW(buffer_w, deadbeef_w), "Expect note: %s, got: %s\n", + wine_dbgstr_w(deadbeef_w), wine_dbgstr_w(buffer_w)); + ok(size == 1, "Got: %d\n", size); + error = GetLastError(); + ok(error == ERROR_INSUFFICIENT_BUFFER, "Expect last error: 0x%08x, got: 0x%08x\n", + ERROR_INSUFFICIENT_BUFFER, error); + + DestroyWindow(hwnd); + } + else + { + hwnd = create_button(type, NULL); + ok(hwnd != NULL, "Expect hwnd not null\n"); + SetLastError(0xdeadbeef); + size = ARRAY_SIZE(buffer_w); + ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w); + ok(!ret, "Expect BCM_GETNOTE return false\n"); + error = GetLastError(); + ok(error == ERROR_NOT_SUPPORTED, "Expect last error: 0x%08x, got: 0x%08x\n", + ERROR_NOT_SUPPORTED, error); + DestroyWindow(hwnd); + } + } +} + +static void test_bm_get_set_image(void) +{ + HWND hwnd; + HDC hdc; + HBITMAP hbmp1x1; + HBITMAP hbmp2x2; + HBITMAP hmask2x2; + ICONINFO icon_info2x2; + HICON hicon2x2; + HBITMAP hbmp; + HICON hicon; + ICONINFO icon_info; + BITMAP bm; + static const DWORD default_style = BS_PUSHBUTTON | WS_TABSTOP | WS_POPUP | WS_VISIBLE; + + hdc = GetDC(0); + hbmp1x1 = CreateCompatibleBitmap(hdc, 1, 1); + hbmp2x2 = CreateCompatibleBitmap(hdc, 2, 2); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(hbmp1x1, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1, + bm.bmWidth, bm.bmHeight); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(hbmp2x2, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2, + bm.bmWidth, bm.bmHeight); + + hmask2x2 = CreateCompatibleBitmap(hdc, 2, 2); + ZeroMemory(&icon_info2x2, sizeof(icon_info2x2)); + icon_info2x2.fIcon = TRUE; + icon_info2x2.hbmMask = hmask2x2; + icon_info2x2.hbmColor = hbmp2x2; + hicon2x2 = CreateIconIndirect(&icon_info2x2); + ok(hicon2x2 !=NULL, "Expect CreateIconIndirect() success\n"); + + ZeroMemory(&icon_info, sizeof(icon_info)); + ok(GetIconInfo(hicon2x2, &icon_info), "Expect GetIconInfo() success\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(icon_info.hbmColor, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2, + bm.bmWidth, bm.bmHeight); + DeleteObject(icon_info.hbmColor); + DeleteObject(icon_info.hbmMask); + + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0, + 0, 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + /* Get image when image is not set */ + hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0); + ok(hbmp == 0, "Expect hbmp == 0\n"); + /* Set image */ + hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp1x1); + ok(hbmp == 0, "Expect hbmp == 0\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0); + ok(hbmp != 0, "Expect hbmp != 0\n"); + /* Set null resets image */ + hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, 0); + ok(hbmp != 0, "Expect hbmp != 0\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0); + ok(hbmp == 0, "Expect hbmp == 0\n"); + DestroyWindow(hwnd); + + /* Set bitmap with BS_BITMAP */ + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0, + 0, 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp1x1); + ok(hbmp == 0, "Expect hbmp == 0\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0); + ok(hbmp != 0, "Expect hbmp != 0\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1, + bm.bmWidth, bm.bmHeight); + DestroyWindow(hwnd); + + /* Set bitmap without BS_BITMAP */ + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style, 0, 0, 100, 100, 0, 0, 0, 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp1x1); + ok(hbmp == 0, "Expect hbmp == 0\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0); + if (hbmp == 0) + { + /* on xp or 2003*/ + win_skip("Show both image and text is not supported. Skip following tests.\n"); + DestroyWindow(hwnd); + goto done; + } + ok(hbmp != 0, "Expect hbmp != 0\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1, + bm.bmWidth, bm.bmHeight); + DestroyWindow(hwnd); + + /* Set icon with BS_ICON */ + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_ICON, 0, 0, 100, 100, 0, 0, 0, + 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + hicon = (HICON)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hicon2x2); + ok(hicon == 0, "Expect hicon == 0\n"); + hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_ICON, 0); + ok(hicon != 0, "Expect hicon != 0\n"); + ZeroMemory(&icon_info, sizeof(icon_info)); + ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(icon_info.hbmColor, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2, + bm.bmWidth, bm.bmHeight); + DeleteObject(icon_info.hbmColor); + DeleteObject(icon_info.hbmMask); + DestroyWindow(hwnd); + + /* Set icon without BS_ICON */ + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style, 0, 0, 100, 100, 0, 0, 0, 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + hicon = (HICON)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hicon2x2); + ok(hicon == 0, "Expect hicon == 0\n"); + hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_ICON, 0); + ok(hicon != 0, "Expect hicon != 0\n"); + ZeroMemory(&icon_info, sizeof(icon_info)); + ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(icon_info.hbmColor, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2, + bm.bmWidth, bm.bmHeight); + DeleteObject(icon_info.hbmColor); + DeleteObject(icon_info.hbmMask); + DestroyWindow(hwnd); + + /* Set icon with BS_BITMAP */ + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0, + 0, 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + hicon = (HICON)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hicon2x2); + ok(hicon == 0, "Expect hicon == 0\n"); + hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_ICON, 0); + ok(hicon != 0, "Expect hicon != 0\n"); + ZeroMemory(&icon_info, sizeof(icon_info)); + ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(icon_info.hbmColor, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2, + bm.bmWidth, bm.bmHeight); + DeleteObject(icon_info.hbmColor); + DeleteObject(icon_info.hbmMask); + DestroyWindow(hwnd); + + /* Set bitmap with BS_ICON */ + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_ICON, 0, 0, 100, 100, 0, 0, 0, + 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp1x1); + ok(hbmp == 0, "Expect hbmp == 0\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0); + ok(hbmp != 0, "Expect hbmp != 0\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1, + bm.bmWidth, bm.bmHeight); + DestroyWindow(hwnd); + + /* Set bitmap with BS_BITMAP and IMAGE_ICON*/ + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0, + 0, 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hbmp1x1); + ok(hbmp == 0, "Expect hbmp == 0\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_ICON, 0); + ok(hbmp != 0, "Expect hbmp != 0\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1, + bm.bmWidth, bm.bmHeight); + DestroyWindow(hwnd); + + /* Set icon with BS_ICON and IMAGE_BITMAP */ + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_ICON, 0, 0, 100, 100, 0, 0, 0, + 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + hicon = (HICON)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hicon2x2); + ok(hicon == 0, "Expect hicon == 0\n"); + hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0); + ok(hicon != 0, "Expect hicon != 0\n"); + ZeroMemory(&icon_info, sizeof(icon_info)); + ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(icon_info.hbmColor, sizeof(BITMAP), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2, + bm.bmWidth, bm.bmHeight); + DeleteObject(icon_info.hbmColor); + DeleteObject(icon_info.hbmMask); + DestroyWindow(hwnd); + + /* Set bitmap with BS_ICON and IMAGE_ICON */ + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_ICON, 0, 0, 100, 100, 0, 0, 0, 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hbmp1x1); + ok(hbmp == 0, "Expect hbmp == 0\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_ICON, 0); + ok(hbmp != 0, "Expect hbmp != 0\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1, + bm.bmWidth, bm.bmHeight); + DestroyWindow(hwnd); + + /* Set icon with BS_BITMAP and IMAGE_BITMAP */ + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0, 0, 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + hicon = (HICON)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hicon2x2); + ok(hicon == 0, "Expect hicon == 0\n"); + hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0); + ok(hicon != 0, "Expect hicon != 0\n"); + ZeroMemory(&icon_info, sizeof(icon_info)); + ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(icon_info.hbmColor, sizeof(BITMAP), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2, + bm.bmWidth, bm.bmHeight); + DeleteObject(icon_info.hbmColor); + DeleteObject(icon_info.hbmMask); + DestroyWindow(hwnd); + +done: + DestroyIcon(hicon2x2); + DeleteObject(hmask2x2); + DeleteObject(hbmp2x2); + DeleteObject(hbmp1x1); + ReleaseDC(0, hdc); +} + static void register_parent_class(void) { WNDCLASSA cls; @@ -824,6 +1228,504 @@ static void register_parent_class(void) RegisterClassA(&cls); } +static void test_button_data(void) +{ + static const DWORD styles[] = + { + BS_PUSHBUTTON, + BS_DEFPUSHBUTTON, + BS_CHECKBOX, + BS_AUTOCHECKBOX, + BS_RADIOBUTTON, + BS_3STATE, + BS_AUTO3STATE, + BS_GROUPBOX, + BS_USERBUTTON, + BS_AUTORADIOBUTTON, + BS_OWNERDRAW, + BS_SPLITBUTTON, + BS_DEFSPLITBUTTON, + BS_COMMANDLINK, + BS_DEFCOMMANDLINK, + }; + + struct button_desc + { + HWND self; + HWND parent; + LONG style; + }; + unsigned int i; + HWND parent; + + parent = CreateWindowExA(0, "TestParentClass", "Test parent", WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 200, 200, 0, 0, 0, NULL); + ok(parent != 0, "Failed to create parent window\n"); + + for (i = 0; i < ARRAY_SIZE(styles); i++) + { + struct button_desc *desc; + HWND hwnd; + + hwnd = create_button(styles[i], parent); + ok(hwnd != NULL, "Failed to create a button.\n"); + + desc = (void *)GetWindowLongPtrA(hwnd, 0); + ok(desc != NULL, "Expected window data.\n"); + + if (desc) + { + ok(desc->self == hwnd, "Unexpected 'self' field.\n"); + ok(desc->parent == parent, "Unexpected 'parent' field.\n"); + ok(desc->style == (WS_CHILD | BS_NOTIFY | styles[i]), "Unexpected 'style' field.\n"); + } + + DestroyWindow(hwnd); + } + + DestroyWindow(parent); +} + +static void test_get_set_imagelist(void) +{ + HWND hwnd; + HIMAGELIST himl; + BUTTON_IMAGELIST biml = {0}; + HDC hdc; + HBITMAP hbmp; + INT width = 16; + INT height = 16; + INT index; + DWORD type; + BOOL ret; + + hdc = GetDC(0); + hbmp = CreateCompatibleBitmap(hdc, width, height); + ok(hbmp != NULL, "Expect hbmp not null\n"); + + himl = pImageList_Create(width, height, ILC_COLOR, 1, 0); + ok(himl != NULL, "Expect himl not null\n"); + index = pImageList_Add(himl, hbmp, NULL); + ok(index == 0, "Expect index == 0\n"); + DeleteObject(hbmp); + ReleaseDC(0, hdc); + + for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++) + { + hwnd = create_button(type, NULL); + ok(hwnd != NULL, "Expect hwnd not null\n"); + + /* Get imagelist when imagelist is unset yet */ + ret = SendMessageA(hwnd, BCM_GETIMAGELIST, 0, (LPARAM)&biml); + ok(ret, "Expect BCM_GETIMAGELIST return true\n"); + ok(biml.himl == 0 && IsRectEmpty(&biml.margin) && biml.uAlign == 0, + "Expect BUTTON_IMAGELIST is empty\n"); + + /* Set imagelist with himl null */ + biml.himl = 0; + biml.uAlign = BUTTON_IMAGELIST_ALIGN_CENTER; + ret = SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml); + ok(ret || broken(!ret), /* xp or 2003 */ + "Expect BCM_SETIMAGELIST return true\n"); + + /* Set imagelist with uAlign invalid */ + biml.himl = himl; + biml.uAlign = -1; + ret = SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml); + ok(ret, "Expect BCM_SETIMAGELIST return true\n"); + + /* Successful get and set imagelist */ + biml.himl = himl; + biml.uAlign = BUTTON_IMAGELIST_ALIGN_CENTER; + ret = SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml); + ok(ret, "Expect BCM_SETIMAGELIST return true\n"); + ret = SendMessageA(hwnd, BCM_GETIMAGELIST, 0, (LPARAM)&biml); + ok(ret, "Expect BCM_GETIMAGELIST return true\n"); + ok(biml.himl == himl, "Expect himl to be same\n"); + ok(biml.uAlign == BUTTON_IMAGELIST_ALIGN_CENTER, "Expect uAlign to be %x\n", + BUTTON_IMAGELIST_ALIGN_CENTER); + + /* BCM_SETIMAGELIST null pointer handling */ + ret = SendMessageA(hwnd, BCM_SETIMAGELIST, 0, 0); + ok(!ret, "Expect BCM_SETIMAGELIST return false\n"); + ret = SendMessageA(hwnd, BCM_GETIMAGELIST, 0, (LPARAM)&biml); + ok(ret, "Expect BCM_GETIMAGELIST return true\n"); + ok(biml.himl == himl, "Expect himl to be same\n"); + + /* BCM_GETIMAGELIST null pointer handling */ + biml.himl = himl; + biml.uAlign = BUTTON_IMAGELIST_ALIGN_CENTER; + ret = SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml); + ok(ret, "Expect BCM_SETIMAGELIST return true\n"); + ret = SendMessageA(hwnd, BCM_GETIMAGELIST, 0, 0); + ok(!ret, "Expect BCM_GETIMAGELIST return false\n"); + + DestroyWindow(hwnd); + } + + pImageList_Destroy(himl); +} + +static void test_get_set_textmargin(void) +{ + HWND hwnd; + RECT margin_in; + RECT margin_out; + BOOL ret; + DWORD type; + + SetRect(&margin_in, 2, 1, 3, 4); + for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++) + { + hwnd = create_button(type, NULL); + ok(hwnd != NULL, "Expect hwnd not null\n"); + + /* Get text margin when it is unset */ + ret = SendMessageA(hwnd, BCM_GETTEXTMARGIN, 0, (LPARAM)&margin_out); + ok(ret, "Expect ret to be true\n"); + ok(IsRectEmpty(&margin_out), "Expect margin empty\n"); + + /* Successful get and set text margin */ + ret = SendMessageA(hwnd, BCM_SETTEXTMARGIN, 0, (LPARAM)&margin_in); + ok(ret, "Expect ret to be true\n"); + SetRectEmpty(&margin_out); + ret = SendMessageA(hwnd, BCM_GETTEXTMARGIN, 0, (LPARAM)&margin_out); + ok(ret, "Expect ret to be true\n"); + ok(EqualRect(&margin_in, &margin_out), "Expect margins to be equal\n"); + + /* BCM_SETTEXTMARGIN null pointer handling */ + ret = SendMessageA(hwnd, BCM_SETTEXTMARGIN, 0, 0); + ok(!ret, "Expect ret to be false\n"); + SetRectEmpty(&margin_out); + ret = SendMessageA(hwnd, BCM_GETTEXTMARGIN, 0, (LPARAM)&margin_out); + ok(ret, "Expect ret to be true\n"); + ok(EqualRect(&margin_in, &margin_out), "Expect margins to be equal\n"); + + /* BCM_GETTEXTMARGIN null pointer handling */ + ret = SendMessageA(hwnd, BCM_SETTEXTMARGIN, 0, (LPARAM)&margin_in); + ok(ret, "Expect ret to be true\n"); + ret = SendMessageA(hwnd, BCM_GETTEXTMARGIN, 0, 0); + ok(!ret, "Expect ret to be true\n"); + + DestroyWindow(hwnd); + } +} + +static void test_state(void) +{ + HWND hwnd; + DWORD type; + LONG state; + + /* Initial button state */ + for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++) + { + hwnd = create_button(type, NULL); + state = SendMessageA(hwnd, BM_GETSTATE, 0, 0); + ok(state == BST_UNCHECKED, "Expect state 0x%08x, got 0x%08x\n", BST_UNCHECKED, state); + DestroyWindow(hwnd); + } +} + +static void test_bcm_get_ideal_size(void) +{ + static const char *button_text2 = "WWWW\nWWWW"; + static const char *button_text = "WWWW"; + static const DWORD imagelist_aligns[] = {BUTTON_IMAGELIST_ALIGN_LEFT, BUTTON_IMAGELIST_ALIGN_RIGHT, + BUTTON_IMAGELIST_ALIGN_TOP, BUTTON_IMAGELIST_ALIGN_BOTTOM, + BUTTON_IMAGELIST_ALIGN_CENTER}; + static const DWORD aligns[] = {0, BS_TOP, BS_LEFT, BS_RIGHT, BS_BOTTOM, + BS_CENTER, BS_VCENTER, BS_RIGHTBUTTON, WS_EX_RIGHT}; + DWORD default_style = WS_TABSTOP | WS_POPUP | WS_VISIBLE; + const LONG client_width = 400, client_height = 200; + LONG image_width, height, line_count, text_width; + HFONT hfont, prev_font; + DWORD style, type; + BOOL ret; + HWND hwnd; + HDC hdc; + LOGFONTA lf; + TEXTMETRICA tm; + SIZE size; + HBITMAP hmask, hbmp; + ICONINFO icon_info; + HICON hicon; + HIMAGELIST himl; + BUTTON_IMAGELIST biml = {0}; + RECT rect; + INT i, j; + + /* Check for NULL pointer handling */ + hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_PUSHBUTTON | default_style, 0, 0, client_width, client_height, + NULL, NULL, 0, NULL); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, 0); + ok(!ret, "Expect BCM_GETIDEALSIZE message to return false.\n"); + + /* Set font so that the test is consistent on Wine and Windows */ + ZeroMemory(&lf, sizeof(lf)); + lf.lfWeight = FW_NORMAL; + lf.lfHeight = 20; + lstrcpyA(lf.lfFaceName, "Tahoma"); + hfont = CreateFontIndirectA(&lf); + ok(hfont != NULL, "Failed to create test font.\n"); + + /* Get tmHeight */ + hdc = GetDC(hwnd); + prev_font = SelectObject(hdc, hfont); + GetTextMetricsA(hdc, &tm); + SelectObject(hdc, prev_font); + DrawTextA(hdc, button_text, -1, &rect, DT_CALCRECT); + text_width = rect.right - rect.left; + ReleaseDC(hwnd, hdc); + DestroyWindow(hwnd); + + /* XP and 2003 doesn't support command links, getting ideal size with button having only text returns client size on these platforms. */ + hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_DEFCOMMANDLINK | default_style, 0, 0, client_width, client_height, NULL, + NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + if (size.cx == client_width && size.cy == client_height) + { + /* on XP and 2003, buttons with image are not supported */ + win_skip("Skipping further tests on XP and 2003\n"); + return; + } + + /* Tests for image placements */ + /* Prepare bitmap */ + image_width = 48; + height = 48; + hdc = GetDC(0); + hmask = CreateCompatibleBitmap(hdc, image_width, height); + hbmp = CreateCompatibleBitmap(hdc, image_width, height); + + /* Only bitmap for push button, ideal size should be enough for image and text */ + hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_DEFPUSHBUTTON | BS_BITMAP | default_style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + /* Ideal size contains text rect even show bitmap only */ + ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)), + "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, image_width + text_width, + size.cy, max(height, tm.tmHeight)); + DestroyWindow(hwnd); + + /* Image alignments when button has bitmap and text*/ + for (i = 0; i < ARRAY_SIZE(aligns); i++) + for (j = 0; j < ARRAY_SIZE(aligns); j++) + { + style = BS_DEFPUSHBUTTON | default_style | aligns[i] | aligns[j]; + hwnd = CreateWindowA(WC_BUTTONA, button_text, style, 0, 0, client_width, client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + if (!(style & (BS_CENTER | BS_VCENTER)) || ((style & BS_CENTER) && (style & BS_CENTER) != BS_CENTER) + || !(style & BS_VCENTER) || (style & BS_VCENTER) == BS_VCENTER) + ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)), + "Style: 0x%08x expect ideal cx %d >= %d and ideal cy %d >= %d\n", style, size.cx, + image_width + text_width, size.cy, max(height, tm.tmHeight)); + else + ok((size.cx >= max(text_width, height) && size.cy >= height + tm.tmHeight), + "Style: 0x%08x expect ideal cx %d >= %d and ideal cy %d >= %d\n", style, size.cx, + max(text_width, height), size.cy, height + tm.tmHeight); + DestroyWindow(hwnd); + } + + /* Image list alignments */ + himl = pImageList_Create(image_width, height, ILC_COLOR, 1, 1); + pImageList_Add(himl, hbmp, 0); + biml.himl = himl; + for (i = 0; i < ARRAY_SIZE(imagelist_aligns); i++) + { + biml.uAlign = imagelist_aligns[i]; + hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_DEFPUSHBUTTON | default_style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + if (biml.uAlign == BUTTON_IMAGELIST_ALIGN_TOP || biml.uAlign == BUTTON_IMAGELIST_ALIGN_BOTTOM) + ok((size.cx >= max(text_width, height) && size.cy >= height + tm.tmHeight), + "Align:%d expect ideal cx %d >= %d and ideal cy %d >= %d\n", biml.uAlign, size.cx, + max(text_width, height), size.cy, height + tm.tmHeight); + else if (biml.uAlign == BUTTON_IMAGELIST_ALIGN_LEFT || biml.uAlign == BUTTON_IMAGELIST_ALIGN_RIGHT) + ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)), + "Align:%d expect ideal cx %d >= %d and ideal cy %d >= %d\n", biml.uAlign, size.cx, + image_width + text_width, size.cy, max(height, tm.tmHeight)); + else + ok(size.cx >= image_width && size.cy >= height, "Align:%d expect ideal cx %d >= %d and ideal cy %d >= %d\n", + biml.uAlign, size.cx, image_width, size.cy, height); + DestroyWindow(hwnd); + } + + /* Icon as image */ + /* Create icon from bitmap */ + ZeroMemory(&icon_info, sizeof(icon_info)); + icon_info.fIcon = TRUE; + icon_info.hbmMask = hmask; + icon_info.hbmColor = hbmp; + hicon = CreateIconIndirect(&icon_info); + + /* Only icon, ideal size should be enough for image and text */ + hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_DEFPUSHBUTTON | BS_ICON | default_style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hicon); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + /* Ideal size contains text rect even show icons only */ + ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)), + "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, image_width + text_width, size.cy, + max(height, tm.tmHeight)); + DestroyWindow(hwnd); + + /* Show icon and text */ + hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_DEFPUSHBUTTON | default_style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hicon); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)), + "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, image_width + text_width, size.cy, + max(height, tm.tmHeight)); + DestroyWindow(hwnd); + + /* Checkbox */ + /* Both bitmap and text for checkbox, ideal size is only enough for text because it doesn't support image(but not image list)*/ + hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_AUTOCHECKBOX | default_style, 0, 0, client_width, client_height, + NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + ok((size.cx <= image_width + text_width && size.cx >= text_width && size.cy <= max(height, tm.tmHeight) + && size.cy >= tm.tmHeight), + "Expect ideal cx %d within range (%d, %d ) and ideal cy %d within range (%d, %d )\n", size.cx, + text_width, image_width + text_width, size.cy, tm.tmHeight, max(height, tm.tmHeight)); + DestroyWindow(hwnd); + + /* Both image list and text for checkbox, ideal size should have enough for image list and text */ + biml.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT; + hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_AUTOCHECKBOX | BS_BITMAP | default_style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)), + "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, image_width + text_width, size.cy, + max(height, tm.tmHeight)); + DestroyWindow(hwnd); + + /* Only bitmap for checkbox, ideal size should have enough for image and text */ + hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_AUTOCHECKBOX | BS_BITMAP | default_style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)), + "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, image_width + text_width, size.cy, + max(height, tm.tmHeight)); + DestroyWindow(hwnd); + + /* Test button with only text */ + /* No text */ + for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++) + { + style = type | default_style; + hwnd = CreateWindowA(WC_BUTTONA, "", style, 0, 0, client_width, client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + + if (type == BS_COMMANDLINK || type == BS_DEFCOMMANDLINK) + { + todo_wine ok((size.cx == 0 && size.cy > 0), "Style 0x%08x expect ideal cx %d >= %d and ideal cy %d >= %d\n", + style, size.cx, 0, size.cy, 0); + } + else + { + ok(size.cx == client_width && size.cy == client_height, + "Style 0x%08x expect size.cx == %d and size.cy == %d, got size.cx: %d size.cy: %d\n", style, + client_width, client_height, size.cx, size.cy); + } + DestroyWindow(hwnd); + } + + /* Single line and multiple lines text */ + for (line_count = 1; line_count <= 2; line_count++) + { + for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++) + { + style = line_count > 1 ? type | BS_MULTILINE : type; + style |= default_style; + + hwnd = CreateWindowA(WC_BUTTONA, (line_count == 2 ? button_text2 : button_text), style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + + if (type == BS_3STATE || type == BS_AUTO3STATE || type == BS_GROUPBOX || type == BS_PUSHBOX + || type == BS_OWNERDRAW) + { + ok(size.cx == client_width && size.cy == client_height, + "Style 0x%08x expect ideal size (%d,%d), got (%d,%d)\n", style, client_width, client_height, size.cx, + size.cy); + } + else if (type == BS_COMMANDLINK || type == BS_DEFCOMMANDLINK) + { + todo_wine ok((size.cx == 0 && size.cy > 0), + "Style 0x%08x expect ideal cx %d >= %d and ideal cy %d >= %d\n", style, size.cx, 0, + size.cy, 0); + } + else + { + height = line_count == 2 ? 2 * tm.tmHeight : tm.tmHeight; + ok(size.cx >= 0 && size.cy >= height, "Style 0x%08x expect ideal cx %d >= 0 and ideal cy %d >= %d\n", + style, size.cx, size.cy, height); + } + DestroyWindow(hwnd); + } + } + + pImageList_Destroy(himl); + DestroyIcon(hicon); + DeleteObject(hbmp); + DeleteObject(hmask); + ReleaseDC(0, hdc); + DeleteObject(hfont); +} + START_TEST(button) { ULONG_PTR ctx_cookie; @@ -839,6 +1741,13 @@ START_TEST(button) test_button_class(); test_button_messages(); + test_note(); + test_button_data(); + test_bm_get_set_image(); + test_get_set_imagelist(); + test_get_set_textmargin(); + test_state(); + test_bcm_get_ideal_size(); unload_v6_module(ctx_cookie, hCtx); } diff --git a/modules/rostests/winetests/comctl32/combo.c b/modules/rostests/winetests/comctl32/combo.c index 83b36f212f..923d826b30 100644 --- a/modules/rostests/winetests/comctl32/combo.c +++ b/modules/rostests/winetests/comctl32/combo.c @@ -264,7 +264,7 @@ static void test_comboex_WM_LBUTTONDOWN(void) WS_VISIBLE|WS_CHILD|CBS_DROPDOWN, 0, 0, 200, 150, hComboExParentWnd, NULL, hMainHinst, NULL); - for (i = 0; i < sizeof(choices)/sizeof(UINT); i++){ + for (i = 0; i < ARRAY_SIZE(choices); i++){ COMBOBOXEXITEMW cbexItem; wsprintfW(buffer, stringFormat, choices[i]); @@ -1165,11 +1165,11 @@ static void test_combo_dropdown_size(DWORD style) int limit; } info_height[] = { {33, 50, -1}, - {35, 50, 40}, + {35, 100, 40}, {15, 50, 3}, }; - for (test = 0; test < sizeof(info_height) / sizeof(info_height[0]); test++) + for (test = 0; test < ARRAY_SIZE(info_height); test++) { const struct list_size_info *info_test = &info_height[test]; int height_item; /* Height of a list item */ @@ -1182,7 +1182,6 @@ static void test_combo_dropdown_size(DWORD style) info_test->height_combo, hMainWnd, (HMENU)COMBO_ID, NULL, 0); min_visible_expected = SendMessageA(hCombo, CB_GETMINVISIBLE, 0, 0); - todo_wine ok(min_visible_expected == 30, "Unexpected number of items %d.\n", min_visible_expected); cbInfo.cbSize = sizeof(COMBOBOXINFO); @@ -1202,10 +1201,8 @@ static void test_combo_dropdown_size(DWORD style) min_visible_expected = info_test->limit; ret = SendMessageA(hCombo, CB_SETMINVISIBLE, min_visible_expected, 0); - todo_wine ok(ret, "Failed to set visible limit.\n"); min_visible_actual = SendMessageA(hCombo, CB_GETMINVISIBLE, 0, 0); - todo_wine ok(min_visible_expected == min_visible_actual, "test %d: unexpected number of items %d.\n", test, min_visible_actual); } @@ -1242,7 +1239,6 @@ static void test_combo_dropdown_size(DWORD style) if (expected_height_list < 0) expected_height_list = 0; - todo_wine ok(expected_height_list == height_list, "Test %d, expected list height to be %d, got %d\n", test, expected_height_list, height_list); } @@ -1250,7 +1246,6 @@ static void test_combo_dropdown_size(DWORD style) { expected_height_list = min(info_test->num_items, min_visible_expected) * height_item; - todo_wine ok(expected_height_list == height_list, "Test %d, expected list height to be %d, got %d\n", test, expected_height_list, height_list); } diff --git a/modules/rostests/winetests/comctl32/datetime.c b/modules/rostests/winetests/comctl32/datetime.c index 7742dc5d18..88abce9ed6 100644 --- a/modules/rostests/winetests/comctl32/datetime.c +++ b/modules/rostests/winetests/comctl32/datetime.c @@ -721,6 +721,46 @@ static void test_dtm_set_and_get_systemtime_with_limits(void) DestroyWindow(hWnd); } +static void test_dtm_get_ideal_size(void) +{ + HWND hwnd; + HDC hdc; + HFONT hfont; + LOGFONTA lf; + TEXTMETRICA tm; + SIZE size; + BOOL r; + + hwnd = create_datetime_control(0); + r = SendMessageA(hwnd, DTM_GETIDEALSIZE, 0, (LPARAM)&size); + if (!r) + { + win_skip("DTM_GETIDEALSIZE is not available\n"); + DestroyWindow(hwnd); + return; + } + + /* Set font so that the test is consistent on Wine and Windows */ + ZeroMemory(&lf, sizeof(lf)); + lf.lfWeight = FW_NORMAL; + lf.lfHeight = 20; + lstrcpyA(lf.lfFaceName, "Tahoma"); + hfont = CreateFontIndirectA(&lf); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + + hdc = GetDC(hwnd); + GetTextMetricsA(hdc, &tm); + ReleaseDC(hwnd, hdc); + + r = SendMessageA(hwnd, DTM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(r, "Expect DTM_GETIDEALSIZE message to return true\n"); + ok(size.cx > 0 && size.cy >= tm.tmHeight, + "Expect size.cx > 0 and size.cy >= %d, got cx:%d cy:%d\n", tm.tmHeight, size.cx, size.cy); + + DestroyWindow(hwnd); + DeleteObject(hfont); +} + static void test_wm_set_get_text(void) { static const CHAR a_str[] = "a"; @@ -821,6 +861,7 @@ START_TEST(datetime) test_dtm_set_and_get_mccolor(); test_dtm_set_and_get_mcfont(); test_dtm_get_monthcal(); + test_dtm_get_ideal_size(); test_wm_set_get_text(); test_dts_shownone(); diff --git a/modules/rostests/winetests/comctl32/edit.c b/modules/rostests/winetests/comctl32/edit.c index 7e9e7e7634..bc44d0c240 100644 --- a/modules/rostests/winetests/comctl32/edit.c +++ b/modules/rostests/winetests/comctl32/edit.c @@ -2969,7 +2969,7 @@ static void test_EM_GETLINE(void) hwnd[0] = create_editcontrol(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); hwnd[1] = create_editcontrolW(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); - for (i = 0; i < sizeof(hwnd)/sizeof(hwnd[0]); i++) + for (i = 0; i < ARRAY_SIZE(hwnd); i++) { static const WCHAR strW[] = {'t','e','x','t',0}; static const char *str = "text"; @@ -2994,13 +2994,13 @@ static void test_EM_GETLINE(void) ok(!strcmp(buff, str), "Unexpected line data %s.\n", buff); memset(buffW, 0, sizeof(buffW)); - *(WORD *)buffW = sizeof(buffW)/sizeof(buffW[0]); + *(WORD *)buffW = ARRAY_SIZE(buffW); r = SendMessageW(hwnd[i], EM_GETLINE, 0, (LPARAM)buffW); ok(r == lstrlenW(strW), "Failed to get a line %d.\n", r); ok(!lstrcmpW(buffW, strW), "Unexpected line data %s.\n", wine_dbgstr_w(buffW)); memset(buffW, 0, sizeof(buffW)); - *(WORD *)buffW = sizeof(buffW)/sizeof(buffW[0]); + *(WORD *)buffW = ARRAY_SIZE(buffW); r = SendMessageW(hwnd[i], EM_GETLINE, 1, (LPARAM)buffW); ok(r == lstrlenW(strW), "Failed to get a line %d.\n", r); ok(!lstrcmpW(buffW, strW), "Unexpected line data %s.\n", wine_dbgstr_w(buffW)); @@ -3061,6 +3061,83 @@ static const struct message killfocus_combined_seq[] = { 0 } }; +static void test_cue_banner(void) +{ + HWND hwnd_edit; + BOOL ret; + static WCHAR getcuetestW[5] = {'T',0}; + static const WCHAR testcmp1W[] = {'T','e','s','t',0}; + static const WCHAR testcmp2W[] = {'T','e','s',0}; + static const WCHAR emptyW[] = {0}; + + hwnd_edit = create_editcontrolW(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); + + ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, (WPARAM)getcuetestW, 5); + if (lstrcmpW(getcuetestW, emptyW) != 0) + { + win_skip("skipping for Win XP and 2003 Server.\n"); + DestroyWindow(hwnd_edit); + return; + } + ok(lstrcmpW(getcuetestW, emptyW) == 0, "First char is %c\n", getcuetestW[0]); + ok(ret == FALSE, "EM_GETCUEBANNER should have returned FALSE.\n"); + + lstrcpyW(getcuetestW, testcmp1W); + ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, (WPARAM)getcuetestW, 0); + ok(lstrcmpW(getcuetestW, testcmp1W) == 0, "String was %s.\n", wine_dbgstr_w(getcuetestW)); + ok(ret == FALSE, "EM_GETCUEBANNER should have returned FALSE.\n"); + + ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, 0, 0); + ok(ret == FALSE, "EM_GETCUEBANNER should have returned FALSE.\n"); + + ret = SendMessageW(hwnd_edit, EM_SETCUEBANNER, 0, 0); + ok(ret == FALSE, "EM_SETCUEBANNER should have returned FALSE.\n"); + + ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, 0, 0); + ok(ret == FALSE, "EM_GETCUEBANNER should have returned FALSE.\n"); + + lstrcpyW(getcuetestW, testcmp1W); + ret = SendMessageW(hwnd_edit, EM_SETCUEBANNER, 0, (LPARAM)getcuetestW); + ok(ret == TRUE, "EM_SETCUEBANNER should have returned TRUE.\n"); + + ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, 0, 5); + ok(ret == TRUE, "EM_GETCUEBANNER should have returned TRUE.\n"); + + ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, (WPARAM)getcuetestW, 5); + ok(ret == TRUE, "EM_GETCUEBANNER should have returned TRUE.\n"); + ok(lstrcmpW(getcuetestW, testcmp1W) == 0, "EM_GETCUEBANNER returned string %s.\n", wine_dbgstr_w(getcuetestW)); + + ret = SendMessageW(hwnd_edit, EM_SETCUEBANNER, 0, (LPARAM)emptyW); + ok(ret == TRUE, "EM_SETCUEBANNER should have returned TRUE.\n"); + + ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, (WPARAM)getcuetestW, 5); + ok(ret == TRUE, "EM_GETCUEBANNER should have returned TRUE.\n"); + ok(lstrcmpW(getcuetestW, emptyW) == 0, "EM_GETCUEBANNER returned string %s.\n", wine_dbgstr_w(getcuetestW)); + + /* EM_GETCUEBANNER's buffer size includes null char */ + ret = SendMessageW(hwnd_edit, EM_SETCUEBANNER, 0, (LPARAM)testcmp1W); + ok(ret == TRUE, "EM_SETCUEBANNER should have returned TRUE.\n"); + memset(getcuetestW, 0, lstrlenW(testcmp1W)*sizeof(WCHAR)); + ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, (WPARAM)getcuetestW, (LPARAM)lstrlenW(testcmp1W)+1); + ok(ret == TRUE, "EM_GETCUEBANNER should have returned TRUE.\n"); + ok(lstrcmpW(getcuetestW, testcmp1W) == 0, "EM_GETCUEBANNER returned string %s.\n", wine_dbgstr_w(getcuetestW)); + memset(getcuetestW, 0, lstrlenW(testcmp1W)*sizeof(WCHAR)); + ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, (WPARAM)getcuetestW, (LPARAM)lstrlenW(testcmp1W)); + ok(lstrcmpW(getcuetestW, testcmp2W) == 0, "EM_GETCUEBANNER returned string %s.\n", wine_dbgstr_w(getcuetestW)); + DestroyWindow(hwnd_edit); + + /* setting cue banner fails for multi-line edit controls */ + hwnd_edit = create_editcontrolW(ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE, 0); + lstrcpyW(getcuetestW, testcmp1W); + ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, (WPARAM)getcuetestW, 5); + ok(ret == FALSE, "EM_SETCUEBANNER.\n"); + ok(lstrcmpW(getcuetestW, testcmp1W) == 0, "String was %s.\n", wine_dbgstr_w(getcuetestW)); + ret = SendMessageW(hwnd_edit, EM_SETCUEBANNER, 0, (LPARAM)getcuetestW); + ok(ret == FALSE, "EM_SETCUEBANNER.\n"); + + DestroyWindow(hwnd_edit); +} + static void test_change_focus(void) { HWND hwnd, parent_wnd; @@ -3138,6 +3215,7 @@ START_TEST(edit) test_EM_GETLINE(); test_wordbreak_proc(); test_change_focus(); + test_cue_banner(); UnregisterWindowClasses(); diff --git a/modules/rostests/winetests/comctl32/header.c b/modules/rostests/winetests/comctl32/header.c index 7ea80519a6..3428936cd7 100644 --- a/modules/rostests/winetests/comctl32/header.c +++ b/modules/rostests/winetests/comctl32/header.c @@ -1130,7 +1130,7 @@ static void test_hdm_index_messages(HWND hParent) ok_sequence(sequences, PARENT_SEQ_INDEX, add_header_to_parent_seq, "adder header control to parent", FALSE); flush_sequences(sequences, NUM_MSG_SEQUENCES); - for (i = 0; i < sizeof(item_texts)/sizeof(item_texts[0]); i++) + for (i = 0; i < ARRAY_SIZE(item_texts); i++) { hdItem.mask = HDI_TEXT | HDI_WIDTH | HDI_FORMAT; hdItem.pszText = (char*)item_texts[i]; @@ -1170,7 +1170,7 @@ static void test_hdm_index_messages(HWND hParent) hdItem.mask = HDI_TEXT | HDI_WIDTH; hdItem.pszText = buffA; - hdItem.cchTextMax = sizeof(buffA)/sizeof(buffA[0]); + hdItem.cchTextMax = ARRAY_SIZE(buffA); retVal = SendMessageA(hChild, HDM_GETITEMA, 0, (LPARAM) &hdItem); ok(retVal == TRUE, "Getting the 1st header item should return TRUE, got %d\n", retVal); diff --git a/modules/rostests/winetests/comctl32/imagelist.c b/modules/rostests/winetests/comctl32/imagelist.c index bcd62c9e9c..8064a2b98e 100644 --- a/modules/rostests/winetests/comctl32/imagelist.c +++ b/modules/rostests/winetests/comctl32/imagelist.c @@ -922,7 +922,6 @@ static void check_ilhead_data(const ILHEAD *ilh, INT cx, INT cy, INT cur, INT ma } else { - grow = (WORD)(grow + 3) & ~3; ok(ilh->cMaxImage == max, "wrong cMaxImage %d (expected %d)\n", ilh->cMaxImage, max); ok(ilh->cGrow == grow_aligned, "Unexpected cGrow %d, expected %d\n", ilh->cGrow, grow_aligned); } @@ -1100,7 +1099,7 @@ static void image_list_init(HIMAGELIST himl, INT grow) check_iml_data(himl, BMP_CX, BMP_CX, 0, 2, grow, ILC_COLOR24, "total 0"); - for (i = 0; i < sizeof(td)/sizeof(td[0]); i++) + for (i = 0; i < ARRAY_SIZE(td); i++) { image_list_add_bitmap(himl, td[i].grey, i + 1); check_iml_data(himl, td[i].cx, td[i].cy, td[i].cur, td[i].max, grow, td[i].bpp, td[i].comment); @@ -2029,7 +2028,11 @@ static void check_color_table(const char *name, HDC hdc, HIMAGELIST himl, UINT i { IMAGEINFO info; INT ret; +#ifdef __REACTOS__ char bmi_buffer[FIELD_OFFSET(BITMAPINFO, bmiColors) + 256 * sizeof(RGBQUAD)]; +#else + char bmi_buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )]; +#endif BITMAPINFO *bmi = (BITMAPINFO *)bmi_buffer; int i, depth = ilc & 0xfe; @@ -2061,7 +2064,11 @@ static void check_color_table(const char *name, HDC hdc, HIMAGELIST himl, UINT i static void get_default_color_table(HDC hdc, int bpp, RGBQUAD *table) { +#ifdef __REACTOS__ char bmi_buffer[FIELD_OFFSET(BITMAPINFO, bmiColors) + 256 * sizeof(RGBQUAD)]; +#else + char bmi_buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )]; +#endif BITMAPINFO *bmi = (BITMAPINFO *)bmi_buffer; HBITMAP tmp; int i; @@ -2109,7 +2116,11 @@ static void test_color_table(UINT ilc) { HIMAGELIST himl; INT ret; +#ifdef __REACTOS__ char bmi_buffer[FIELD_OFFSET(BITMAPINFO, bmiColors) + 256 * sizeof(RGBQUAD)]; +#else + char bmi_buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )]; +#endif BITMAPINFO *bmi = (BITMAPINFO *)bmi_buffer; HDC hdc = CreateCompatibleDC(0); HBITMAP dib4, dib8, dib32; diff --git a/modules/rostests/winetests/comctl32/ipaddress.c b/modules/rostests/winetests/comctl32/ipaddress.c index 52581899a7..cc79b7bf11 100644 --- a/modules/rostests/winetests/comctl32/ipaddress.c +++ b/modules/rostests/winetests/comctl32/ipaddress.c @@ -50,12 +50,12 @@ static void test_get_set_text(void) } /* check text just after creation */ - r = GetWindowTextA(hwnd, ip, sizeof(ip)/sizeof(CHAR)); + r = GetWindowTextA(hwnd, ip, ARRAY_SIZE(ip)); expect(7, r); ok(strcmp(ip, "0.0.0.0") == 0, "Expected null IP address, got %s\n", ip); SendMessageA(hwnd, IPM_SETADDRESS, 0, MAKEIPADDRESS(127, 0, 0, 1)); - r = GetWindowTextA(hwnd, ip, sizeof(ip)/sizeof(CHAR)); + r = GetWindowTextA(hwnd, ip, ARRAY_SIZE(ip)); expect(9, r); ok(strcmp(ip, "127.0.0.1") == 0, "Expected 127.0.0.1, got %s\n", ip); diff --git a/modules/rostests/winetests/comctl32/listbox.c b/modules/rostests/winetests/comctl32/listbox.c index 1513bea7f5..e789483983 100644 --- a/modules/rostests/winetests/comctl32/listbox.c +++ b/modules/rostests/winetests/comctl32/listbox.c @@ -30,6 +30,52 @@ #include "wine/heap.h" #include "wine/test.h" #include "v6util.h" +#include "msg.h" + +enum seq_index +{ + LB_SEQ_INDEX, + PARENT_SEQ_INDEX, + NUM_MSG_SEQUENCES +}; + +static struct msg_sequence *sequences[NUM_MSG_SEQUENCES]; + +/* encoded MEASUREITEMSTRUCT into a WPARAM */ +typedef struct +{ + union + { + struct + { + UINT CtlType : 4; + UINT CtlID : 4; + UINT itemID : 4; + UINT wParam : 20; + } item; + WPARAM wp; + } u; +} MEASURE_ITEM_STRUCT; + +static unsigned hash_Ly_W(const WCHAR *str) +{ + unsigned hash = 0; + + for (; *str; str++) + hash = hash * 1664525u + (unsigned char)(*str) + 1013904223u; + + return hash; +} + +static unsigned hash_Ly(const char *str) +{ + unsigned hash = 0; + + for (; *str; str++) + hash = hash * 1664525u + (unsigned char)(*str) + 1013904223u; + + return hash; +} static const char * const strings[4] = { "First added", @@ -43,22 +89,53 @@ static const char * const strings[4] = { static const char BAD_EXTENSION[] = "*.badtxt"; -static int strcmp_aw(LPCWSTR strw, const char *stra) +#define ID_LISTBOX 1 + +static LRESULT WINAPI listbox_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { - WCHAR buf[1024]; + WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA); + static LONG defwndproc_counter = 0; + struct message msg = { 0 }; + LRESULT ret; + + switch (message) + { + case WM_SIZE: + case WM_GETTEXT: + case WM_PAINT: + case WM_ERASEBKGND: + case WM_WINDOWPOSCHANGING: + case WM_WINDOWPOSCHANGED: + case WM_NCCALCSIZE: + case WM_NCPAINT: + case WM_NCHITTEST: + case WM_DEVICECHANGE: + break; + + default: + msg.message = message; + msg.flags = sent|wparam|lparam; + if (defwndproc_counter) msg.flags |= defwinproc; + msg.wParam = wParam; + msg.lParam = lParam; + add_message(sequences, LB_SEQ_INDEX, &msg); + } + + defwndproc_counter++; + ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam); + defwndproc_counter--; - if (!stra) return 1; - MultiByteToWideChar(CP_ACP, 0, stra, -1, buf, sizeof(buf)/sizeof(WCHAR)); - return lstrcmpW(strw, buf); + return ret; } static HWND create_listbox(DWORD add_style, HWND parent) { INT_PTR ctl_id = 0; + WNDPROC oldproc; HWND handle; if (parent) - ctl_id=1; + ctl_id = ID_LISTBOX; handle = CreateWindowA(WC_LISTBOXA, "TestList", (LBS_STANDARD & ~LBS_SORT) | add_style, 0, 0, 100, 100, parent, (HMENU)ctl_id, NULL, 0); @@ -69,6 +146,9 @@ static HWND create_listbox(DWORD add_style, HWND parent) SendMessageA(handle, LB_ADDSTRING, 0, (LPARAM) strings[2]); SendMessageA(handle, LB_ADDSTRING, 0, (LPARAM) strings[3]); + oldproc = (WNDPROC)SetWindowLongPtrA(handle, GWLP_WNDPROC, (LONG_PTR)listbox_wnd_proc); + SetWindowLongPtrA(handle, GWLP_USERDATA, (LONG_PTR)oldproc); + return handle; } @@ -84,7 +164,6 @@ struct listbox_stat struct listbox_test { - struct listbox_prop prop; struct listbox_stat init, init_todo; struct listbox_stat click, click_todo; struct listbox_stat step, step_todo; @@ -117,8 +196,7 @@ static void keypress(HWND handle, WPARAM keycode, BYTE scancode, BOOL extended) #define listbox_field_ok(t, s, f, got) \ ok (t.s.f==got.f, "style %#x, step " #s ", field " #f \ - ": expected %d, got %d\n", (unsigned int)t.prop.add_style, \ - t.s.f, got.f) + ": expected %d, got %d\n", style, t.s.f, got.f) #define listbox_todo_field_ok(t, s, f, got) \ todo_wine_if (t.s##_todo.f) { listbox_field_ok(t, s, f, got); } @@ -129,12 +207,23 @@ static void keypress(HWND handle, WPARAM keycode, BYTE scancode, BOOL extended) listbox_todo_field_ok(t, s, caret, got); \ listbox_todo_field_ok(t, s, selcount, got) -static void run_test(const struct listbox_test test) +static void run_test(DWORD style, const struct listbox_test test) { + static const struct message delete_seq[] = + { + { LB_DELETESTRING, sent|wparam|lparam, 0, 0 }, + { LB_DELETESTRING, sent|wparam|lparam, 0, 0 }, + { LB_DELETESTRING, sent|wparam|lparam, 0, 0 }, + { LB_DELETESTRING, sent|wparam|lparam, 0, 0 }, + { LB_RESETCONTENT, sent|wparam|lparam|defwinproc, 0, 0 }, + { 0 } + }; struct listbox_stat answer; - HWND hLB=create_listbox (test.prop.add_style, 0); + int i, res, count; RECT second_item; - int i, res; + HWND hLB; + + hLB = create_listbox (style, 0); listbox_query (hLB, &answer); listbox_ok (test, init, answer); @@ -152,13 +241,13 @@ static void run_test(const struct listbox_test test) DestroyWindow(hLB); - hLB = create_listbox(test.prop.add_style, 0); + hLB = create_listbox(style, 0); SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, 2)); listbox_query(hLB, &answer); listbox_ok(test, sel, answer); - for (i = 0; i < 4; i++) + for (i = 0; i < 4 && !(style & LBS_NODATA); i++) { DWORD size = SendMessageA(hLB, LB_GETTEXTLEN, i, 0); int resA, resW; @@ -171,13 +260,9 @@ static void run_test(const struct listbox_test test) txtw = heap_alloc_zero((size + 1) * sizeof(*txtw)); resW = SendMessageW(hLB, LB_GETTEXT, i, (LPARAM)txtw); - if (resA != resW) - trace("SendMessageW(LB_GETTEXT) not supported on this platform (resA=%d resW=%d), skipping...\n", resA, resW); - else - { - WideCharToMultiByte(CP_ACP, 0, txtw, -1, txt, size, NULL, NULL); - ok(!strcmp (txt, strings[i]), "returned string for item %d does not match %s vs %s\n", i, txt, strings[i]); - } + ok(resA == resW, "Unexpected text length.\n"); + WideCharToMultiByte(CP_ACP, 0, txtw, -1, txt, size, NULL, NULL); + ok(!strcmp (txt, strings[i]), "Unexpected string for item %d, %s vs %s.\n", i, txt, strings[i]); heap_free(txtw); heap_free(txt); @@ -190,8 +275,17 @@ static void run_test(const struct listbox_test test) ok(res == LB_ERR, "Expected LB_ERR items, got %d\n", res); res = SendMessageA(hLB, LB_DELETESTRING, 4, 0); ok(res == LB_ERR, "Expected LB_ERR items, got %d\n", res); - res = SendMessageA(hLB, LB_GETCOUNT, 0, 0); - ok(res == 4, "Expected 4 items, got %d\n", res); + count = SendMessageA(hLB, LB_GETCOUNT, 0, 0); + ok(count == 4, "Unexpected item count %d.\n", count); + + /* Emptying listbox sends a LB_RESETCONTENT to itself. */ + flush_sequence(sequences, LB_SEQ_INDEX); + for (i = count; i--;) + { + res = SendMessageA(hLB, LB_DELETESTRING, 0, 0); + ok(res == i, "Unexpected return value %d.\n", res); + } + ok_sequence(sequences, LB_SEQ_INDEX, delete_seq, "Emptying listbox", FALSE); DestroyWindow(hLB); } @@ -234,41 +328,85 @@ static void test_item_height(void) static int got_selchange; -static LRESULT WINAPI main_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +static LRESULT WINAPI main_window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + static LONG defwndproc_counter = 0; + struct message m = { 0 }; + LRESULT ret; + + m.message = msg; + m.flags = sent|wparam|lparam; + if (defwndproc_counter) m.flags |= defwinproc; + m.wParam = wParam; + m.lParam = lParam; + switch (msg) { case WM_MEASUREITEM: { - DWORD style = GetWindowLongA(GetWindow(hwnd, GW_CHILD), GWL_STYLE); - MEASUREITEMSTRUCT *mi = (void*)lparam; + MEASUREITEMSTRUCT *mis = (void *)lParam; + BOOL is_unicode_data = FALSE; + MEASURE_ITEM_STRUCT mi; - ok(wparam == mi->CtlID, "got wParam=%08lx, expected %08x\n", wparam, mi->CtlID); - ok(mi->CtlType == ODT_LISTBOX, "mi->CtlType = %u\n", mi->CtlType); - ok(mi->CtlID == 1, "mi->CtlID = %u\n", mi->CtlID); - ok(mi->itemHeight, "mi->itemHeight = 0\n"); + if (mis->CtlType == ODT_LISTBOX) + { + HWND ctrl = GetDlgItem(hwnd, mis->CtlID); + is_unicode_data = GetWindowLongA(ctrl, GWL_STYLE) & LBS_HASSTRINGS; + } - if (mi->itemID > 4 || style & LBS_OWNERDRAWFIXED) - break; + mi.u.wp = 0; + mi.u.item.CtlType = mis->CtlType; + mi.u.item.CtlID = mis->CtlID; + mi.u.item.itemID = mis->itemID; + mi.u.item.wParam = wParam; + + m.wParam = mi.u.wp; + if (is_unicode_data) + m.lParam = mis->itemData ? hash_Ly_W((const WCHAR *)mis->itemData) : 0; + else + m.lParam = mis->itemData ? hash_Ly((const char *)mis->itemData) : 0; + add_message(sequences, PARENT_SEQ_INDEX, &m); + + ok(wParam == mis->CtlID, "got wParam=%08lx, expected %08x\n", wParam, mis->CtlID); + ok(mis->CtlType == ODT_LISTBOX, "mi->CtlType = %u\n", mis->CtlType); + ok(mis->CtlID == 1, "mi->CtlID = %u\n", mis->CtlID); + ok(mis->itemHeight, "mi->itemHeight = 0\n"); - if (style & LBS_HASSTRINGS) + break; + } + case WM_COMPAREITEM: + { + COMPAREITEMSTRUCT *cis = (COMPAREITEMSTRUCT *)lParam; + HWND ctrl = GetDlgItem(hwnd, cis->CtlID); + BOOL is_unicode_data = TRUE; + + ok(wParam == cis->CtlID, "expected %#x, got %#lx\n", cis->CtlID, wParam); + ok(cis->hwndItem == ctrl, "expected %p, got %p\n", ctrl, cis->hwndItem); + ok((int)cis->itemID1 >= 0, "expected >= 0, got %d\n", cis->itemID1); + ok((int)cis->itemID2 == -1, "expected -1, got %d\n", cis->itemID2); + + if (cis->CtlType == ODT_LISTBOX) + is_unicode_data = GetWindowLongA(ctrl, GWL_STYLE) & LBS_HASSTRINGS; + + if (is_unicode_data) { - ok(!strcmp_aw((WCHAR*)mi->itemData, strings[mi->itemID]), - "mi->itemData = %s (%d)\n", wine_dbgstr_w((WCHAR*)mi->itemData), mi->itemID); + m.wParam = cis->itemData1 ? hash_Ly_W((const WCHAR *)cis->itemData1) : 0; + m.lParam = cis->itemData2 ? hash_Ly_W((const WCHAR *)cis->itemData2) : 0; } else { - ok((void*)mi->itemData == strings[mi->itemID], - "mi->itemData = %08lx, expected %p\n", mi->itemData, strings[mi->itemID]); + m.wParam = cis->itemData1 ? hash_Ly((const char *)cis->itemData1) : 0; + m.lParam = cis->itemData2 ? hash_Ly((const char *)cis->itemData2) : 0; } + add_message(sequences, PARENT_SEQ_INDEX, &m); break; } case WM_DRAWITEM: { RECT rc_item, rc_client, rc_clip; - DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lparam; + DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam; - ok(wparam == dis->CtlID, "got wParam=%08lx instead of %08x\n", wparam, dis->CtlID); + ok(wParam == dis->CtlID, "got wParam=%08lx instead of %08x\n", wParam, dis->CtlID); ok(dis->CtlType == ODT_LISTBOX, "wrong CtlType %04x\n", dis->CtlType); GetClientRect(dis->hwndItem, &rc_client); @@ -284,14 +422,18 @@ static LRESULT WINAPI main_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARA } case WM_COMMAND: - if (HIWORD( wparam ) == LBN_SELCHANGE) got_selchange++; + if (HIWORD( wParam ) == LBN_SELCHANGE) got_selchange++; break; default: break; } - return DefWindowProcA(hwnd, msg, wparam, lparam); + defwndproc_counter++; + ret = DefWindowProcA(hwnd, msg, wParam, lParam); + defwndproc_counter--; + + return msg == WM_COMPAREITEM ? -1 : ret; } static HWND create_parent( void ) @@ -320,33 +462,72 @@ static HWND create_parent( void ) static void test_ownerdraw(void) { + static const DWORD styles[] = + { + 0, + LBS_NODATA + }; HWND parent, hLB; INT ret; RECT rc; + UINT i; parent = create_parent(); ok(parent != NULL, "Failed to create parent window.\n"); - hLB = create_listbox(LBS_OWNERDRAWFIXED | WS_CHILD | WS_VISIBLE, parent); - ok(hLB != NULL, "Failed to create listbox window.\n"); + for (i = 0; i < ARRAY_SIZE(styles); i++) + { + hLB = create_listbox(LBS_OWNERDRAWFIXED | WS_CHILD | WS_VISIBLE | styles[i], parent); + ok(hLB != NULL, "Failed to create listbox window.\n"); - SetForegroundWindow(hLB); - UpdateWindow(hLB); + SetForegroundWindow(hLB); + UpdateWindow(hLB); - /* make height short enough */ - SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc); - SetWindowPos(hLB, 0, 0, 0, 100, rc.bottom - rc.top + 1, SWP_NOZORDER | SWP_NOMOVE); + /* make height short enough */ + SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc); + SetWindowPos(hLB, 0, 0, 0, 100, rc.bottom - rc.top + 1, SWP_NOZORDER | SWP_NOMOVE); - /* make 0 item invisible */ - SendMessageA(hLB, LB_SETTOPINDEX, 1, 0); - ret = SendMessageA(hLB, LB_GETTOPINDEX, 0, 0); - ok(ret == 1, "wrong top index %d\n", ret); + /* make 0 item invisible */ + SendMessageA(hLB, LB_SETTOPINDEX, 1, 0); + ret = SendMessageA(hLB, LB_GETTOPINDEX, 0, 0); + ok(ret == 1, "wrong top index %d\n", ret); - SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc); - ok(!IsRectEmpty(&rc), "empty item rect\n"); - ok(rc.top < 0, "rc.top is not negative (%d)\n", rc.top); + SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc); + ok(!IsRectEmpty(&rc), "empty item rect\n"); + ok(rc.top < 0, "rc.top is not negative (%d)\n", rc.top); + + DestroyWindow(hLB); + + /* Both FIXED and VARIABLE, FIXED should override VARIABLE. */ + hLB = CreateWindowA(WC_LISTBOXA, "TestList", LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE | styles[i], + 0, 0, 100, 100, NULL, NULL, NULL, 0); + ok(hLB != NULL, "last error 0x%08x\n", GetLastError()); + + ok(GetWindowLongA(hLB, GWL_STYLE) & LBS_OWNERDRAWVARIABLE, "Unexpected window style.\n"); + + ret = SendMessageA(hLB, LB_INSERTSTRING, -1, 0); + ok(ret == 0, "Unexpected return value %d.\n", ret); + ret = SendMessageA(hLB, LB_INSERTSTRING, -1, 0); + ok(ret == 1, "Unexpected return value %d.\n", ret); + + ret = SendMessageA(hLB, LB_SETITEMHEIGHT, 0, 13); + ok(ret == LB_OKAY, "Failed to set item height, %d.\n", ret); + + ret = SendMessageA(hLB, LB_GETITEMHEIGHT, 0, 0); + ok(ret == 13, "Unexpected item height %d.\n", ret); + + ret = SendMessageA(hLB, LB_SETITEMHEIGHT, 1, 42); + ok(ret == LB_OKAY, "Failed to set item height, %d.\n", ret); + + ret = SendMessageA(hLB, LB_GETITEMHEIGHT, 0, 0); + ok(ret == 42, "Unexpected item height %d.\n", ret); + + ret = SendMessageA(hLB, LB_GETITEMHEIGHT, 1, 0); + ok(ret == 42, "Unexpected item height %d.\n", ret); + + DestroyWindow (hLB); + } - DestroyWindow(hLB); DestroyWindow(parent); } @@ -447,19 +628,123 @@ static void test_LB_SETCURSEL(void) SendMessageA(hLB, LB_SETITEMHEIGHT, 0, 32); + ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0); + ok(ret == -1, "Unexpected anchor index %d.\n", ret); + ret = SendMessageA(hLB, LB_SETCURSEL, 2, 0); ok(ret == 2, "LB_SETCURSEL returned %d instead of 2\n", ret); ret = GetScrollPos(hLB, SB_VERT); ok(ret == 0, "expected vscroll 0, got %d\n", ret); + ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0); + ok(ret == -1, "Unexpected anchor index %d.\n", ret); + ret = SendMessageA(hLB, LB_SETCURSEL, 3, 0); ok(ret == 3, "LB_SETCURSEL returned %d instead of 3\n", ret); ret = GetScrollPos(hLB, SB_VERT); ok(ret == 1, "expected vscroll 1, got %d\n", ret); + ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0); + ok(ret == -1, "Unexpected anchor index %d.\n", ret); + + DestroyWindow(hLB); + + hLB = create_listbox(0, 0); + ok(hLB != NULL, "Failed to create ListBox window.\n"); + + ret = SendMessageA(hLB, LB_SETCURSEL, 1, 0); + ok(ret == 1, "Unexpected return value %d.\n", ret); + + ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0); + ok(ret == -1, "Unexpected anchor index %d.\n", ret); + + DestroyWindow(hLB); + + /* LBS_EXTENDEDSEL */ + hLB = create_listbox(LBS_EXTENDEDSEL, 0); + ok(hLB != NULL, "Failed to create listbox.\n"); + + ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0); + ok(ret == -1, "Unexpected anchor index %d.\n", ret); + + ret = SendMessageA(hLB, LB_SETCURSEL, 2, 0); + ok(ret == -1, "LB_SETCURSEL returned %d instead of 2\n", ret); + + ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0); + ok(ret == -1, "Unexpected anchor index %d.\n", ret); + + DestroyWindow(hLB); + + /* LBS_MULTIPLESEL */ + hLB = create_listbox(LBS_MULTIPLESEL, 0); + ok(hLB != NULL, "Failed to create listbox.\n"); + + ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0); + ok(ret == -1, "Unexpected anchor index %d.\n", ret); + + ret = SendMessageA(hLB, LB_SETCURSEL, 2, 0); + ok(ret == -1, "LB_SETCURSEL returned %d instead of 2\n", ret); + + ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0); + ok(ret == -1, "Unexpected anchor index %d.\n", ret); + DestroyWindow(hLB); } +static void test_LB_SETSEL(void) +{ + HWND list; + int ret; + + /* LBS_EXTENDEDSEL */ + list = create_listbox(LBS_EXTENDEDSEL, 0); + ok(list != NULL, "Failed to create ListBox window.\n"); + + ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0); + ok(ret == -1, "Unexpected anchor index %d.\n", ret); + + ret = SendMessageA(list, LB_SETSEL, TRUE, 0); + ok(ret == 0, "Unexpected return value %d.\n", ret); + ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0); + ok(ret == 0, "Unexpected anchor index %d.\n", ret); + + ret = SendMessageA(list, LB_SETSEL, TRUE, 1); + ok(ret == 0, "Unexpected return value %d.\n", ret); + ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0); + ok(ret == 1, "Unexpected anchor index %d.\n", ret); + + ret = SendMessageA(list, LB_SETSEL, FALSE, 1); + ok(ret == 0, "Unexpected return value %d.\n", ret); + ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0); + ok(ret == 1, "Unexpected anchor index %d.\n", ret); + + DestroyWindow(list); + + /* LBS_MULTIPLESEL */ + list = create_listbox(LBS_MULTIPLESEL, 0); + ok(list != NULL, "Failed to create ListBox window.\n"); + + ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0); + ok(ret == -1, "Unexpected anchor index %d.\n", ret); + + ret = SendMessageA(list, LB_SETSEL, TRUE, 0); + ok(ret == 0, "Unexpected return value %d.\n", ret); + ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0); + ok(ret == 0, "Unexpected anchor index %d.\n", ret); + + ret = SendMessageA(list, LB_SETSEL, TRUE, 1); + ok(ret == 0, "Unexpected return value %d.\n", ret); + ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0); + ok(ret == 1, "Unexpected anchor index %d.\n", ret); + + ret = SendMessageA(list, LB_SETSEL, FALSE, 1); + ok(ret == 0, "Unexpected return value %d.\n", ret); + ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0); + ok(ret == 1, "Unexpected anchor index %d.\n", ret); + + DestroyWindow(list); +} + static void test_listbox_height(void) { HWND hList; @@ -603,6 +888,7 @@ static void test_listbox_item_data(void) static void test_listbox_LB_DIR(void) { + char path[MAX_PATH], curdir[MAX_PATH]; HWND hList; int res, itemCount; int itemCount_justFiles; @@ -615,6 +901,16 @@ static void test_listbox_LB_DIR(void) char driveletter; const char *wildcard = "*"; HANDLE file; + BOOL ret; + + GetCurrentDirectoryA(ARRAY_SIZE(curdir), curdir); + + GetTempPathA(ARRAY_SIZE(path), path); + ret = SetCurrentDirectoryA(path); + ok(ret, "Failed to set current directory.\n"); + + ret = CreateDirectoryA("lb_dir_test", NULL); + ok(ret, "Failed to create test directory.\n"); file = CreateFileA( "wtest1.tmp.c", GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL ); ok(file != INVALID_HANDLE_VALUE, "Error creating the test file: %d\n", GetLastError()); @@ -945,11 +1241,11 @@ static void test_listbox_LB_DIR(void) itemCount, itemCount_allDirs); ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, *) returned incorrect index!\n"); - if (itemCount && GetCurrentDirectoryA( MAX_PATH, pathBuffer ) > 3) /* there's no [..] in drive root */ + if (itemCount) { memset(pathBuffer, 0, MAX_PATH); SendMessageA(hList, LB_GETTEXT, 0, (LPARAM)pathBuffer); - ok( !strcmp(pathBuffer, "[..]"), "First element is not [..]\n"); + ok( !strcmp(pathBuffer, "[..]"), "First element is %s, not [..]\n", pathBuffer); } /* This tests behavior when no files match the wildcard */ @@ -1034,6 +1330,9 @@ static void test_listbox_LB_DIR(void) DestroyWindow(hList); DeleteFileA( "wtest1.tmp.c" ); + RemoveDirectoryA("lb_dir_test"); + + SetCurrentDirectoryA(curdir); } static HWND g_listBox; @@ -1533,7 +1832,13 @@ static void test_listbox_dlgdir(void) static void test_set_count( void ) { + static const DWORD styles[] = + { + LBS_OWNERDRAWFIXED, + LBS_HASSTRINGS, + }; HWND parent, listbox; + unsigned int i; LONG ret; RECT r; @@ -1563,37 +1868,39 @@ static void test_set_count( void ) ok( !IsRectEmpty( &r ), "got empty rect\n"); DestroyWindow( listbox ); - DestroyWindow( parent ); -} -static int lb_getlistboxinfo; + for (i = 0; i < ARRAY_SIZE(styles); ++i) + { + listbox = create_listbox( styles[i] | WS_CHILD | WS_VISIBLE, parent ); -static LRESULT WINAPI listbox_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA); + SetLastError( 0xdeadbeef ); + ret = SendMessageA( listbox, LB_SETCOUNT, 100, 0 ); + ok( ret == LB_ERR, "expected %d, got %d\n", LB_ERR, ret ); + ok( GetLastError() == 0xdeadbeef, "Unexpected error %d.\n", GetLastError() ); - if (message == LB_GETLISTBOXINFO) - lb_getlistboxinfo++; + DestroyWindow( listbox ); + } - return CallWindowProcA(oldproc, hwnd, message, wParam, lParam); + DestroyWindow( parent ); } static void test_GetListBoxInfo(void) { + static const struct message getlistboxinfo_seq[] = + { + { LB_GETLISTBOXINFO, sent }, + { 0 } + }; HWND listbox, parent; - WNDPROC oldproc; DWORD ret; parent = create_parent(); listbox = create_listbox(WS_CHILD | WS_VISIBLE, parent); - oldproc = (WNDPROC)SetWindowLongPtrA(listbox, GWLP_WNDPROC, (LONG_PTR)listbox_subclass_proc); - SetWindowLongPtrA(listbox, GWLP_USERDATA, (LONG_PTR)oldproc); - - lb_getlistboxinfo = 0; + flush_sequences(sequences, NUM_MSG_SEQUENCES); ret = GetListBoxInfo(listbox); ok(ret > 0, "got %d\n", ret); - ok(lb_getlistboxinfo == 1, "got %d\n", lb_getlistboxinfo); + ok_sequence(sequences, LB_SEQ_INDEX, getlistboxinfo_seq, "GetListBoxInfo()", FALSE); DestroyWindow(listbox); DestroyWindow(parent); @@ -1805,76 +2112,101 @@ static void test_listbox(void) { static const struct listbox_test SS = /* {add_style} */ - {{0}, - {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, + {{LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, { 1, 1, 1, LB_ERR}, {0,0,0,0}, { 2, 2, 2, LB_ERR}, {0,0,0,0}, {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}}; /* {selected, anchor, caret, selcount}{TODO fields} */ static const struct listbox_test SS_NS = - {{LBS_NOSEL}, - {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, + {{LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, { 1, 1, 1, LB_ERR}, {0,0,0,0}, { 2, 2, 2, LB_ERR}, {0,0,0,0}, {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}}; static const struct listbox_test MS = - {{LBS_MULTIPLESEL}, - { 0, LB_ERR, 0, 0}, {0,0,0,0}, + {{ 0, LB_ERR, 0, 0}, {0,0,0,0}, { 1, 1, 1, 1}, {0,0,0,0}, { 2, 1, 2, 1}, {0,0,0,0}, { 0, LB_ERR, 0, 2}, {0,0,0,0}}; static const struct listbox_test MS_NS = - {{LBS_MULTIPLESEL | LBS_NOSEL}, - {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, + {{LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, { 1, 1, 1, LB_ERR}, {0,0,0,0}, { 2, 2, 2, LB_ERR}, {0,0,0,0}, {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}}; static const struct listbox_test ES = - {{LBS_EXTENDEDSEL}, - { 0, LB_ERR, 0, 0}, {0,0,0,0}, + {{ 0, LB_ERR, 0, 0}, {0,0,0,0}, { 1, 1, 1, 1}, {0,0,0,0}, { 2, 2, 2, 1}, {0,0,0,0}, { 0, LB_ERR, 0, 2}, {0,0,0,0}}; static const struct listbox_test ES_NS = - {{LBS_EXTENDEDSEL | LBS_NOSEL}, - {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, + {{LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, { 1, 1, 1, LB_ERR}, {0,0,0,0}, { 2, 2, 2, LB_ERR}, {0,0,0,0}, {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}}; static const struct listbox_test EMS = - {{LBS_EXTENDEDSEL | LBS_MULTIPLESEL}, - { 0, LB_ERR, 0, 0}, {0,0,0,0}, + {{ 0, LB_ERR, 0, 0}, {0,0,0,0}, { 1, 1, 1, 1}, {0,0,0,0}, { 2, 2, 2, 1}, {0,0,0,0}, { 0, LB_ERR, 0, 2}, {0,0,0,0}}; static const struct listbox_test EMS_NS = - {{LBS_EXTENDEDSEL | LBS_MULTIPLESEL | LBS_NOSEL}, - {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, + {{LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, { 1, 1, 1, LB_ERR}, {0,0,0,0}, { 2, 2, 2, LB_ERR}, {0,0,0,0}, {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}}; - run_test(SS); - run_test(SS_NS); - run_test(MS); - run_test(MS_NS); - run_test(ES); - run_test(ES_NS); - run_test(EMS); - run_test(EMS_NS); + run_test(0, SS); + run_test(LBS_NOSEL, SS_NS); + run_test(LBS_MULTIPLESEL, MS); + run_test(LBS_MULTIPLESEL | LBS_NOSEL, MS_NS); + run_test(LBS_EXTENDEDSEL, ES); + run_test(LBS_EXTENDEDSEL | LBS_NOSEL, ES_NS); + run_test(LBS_EXTENDEDSEL | LBS_MULTIPLESEL, EMS); + run_test(LBS_EXTENDEDSEL | LBS_MULTIPLESEL | LBS_NOSEL, EMS_NS); + + run_test(LBS_NODATA | LBS_OWNERDRAWFIXED, SS); + run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_NOSEL, SS_NS); + run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_MULTIPLESEL, MS); + run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_MULTIPLESEL | LBS_NOSEL, MS_NS); + run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL, ES); + run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL | LBS_NOSEL, ES_NS); + run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL | LBS_MULTIPLESEL, EMS); + run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL | LBS_MULTIPLESEL | LBS_NOSEL, EMS_NS); } +static const struct message lb_addstring_ownerdraw_parent_seq[] = +{ + { WM_MEASUREITEM, sent|wparam|lparam, 0x1012, 0xf30604ed }, + { WM_MEASUREITEM, sent|wparam|lparam, 0x1112, 0xf30604ee }, + { WM_MEASUREITEM, sent|wparam|lparam, 0x1212, 0xf30604ef }, + { 0 } +}; + +static const struct message lb_addstring_sort_parent_seq[] = +{ + { WM_MEASUREITEM, sent|wparam|lparam, 0x1012, 0xf30604ed }, + { WM_COMPAREITEM, sent|wparam|lparam, 0xf30604ed, 0xf30604ee }, + { WM_MEASUREITEM, sent|wparam|lparam, 0x1112, 0xf30604ee }, + { WM_COMPAREITEM, sent|wparam|lparam, 0xf30604ed, 0xf30604ef }, + { WM_COMPAREITEM, sent|wparam|lparam, 0xf30604ee, 0xf30604ef }, + { WM_MEASUREITEM, sent|wparam|lparam, 0x1212, 0xf30604ef }, + { 0 } +}; + +static const struct message empty_seq[] = +{ + { 0 } +}; + static void test_WM_MEASUREITEM(void) { HWND parent, listbox; - LRESULT data; + LRESULT data, ret; parent = create_parent(); listbox = create_listbox(WS_CHILD | LBS_OWNERDRAWVARIABLE, parent); @@ -1888,6 +2220,187 @@ static void test_WM_MEASUREITEM(void) data = SendMessageA(listbox, LB_GETITEMDATA, 0, 0); ok(!data, "data = %08lx\n", data); + + /* LBS_HASSTRINGS */ + parent = create_parent(); + listbox = CreateWindowExA(WS_EX_NOPARENTNOTIFY, WC_LISTBOXA, NULL, + WS_CHILD | LBS_NOTIFY | LBS_OWNERDRAWVARIABLE | LBS_HASSTRINGS | WS_VISIBLE, + 10, 10, 80, 80, parent, (HMENU)ID_LISTBOX, 0, NULL); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 0"); + ok(ret == 0, "expected 0, got %ld\n", ret); + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 1"); + ok(ret == 1, "expected 1, got %ld\n", ret); + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 2"); + ok(ret == 2, "expected 2, got %ld\n", ret); + + ok_sequence(sequences, PARENT_SEQ_INDEX, lb_addstring_ownerdraw_parent_seq, + "LB_ADDSTRING (LBS_HASSTRINGS, ownerdraw)", FALSE); + DestroyWindow(listbox); + + /* LBS_SORT, no LBS_HASSTRINGS */ + listbox = CreateWindowExA(WS_EX_NOPARENTNOTIFY, WC_LISTBOXA, NULL, + WS_CHILD | LBS_NOTIFY | LBS_OWNERDRAWVARIABLE | LBS_SORT | WS_VISIBLE, + 10, 10, 80, 80, parent, (HMENU)ID_LISTBOX, 0, NULL); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 0"); + ok(ret == 0, "expected 0, got %ld\n", ret); + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 1"); + ok(ret == 1, "expected 1, got %ld\n", ret); + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 2"); + ok(ret == 2, "expected 2, got %ld\n", ret); + + ok_sequence(sequences, PARENT_SEQ_INDEX, lb_addstring_sort_parent_seq, "LB_ADDSTRING (LBS_SORT)", FALSE); + DestroyWindow(listbox); + + /* LBS_HASSTRINGS */ + listbox = CreateWindowExA(WS_EX_NOPARENTNOTIFY, WC_LISTBOXA, NULL, + WS_CHILD | LBS_NOTIFY | LBS_HASSTRINGS | WS_VISIBLE, + 10, 10, 80, 80, parent, (HMENU)ID_LISTBOX, 0, NULL); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 2"); + ok(ret == 0, "expected 0, got %ld\n", ret); + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 0"); + ok(ret == 1, "expected 1, got %ld\n", ret); + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 1"); + ok(ret == 2, "expected 2, got %ld\n", ret); + + ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "LB_ADDSTRING (LBS_HASSTRINGS)", FALSE); + DestroyWindow(listbox); + + /* LBS_HASSTRINGS, LBS_SORT */ + listbox = CreateWindowExA(WS_EX_NOPARENTNOTIFY, WC_LISTBOXA, NULL, + WS_CHILD | LBS_NOTIFY | LBS_HASSTRINGS | LBS_SORT | WS_VISIBLE, + 10, 10, 80, 80, parent, (HMENU)ID_LISTBOX, 0, NULL); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 2"); + ok(ret == 0, "expected 0, got %ld\n", ret); + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 0"); + ok(ret == 0, "expected 0, got %ld\n", ret); + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 1"); + ok(ret == 1, "expected 1, got %ld\n", ret); + + ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "LB_ADDSTRING (LBS_HASSTRINGS, LBS_SORT)", FALSE); + DestroyWindow(listbox); + + DestroyWindow(parent); +} + +static void test_LBS_NODATA(void) +{ + static const DWORD invalid_styles[] = + { + 0, + LBS_OWNERDRAWVARIABLE, + LBS_SORT, + LBS_HASSTRINGS, + LBS_OWNERDRAWFIXED | LBS_SORT, + LBS_OWNERDRAWFIXED | LBS_HASSTRINGS, + }; + static const UINT invalid_idx[] = { -2, 2 }; + static const UINT valid_idx[] = { 0, 1 }; + static const ULONG_PTR zero_data; + HWND listbox, parent; + INT ret, text_len; + unsigned int i; + ULONG_PTR data; + BOOL is_wow64; + + listbox = CreateWindowA(WC_LISTBOXA, "TestList", LBS_NODATA | LBS_OWNERDRAWFIXED | WS_VISIBLE, + 0, 0, 100, 100, NULL, NULL, NULL, 0); + ok(listbox != NULL, "Failed to create ListBox window.\n"); + + ret = SendMessageA(listbox, LB_INSERTSTRING, -1, 0); + ok(ret == 0, "Unexpected return value %d.\n", ret); + ret = SendMessageA(listbox, LB_INSERTSTRING, -1, 0); + ok(ret == 1, "Unexpected return value %d.\n", ret); + ret = SendMessageA(listbox, LB_GETCOUNT, 0, 0); + ok(ret == 2, "Unexpected return value %d.\n", ret); + + /* Invalid indices. */ + for (i = 0; i < ARRAY_SIZE(invalid_idx); ++i) + { + ret = SendMessageA(listbox, LB_SETITEMDATA, invalid_idx[i], 42); + ok(ret == LB_ERR, "Unexpected return value %d.\n", ret); + ret = SendMessageA(listbox, LB_GETTEXTLEN, invalid_idx[i], 0); + ok(ret == LB_ERR, "Unexpected return value %d.\n", ret); + if (ret == LB_ERR) + { + ret = SendMessageA(listbox, LB_GETTEXT, invalid_idx[i], (LPARAM)&data); + ok(ret == LB_ERR, "Unexpected return value %d.\n", ret); + } + ret = SendMessageA(listbox, LB_GETITEMDATA, invalid_idx[i], 0); + ok(ret == LB_ERR, "Unexpected return value %d.\n", ret); + } + + IsWow64Process(GetCurrentProcess(), &is_wow64); +#ifdef _WIN64 + text_len = 8; +#else + text_len = is_wow64 ? 8 : 4; +#endif + + /* Valid indices. */ + for (i = 0; i < ARRAY_SIZE(valid_idx); ++i) + { + ret = SendMessageA(listbox, LB_SETITEMDATA, valid_idx[i], 42); + ok(ret == TRUE, "Unexpected return value %d.\n", ret); + ret = SendMessageA(listbox, LB_GETTEXTLEN, valid_idx[i], 0); + todo_wine_if(is_wow64) + ok(ret == text_len, "Unexpected return value %d.\n", ret); + + memset(&data, 0xee, sizeof(data)); + ret = SendMessageA(listbox, LB_GETTEXT, valid_idx[i], (LPARAM)&data); + ok(ret == sizeof(data), "Unexpected return value %d.\n", ret); + ok(!memcmp(&data, &zero_data, sizeof(data)), "Unexpected item data.\n"); + + ret = SendMessageA(listbox, LB_GETITEMDATA, valid_idx[i], 0); + ok(ret == 0, "Unexpected return value %d.\n", ret); + } + + /* More messages that don't work with LBS_NODATA. */ + ret = SendMessageA(listbox, LB_FINDSTRING, 1, 0); + ok(ret == LB_ERR, "Unexpected return value %d.\n", ret); + ret = SendMessageA(listbox, LB_FINDSTRING, 1, 42); + ok(ret == LB_ERR, "Unexpected return value %d.\n", ret); + ret = SendMessageA(listbox, LB_FINDSTRINGEXACT, 1, 0); + ok(ret == LB_ERR, "Unexpected return value %d.\n", ret); + ret = SendMessageA(listbox, LB_FINDSTRINGEXACT, 1, 42); + ok(ret == LB_ERR, "Unexpected return value %d.\n", ret); + ret = SendMessageA(listbox, LB_SELECTSTRING, 1, 0); + ok(ret == LB_ERR, "Unexpected return value %d.\n", ret); + ret = SendMessageA(listbox, LB_SELECTSTRING, 1, 42); + ok(ret == LB_ERR, "Unexpected return value %d.\n", ret); + + DestroyWindow(listbox); + + /* Invalid window style combinations. */ + parent = create_parent(); + ok(parent != NULL, "Failed to create parent window.\n"); + + for (i = 0; i < ARRAY_SIZE(invalid_styles); ++i) + { + DWORD style; + + listbox = CreateWindowA(WC_LISTBOXA, "TestList", LBS_NODATA | WS_CHILD | invalid_styles[i], + 0, 0, 100, 100, parent, (HMENU)ID_LISTBOX, NULL, 0); + ok(listbox != NULL, "Failed to create a listbox.\n"); + + style = GetWindowLongA(listbox, GWL_STYLE); + ok((style & invalid_styles[i]) == invalid_styles[i], "%u: unexpected window styles %#x.\n", i, style); + ret = SendMessageA(listbox, LB_SETCOUNT, 100, 0); + ok(ret == LB_ERR, "%u: unexpected return value %d.\n", i, ret); + DestroyWindow(listbox); + } + DestroyWindow(parent); } @@ -1899,6 +2412,8 @@ START_TEST(listbox) if (!load_v6_module(&ctx_cookie, &hCtx)) return; + init_msg_sequences(sequences, NUM_MSG_SEQUENCES); + test_listbox(); test_item_height(); test_ownerdraw(); @@ -1914,6 +2429,8 @@ START_TEST(listbox) test_missing_lbuttonup(); test_extents(); test_WM_MEASUREITEM(); + test_LB_SETSEL(); + test_LBS_NODATA(); unload_v6_module(ctx_cookie, hCtx); } diff --git a/modules/rostests/winetests/comctl32/listview.c b/modules/rostests/winetests/comctl32/listview.c index ddeb991c81..ec289fa3f2 100644 --- a/modules/rostests/winetests/comctl32/listview.c +++ b/modules/rostests/winetests/comctl32/listview.c @@ -75,6 +75,8 @@ static BOOL g_disp_A_to_W; static NMLVDISPINFOA g_editbox_disp_info; /* when this is set focus will be tested on LVN_DELETEITEM */ static BOOL g_focus_test_LVN_DELETEITEM; +/* Whether to send WM_KILLFOCUS to the edit control during LVN_ENDLABELEDIT */ +static BOOL g_WM_KILLFOCUS_on_LVN_ENDLABELEDIT; static HWND subclass_editbox(HWND hwndListview); @@ -445,6 +447,25 @@ static const struct message parent_list_cd_seq[] = { { 0 } }; +static const struct message listview_end_label_edit[] = { + { WM_NOTIFY, sent|id, 0, 0, LVN_ENDLABELEDITA }, + { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGING}, + { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED }, + { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW }, /* XP */ + { WM_NOTIFY, sent|id, 0, 0, NM_SETFOCUS }, + { 0 } +}; + +static const struct message listview_end_label_edit_kill_focus[] = { + { WM_NOTIFY, sent|id, 0, 0, LVN_ENDLABELEDITA }, + { WM_COMMAND, sent|id|optional, 0, 0, EN_KILLFOCUS }, /* todo: not sent by wine yet */ + { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGING }, + { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED }, + { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW }, /* XP */ + { WM_NOTIFY, sent|id, 0, 0, NM_SETFOCUS }, + { 0 } +}; + static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static LONG defwndproc_counter = 0; @@ -457,6 +478,7 @@ static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LP msg.wParam = wParam; msg.lParam = lParam; if (message == WM_NOTIFY && lParam) msg.id = ((NMHDR*)lParam)->code; + if (message == WM_COMMAND) msg.id = HIWORD(wParam); /* log system messages, except for painting */ if (message < WM_USER && @@ -503,24 +525,17 @@ static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LP /* always accept new item text */ NMLVDISPINFOA *di = (NMLVDISPINFOA*)lParam; g_editbox_disp_info = *di; - trace("LVN_ENDLABELEDIT: text=%s\n", di->item.pszText ? di->item.pszText : "(null)"); /* edit control still available from this notification */ edit = (HWND)SendMessageA(((NMHDR*)lParam)->hwndFrom, LVM_GETEDITCONTROL, 0, 0); ok(IsWindow(edit), "expected valid edit control handle\n"); ok((GetWindowLongA(edit, GWL_STYLE) & ES_MULTILINE) == 0, "edit is multiline\n"); - return TRUE; - } - case LVN_BEGINSCROLL: - case LVN_ENDSCROLL: - { - NMLVSCROLL *pScroll = (NMLVSCROLL*)lParam; + if (g_WM_KILLFOCUS_on_LVN_ENDLABELEDIT) + SendMessageA(edit, WM_KILLFOCUS, 0, 0); - trace("LVN_%sSCROLL: (%d,%d)\n", pScroll->hdr.code == LVN_BEGINSCROLL ? - "BEGIN" : "END", pScroll->dx, pScroll->dy); + return TRUE; } - break; case LVN_ITEMCHANGING: { NMLISTVIEW *nmlv = (NMLISTVIEW*)lParam; @@ -1781,6 +1796,7 @@ static void test_redraw(void) HDC hdc; BOOL res; DWORD r; + RECT rect; hwnd = create_listview_control(LVS_REPORT); subclass_header(hwnd); @@ -1838,6 +1854,13 @@ static void test_redraw(void) ReleaseDC(hwndparent, hdc); + /* test setting the window style to what it already was */ + UpdateWindow(hwnd); + SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE)); + GetUpdateRect(hwnd, &rect, FALSE); + ok(rect.left == 0 && rect.top == 0 && rect.right == 0 && rect.bottom == 0, + "Expected empty update rect, got %s\n", wine_dbgstr_rect(&rect)); + DestroyWindow(hwnd); } @@ -2337,7 +2360,7 @@ static void test_multiselect(void) r = SendMessageA(hwnd, LVM_GETSELECTIONMARK, 0, 0); ok(r == 0, "got %d\n", r); - for (i = 0; i < sizeof(task_list)/sizeof(task_list[0]); i++) { + for (i = 0; i < ARRAY_SIZE(task_list); i++) { DWORD selected_count; LVITEMA item; @@ -3490,7 +3513,7 @@ static void test_norecompute(void) item.mask = LVIF_TEXT | LVIF_NORECOMPUTE; item.iItem = 0; item.pszText = buff; - item.cchTextMax = sizeof(buff)/sizeof(CHAR); + item.cchTextMax = ARRAY_SIZE(buff); res = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item); expect(TRUE, res); ok(lstrcmpA(buff, testA) == 0, "Expected (%s), got (%s)\n", testA, buff); @@ -3504,7 +3527,7 @@ static void test_norecompute(void) item.mask = LVIF_TEXT | LVIF_NORECOMPUTE; item.iItem = 1; item.pszText = buff; - item.cchTextMax = sizeof(buff)/sizeof(CHAR); + item.cchTextMax = ARRAY_SIZE(buff); flush_sequences(sequences, NUM_MSG_SEQUENCES); res = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item); @@ -3529,7 +3552,7 @@ static void test_norecompute(void) item.mask = LVIF_TEXT | LVIF_NORECOMPUTE; item.iItem = 0; item.pszText = buff; - item.cchTextMax = sizeof(buff)/sizeof(CHAR); + item.cchTextMax = ARRAY_SIZE(buff); flush_sequences(sequences, NUM_MSG_SEQUENCES); res = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item); expect(TRUE, res); @@ -4671,7 +4694,7 @@ static void test_canceleditlabel(void) ok(!IsWindow(hwndedit), "Expected edit control to be destroyed\n"); memset(&itema, 0, sizeof(itema)); itema.pszText = buff; - itema.cchTextMax = sizeof(buff)/sizeof(CHAR); + itema.cchTextMax = ARRAY_SIZE(buff); ret = SendMessageA(hwnd, LVM_GETITEMTEXTA, 0, (LPARAM)&itema); expect(5, ret); ok(strcmp(buff, test1) == 0, "Expected label text not to change\n"); @@ -5499,7 +5522,7 @@ static void test_header_notification2(void) memset(&itemW, 0, sizeof(itemW)); itemW.mask = HDI_WIDTH | HDI_ORDER | HDI_TEXT; itemW.pszText = buffer; - itemW.cchTextMax = sizeof(buffer); + itemW.cchTextMax = ARRAY_SIZE(buffer); ret = SendMessageW(header, HDM_GETITEMW, 0, (LPARAM)&itemW); expect(1, ret); @@ -5667,7 +5690,7 @@ static void test_dispinfo(void) g_disp_A_to_W = TRUE; item.pszText = (char*)buff; - item.cchTextMax = sizeof(buff)/sizeof(WCHAR); + item.cchTextMax = ARRAY_SIZE(buff); ret = SendMessageA(hwnd, LVM_GETITEMTEXTA, 0, (LPARAM)&item); ok(ret == sizeof(testA)-1, "got %d, expected 4\n", ret); g_disp_A_to_W = FALSE; @@ -6186,7 +6209,7 @@ static void test_state_image(void) }; int i; - for (i = 0; i < sizeof(styles)/sizeof(styles[0]); i++) + for (i = 0; i < ARRAY_SIZE(styles); i++) { static char text[] = "Item"; static char subtext[] = "Subitem"; @@ -6331,6 +6354,127 @@ static void test_LVSCW_AUTOSIZE(void) DestroyWindow(hwnd); } +static void test_LVN_ENDLABELEDIT(void) +{ + WCHAR text[] = {'l','a','l','a',0}; + HWND hwnd, hwndedit; + LVITEMW item = {0}; + DWORD ret; + + hwnd = create_listview_control(LVS_REPORT | LVS_EDITLABELS); + + insert_column(hwnd, 0); + + item.mask = LVIF_TEXT; + item.pszText = text; + SendMessageW(hwnd, LVM_INSERTITEMW, 0, (LPARAM)&item); + + /* Test normal editing */ + SetFocus(hwnd); + hwndedit = (HWND)SendMessageW(hwnd, LVM_EDITLABELW, 0, 0); + ok(hwndedit != NULL, "Failed to get edit control.\n"); + + ret = SendMessageA(hwndedit, WM_SETTEXT, 0, (LPARAM)"test"); + ok(ret, "Failed to set edit text.\n"); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + ret = SendMessageA(hwndedit, WM_KEYDOWN, VK_RETURN, 0); + ok_sequence(sequences, PARENT_SEQ_INDEX, listview_end_label_edit, "Label edit", FALSE); + + /* Test editing with kill focus */ + SetFocus(hwnd); + hwndedit = (HWND)SendMessageW(hwnd, LVM_EDITLABELW, 0, 0); + ok(hwndedit != NULL, "Failed to get edit control.\n"); + + ret = SendMessageA(hwndedit, WM_SETTEXT, 0, (LPARAM)"test2"); + ok(ret, "Failed to set edit text.\n"); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + g_WM_KILLFOCUS_on_LVN_ENDLABELEDIT = TRUE; + ret = SendMessageA(hwndedit, WM_KEYDOWN, VK_RETURN, 0); + g_WM_KILLFOCUS_on_LVN_ENDLABELEDIT = FALSE; + + ok_sequence(sequences, PARENT_SEQ_INDEX, listview_end_label_edit_kill_focus, + "Label edit, kill focus", FALSE); + ok(GetFocus() == hwnd, "Unexpected focused window.\n"); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + DestroyWindow(hwnd); +} + +static LRESULT CALLBACK create_item_height_wndproc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_CREATE) + return 0; + + return CallWindowProcA(listviewWndProc, hwnd, msg, wParam, lParam); +} + +static void test_LVM_GETCOUNTPERPAGE(void) +{ + static const DWORD styles[] = { LVS_ICON, LVS_LIST, LVS_REPORT, LVS_SMALLICON }; + unsigned int i, j; + WNDCLASSEXA cls; + ATOM class; + HWND hwnd; + BOOL ret; + + cls.cbSize = sizeof(WNDCLASSEXA); + ret = GetClassInfoExA(GetModuleHandleA(NULL), WC_LISTVIEWA, &cls); + ok(ret, "Failed to get class info.\n"); + listviewWndProc = cls.lpfnWndProc; + cls.lpfnWndProc = create_item_height_wndproc; + cls.lpszClassName = "CountPerPageClass"; + class = RegisterClassExA(&cls); + ok(class, "Failed to register class.\n"); + + for (i = 0; i < ARRAY_SIZE(styles); i++) + { + static char text[] = "item text"; + LVITEMA item = { 0 }; + UINT count, count2; + + hwnd = create_listview_control(styles[i]); + ok(hwnd != NULL, "Failed to create listview window.\n"); + + count = SendMessageA(hwnd, LVM_GETCOUNTPERPAGE, 0, 0); + if (styles[i] == LVS_LIST || styles[i] == LVS_REPORT) + ok(count > 0 || broken(styles[i] == LVS_LIST && count == 0), "%u: unexpected count %u.\n", i, count); + else + ok(count == 0, "%u: unexpected count %u.\n", i, count); + + for (j = 0; j < 10; j++) + { + item.mask = LVIF_TEXT; + item.pszText = text; + SendMessageA(hwnd, LVM_INSERTITEMA, 0, (LPARAM)&item); + } + + count2 = SendMessageA(hwnd, LVM_GETCOUNTPERPAGE, 0, 0); + if (styles[i] == LVS_LIST || styles[i] == LVS_REPORT) + ok(count == count2, "%u: unexpected count %u.\n", i, count2); + else + ok(count2 == 10, "%u: unexpected count %u.\n", i, count2); + + DestroyWindow(hwnd); + + hwnd = CreateWindowA("CountPerPageClass", "Test", WS_VISIBLE | styles[i], 0, 0, 100, 100, NULL, NULL, + GetModuleHandleA(NULL), 0); + ok(hwnd != NULL, "Failed to create a window.\n"); + + count = SendMessageA(hwnd, LVM_GETCOUNTPERPAGE, 0, 0); + ok(count == 0, "%u: unexpected count %u.\n", i, count); + + DestroyWindow(hwnd); + } + + ret = UnregisterClassA("CountPerPageClass", NULL); + ok(ret, "Failed to unregister test class.\n"); +} + START_TEST(listview) { ULONG_PTR ctx_cookie; @@ -6392,6 +6536,8 @@ START_TEST(listview) test_callback_mask(); test_state_image(); test_LVSCW_AUTOSIZE(); + test_LVN_ENDLABELEDIT(); + test_LVM_GETCOUNTPERPAGE(); if (!load_v6_module(&ctx_cookie, &hCtx)) { @@ -6434,6 +6580,8 @@ START_TEST(listview) test_oneclickactivate(); test_state_image(); test_LVSCW_AUTOSIZE(); + test_LVN_ENDLABELEDIT(); + test_LVM_GETCOUNTPERPAGE(); unload_v6_module(ctx_cookie, hCtx); diff --git a/modules/rostests/winetests/comctl32/misc.c b/modules/rostests/winetests/comctl32/misc.c index e18ca39453..a52744fc2e 100644 --- a/modules/rostests/winetests/comctl32/misc.c +++ b/modules/rostests/winetests/comctl32/misc.c @@ -269,8 +269,7 @@ static void test_LoadIconWithScaleDown(void) /* non-existing filename */ hr = pLoadIconMetric(NULL, nonexisting_fileW, LIM_LARGE, &icon); - todo_wine - ok(hr == HRESULT_FROM_WIN32(ERROR_RESOURCE_TYPE_NOT_FOUND), + ok(hr == HRESULT_FROM_WIN32(ERROR_RESOURCE_TYPE_NOT_FOUND) || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) /* Win7 */, "Expected HRESULT_FROM_WIN32(ERROR_RESOURCE_TYPE_NOT_FOUND), got %x\n", hr); hr = pLoadIconWithScaleDown(NULL, nonexisting_fileW, 32, 32, &icon); @@ -343,23 +342,32 @@ static void test_LoadIconWithScaleDown(void) FreeLibrary(hinst); } -static void check_class( const char *name, int must_exist, UINT style, UINT ignore ) +static void check_class( const char *name, int must_exist, UINT style, UINT ignore, BOOL v6 ) { WNDCLASSA wc; if (GetClassInfoA( 0, name, &wc )) { -todo_wine_if(strcmp(name, "Button") && - strcmp(name, "ComboBox") && - strcmp(name, "Edit") && - strcmp(name, "Static") && - strcmp(name, "ListBox") && - strcmp(name, "ComboLBox")) + char buff[64]; + HWND hwnd; + +todo_wine_if(!strcmp(name, "SysLink") && !must_exist && !v6) + ok( must_exist, "System class %s should %sexist\n", name, must_exist ? "" : "NOT " ); + if (!must_exist) return; + +todo_wine_if(!strcmp(name, "ScrollBar") || (!strcmp(name, "tooltips_class32") && v6)) ok( !(~wc.style & style & ~ignore), "System class %s is missing bits %x (%08x/%08x)\n", name, ~wc.style & style, wc.style, style ); +todo_wine_if((!strcmp(name, "tooltips_class32") && v6) || !strcmp(name, "SysLink")) ok( !(wc.style & ~style), "System class %s has extra bits %x (%08x/%08x)\n", name, wc.style & ~style, wc.style, style ); ok( !wc.hInstance, "System class %s has hInstance %p\n", name, wc.hInstance ); + + hwnd = CreateWindowA(name, 0, 0, 0, 0, 0, 0, 0, NULL, GetModuleHandleA(NULL), 0); + ok( hwnd != NULL, "Failed to create window for class %s.\n", name ); + GetClassNameA(hwnd, buff, ARRAY_SIZE(buff)); + ok( !strcmp(name, buff), "Unexpected class name %s, expected %s.\n", buff, name ); + DestroyWindow(hwnd); } else ok( !must_exist, "System class %s does not exist\n", name ); @@ -369,13 +377,40 @@ todo_wine_if(strcmp(name, "Button") && static void test_builtin_classes(void) { /* check style bits */ - check_class( "Button", 1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0 ); - check_class( "ComboBox", 1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0 ); - check_class( "Edit", 1, CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS, 0 ); - check_class( "ListBox", 1, CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS, 0 ); - check_class( "ScrollBar", 1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0 ); - check_class( "Static", 1, CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS, 0 ); - check_class( "ComboLBox", 1, CS_SAVEBITS | CS_DBLCLKS | CS_DROPSHADOW | CS_GLOBALCLASS, CS_DROPSHADOW ); + check_class( "Button", 1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0, FALSE ); + check_class( "ComboBox", 1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0, FALSE ); + check_class( "Edit", 1, CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE ); + check_class( "ListBox", 1, CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE ); + check_class( "ScrollBar", 1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0, FALSE ); + check_class( "Static", 1, CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE ); + check_class( "ComboLBox", 1, CS_SAVEBITS | CS_DBLCLKS | CS_DROPSHADOW | CS_GLOBALCLASS, CS_DROPSHADOW, FALSE ); +} + +static void test_comctl32_classes(BOOL v6) +{ + check_class(ANIMATE_CLASSA, 1, CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE); + check_class(WC_COMBOBOXEXA, 1, CS_GLOBALCLASS, 0, FALSE); + check_class(DATETIMEPICK_CLASSA, 1, CS_GLOBALCLASS, 0, FALSE); + check_class(WC_HEADERA, 1, CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE); + check_class(HOTKEY_CLASSA, 1, CS_GLOBALCLASS, 0, FALSE); + check_class(WC_IPADDRESSA, 1, CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0, FALSE); + check_class(WC_LISTVIEWA, 1, CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE); + check_class(MONTHCAL_CLASSA, 1, CS_GLOBALCLASS, 0, FALSE); + check_class(WC_NATIVEFONTCTLA, 1, CS_GLOBALCLASS, 0, FALSE); + check_class(WC_PAGESCROLLERA, 1, CS_GLOBALCLASS, 0, FALSE); + check_class(PROGRESS_CLASSA, 1, CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0, FALSE); + check_class(REBARCLASSNAMEA, 1, CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE); + check_class(STATUSCLASSNAMEA, 1, CS_DBLCLKS | CS_VREDRAW | CS_GLOBALCLASS, 0, FALSE); + check_class(WC_TABCONTROLA, 1, CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0, FALSE); + check_class(TOOLBARCLASSNAMEA, 1, CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE); + if (v6) + check_class(TOOLTIPS_CLASSA, 1, CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS | CS_DROPSHADOW, CS_SAVEBITS | CS_HREDRAW | CS_VREDRAW /* XP */, TRUE); + else + check_class(TOOLTIPS_CLASSA, 1, CS_DBLCLKS | CS_GLOBALCLASS | CS_SAVEBITS, CS_HREDRAW | CS_VREDRAW /* XP */, FALSE); + check_class(TRACKBAR_CLASSA, 1, CS_GLOBALCLASS, 0, FALSE); + check_class(WC_TREEVIEWA, 1, CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE); + check_class(UPDOWN_CLASSA, 1, CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0, FALSE); + check_class("SysLink", v6, CS_GLOBALCLASS, 0, FALSE); } START_TEST(misc) @@ -389,9 +424,12 @@ START_TEST(misc) test_GetPtrAW(); test_Alloc(); + test_comctl32_classes(FALSE); + if (!load_v6_module(&ctx_cookie, &hCtx)) return; + test_comctl32_classes(TRUE); test_builtin_classes(); test_LoadIconWithScaleDown(); diff --git a/modules/rostests/winetests/comctl32/monthcal.c b/modules/rostests/winetests/comctl32/monthcal.c index 85822a320c..11a637fb82 100644 --- a/modules/rostests/winetests/comctl32/monthcal.c +++ b/modules/rostests/winetests/comctl32/monthcal.c @@ -1216,7 +1216,7 @@ if (0) } else { title_index++; - if (sizeof(title_hits) / sizeof(title_hits[0]) <= title_index) + if (ARRAY_SIZE(title_hits) <= title_index) break; todo_wine_if(title_hits[title_index].todo) @@ -1241,8 +1241,7 @@ if (0) todo_wine ok(month_count + year_count >= 1, "Not enough month and year items\n"); - ok(r.right <= x && title_index + 1 == sizeof(title_hits) / sizeof(title_hits[0]), - "Wrong title layout\n"); + ok(r.right <= x && title_index + 1 == ARRAY_SIZE(title_hits), "Wrong title layout\n"); DestroyWindow(hwnd); } @@ -1799,7 +1798,7 @@ static void test_hittest_v6(void) mchit.iOffset = -1; mchit.iCol = mchit.iRow = -1; mchit.uHit = 0; - mchit.rc.left = mchit.rc.right = mchit.rc.top = mchit.rc.bottom = -1; + SetRect(&mchit.rc, -1, -1, -1, -1); ret = SendMessageA(hwnd, MCM_HITTEST, 0, (LPARAM)&mchit); expect_hex(MCHT_CALENDARDATE, ret); expect_hex(MCHT_CALENDARDATE, mchit.uHit); @@ -1816,7 +1815,7 @@ static void test_hittest_v6(void) mchit.iOffset = -1; mchit.iCol = mchit.iRow = -1; mchit.uHit = 0; - mchit.rc.left = mchit.rc.right = mchit.rc.top = mchit.rc.bottom = -1; + SetRect(&mchit.rc, -1, -1, -1, -1); ret = SendMessageA(hwnd, MCM_HITTEST, 0, (LPARAM)&mchit); expect_hex(MCHT_TITLE, ret); expect_hex(MCHT_TITLE, mchit.uHit); @@ -1835,7 +1834,7 @@ static void test_hittest_v6(void) mchit.iOffset = -2; mchit.iCol = mchit.iRow = -2; mchit.uHit = ~0; - mchit.rc.left = mchit.rc.right = mchit.rc.top = mchit.rc.bottom = -1; + SetRect(&mchit.rc, -1, -1, -1, -1); ret = SendMessageA(hwnd, MCM_HITTEST, 0, (LPARAM)&mchit); todo_wine expect_hex(MCHT_NOWHERE, ret); todo_wine expect_hex(MCHT_NOWHERE, mchit.uHit); @@ -2016,7 +2015,7 @@ static void test_sel_notify(void) }; int i; - for(i = 0; i < sizeof styles / sizeof styles[0]; i++) + for(i = 0; i < ARRAY_SIZE(styles); i++) { hwnd = create_monthcal_control(styles[i].val); SetWindowLongPtrA(hwnd, GWLP_ID, SEL_NOTIFY_TEST_ID); diff --git a/modules/rostests/winetests/comctl32/mru.c b/modules/rostests/winetests/comctl32/mru.c index dfad6f859d..b3d3e7a265 100644 --- a/modules/rostests/winetests/comctl32/mru.c +++ b/modules/rostests/winetests/comctl32/mru.c @@ -118,7 +118,7 @@ static LSTATUS mru_RegDeleteTreeA(HKEY hKey, LPCSTR lpszSubKey) dwMaxSubkeyLen++; dwMaxValueLen++; dwMaxLen = max(dwMaxSubkeyLen, dwMaxValueLen); - if (dwMaxLen > sizeof(szNameBuf)/sizeof(CHAR)) + if (dwMaxLen > ARRAY_SIZE(szNameBuf)) { /* Name too big: alloc a buffer for it */ if (!(lpszName = heap_alloc(dwMaxLen * sizeof(CHAR)))) @@ -480,7 +480,7 @@ static void test_CreateMRUListLazyA(void) return; } - for (i = 0; i < sizeof(create_lazyA)/sizeof(create_lazya_t); i++) + for (i = 0; i < ARRAY_SIZE(create_lazyA); i++) { const create_lazya_t *ptr = &create_lazyA[i]; HANDLE hMRU; diff --git a/modules/rostests/winetests/comctl32/pager.c b/modules/rostests/winetests/comctl32/pager.c index 0da396cdd1..f48dc03954 100644 --- a/modules/rostests/winetests/comctl32/pager.c +++ b/modules/rostests/winetests/comctl32/pager.c @@ -28,10 +28,195 @@ #define PAGER_SEQ_INDEX 0 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}; +/* Double zero so that it's safe to cast it to WCHAR * */ +static CHAR te_a[] = {'t', 'e', 0, 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. */ +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, + SET_NULL_IF_NO_MASK = 0x40, + ZERO_SEND = 0x80 +}; + +enum handler_ids +{ + TVITEM_NEW_HANDLER, + TVITEM_OLD_HANDLER +}; + +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; + UINT handler_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; + UINT handler_id; +}; + +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)}, +}; + +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} +}; + +static const struct notify_test_datetime_format +{ + /* Data send to parent */ + WCHAR *send_pszformat; + /* Data expected by parent */ + CHAR *expect_pszformat; + /* Data for parent to write */ + CHAR *write_szdisplay; + INT write_szdisplay_size; + CHAR *write_pszdisplay; + /* Data when message returned */ + WCHAR *return_szdisplay; + INT return_szdisplay_size; + WCHAR *return_pszdisplay; +} test_datetime_format_data[] = +{ + {test_w, test_a}, + {NULL, NULL, NULL, 0, test_a, empty_w, -1, test_w}, + {NULL, NULL, test_a, sizeof(test_a), NULL, test_w, -1, test_w}, + {NULL, NULL, test_a, 2, test_a, (WCHAR *)te_a, -1, test_w}, + {NULL, NULL, NULL, 0, large_a, NULL, 0, large_w} +}; #define CHILD1_ID 1 #define CHILD2_ID 2 +static BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*); static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR); static struct msg_sequence *sequences[NUM_MSG_SEQUENCES]; @@ -97,6 +282,14 @@ static const struct message set_pos_empty_seq[] = { { 0 } }; +static CHAR *heap_strdup(const CHAR *str) +{ + int len = lstrlenA(str) + 1; + CHAR *ret = heap_alloc(len * sizeof(CHAR)); + lstrcpyA(ret, str); + return ret; +} + static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static LONG defwndproc_counter = 0; @@ -257,11 +450,7 @@ static void test_pager(void) RECT rect, rect2; pager = create_pager_control( PGS_HORZ ); - if (!pager) - { - win_skip( "Pager control not supported\n" ); - return; - } + ok(pager != NULL, "Fail to create pager\n"); register_child_wnd_class(); @@ -333,11 +522,809 @@ static void test_pager(void) DestroyWindow( pager ); } -START_TEST(pager) +static LRESULT WINAPI test_notifyformat_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { - HMODULE mod = GetModuleHandleA("comctl32.dll"); + switch (message) + { + case WM_NOTIFYFORMAT: + if (lParam == NF_QUERY) + { + notify_query_received = TRUE; + return notify_format; + } + else if (lParam == NF_REQUERY) + return SendMessageA(GetParent(hwnd), WM_NOTIFYFORMAT, (WPARAM)hwnd, NF_QUERY); + else + return 0; + default: + return notify_format == NFR_UNICODE ? DefWindowProcW(hwnd, message, wParam, lParam) + : DefWindowProcA(hwnd, message, wParam, lParam); + } +} + +static BOOL register_notifyformat_class(void) +{ + static const WCHAR class_w[] = {'P', 'a', 'g', 'e', 'r', ' ', 'n', 'o', 't', 'i', 'f', 'y', 'f', + 'o', 'r', 'm', 'a', 't', ' ', 'c', 'l', 'a', 's', 's', 0}; + WNDCLASSW cls = {0}; + + cls.lpfnWndProc = test_notifyformat_proc; + cls.hInstance = GetModuleHandleW(NULL); + cls.lpszClassName = class_w; + return RegisterClassW(&cls); +} + +static void test_wm_notifyformat(void) +{ + static const WCHAR class_w[] = {'P', 'a', 'g', 'e', 'r', ' ', 'n', 'o', 't', 'i', 'f', 'y', 'f', + 'o', 'r', 'm', 'a', 't', ' ', 'c', 'l', 'a', 's', 's', 0}; + static const WCHAR parent_w[] = {'p', 'a', 'r', 'e', 'n', 't', 0}; + static const WCHAR pager_w[] = {'p', 'a', 'g', 'e', 'r', 0}; + static const WCHAR child_w[] = {'c', 'h', 'i', 'l', 'd', 0}; + static const INT formats[] = {NFR_UNICODE, NFR_ANSI}; + HWND parent, pager, child; + LRESULT ret; + INT i; + + ok(register_notifyformat_class(), "Register test class failed, error 0x%08x\n", GetLastError()); + + for (i = 0; i < ARRAY_SIZE(formats); i++) + { + notify_format = formats[i]; + parent = CreateWindowW(class_w, parent_w, WS_OVERLAPPED, 0, 0, 100, 100, 0, 0, GetModuleHandleW(0), 0); + ok(parent != NULL, "CreateWindow failed\n"); + pager = CreateWindowW(WC_PAGESCROLLERW, pager_w, WS_CHILD, 0, 0, 100, 100, parent, 0, GetModuleHandleW(0), 0); + ok(pager != NULL, "CreateWindow failed\n"); + child = CreateWindowW(class_w, child_w, WS_CHILD, 0, 0, 100, 100, pager, 0, GetModuleHandleW(0), 0); + ok(child != NULL, "CreateWindow failed\n"); + SendMessageW(pager, PGM_SETCHILD, 0, (LPARAM)child); + + /* Test parent */ + notify_query_received = FALSE; + ret = SendMessageW(pager, WM_NOTIFYFORMAT, (WPARAM)parent, NF_REQUERY); + ok(ret == notify_format, "Expect %d, got %ld\n", notify_format, ret); + ok(notify_query_received, "Didn't receive notify\n"); + + /* Send NF_QUERY directly to parent */ + notify_query_received = FALSE; + ret = SendMessageW(parent, WM_NOTIFYFORMAT, (WPARAM)pager, NF_QUERY); + ok(ret == notify_format, "Expect %d, got %ld\n", notify_format, ret); + ok(notify_query_received, "Didn't receive notify\n"); + + /* Pager send notifications to its parent regardless of wParam */ + notify_query_received = FALSE; + ret = SendMessageW(pager, WM_NOTIFYFORMAT, (WPARAM)parent_wnd, NF_REQUERY); + ok(ret == notify_format, "Expect %d, got %ld\n", notify_format, ret); + ok(notify_query_received, "Didn't receive notify\n"); + + /* Pager always wants Unicode notifications from children */ + ret = SendMessageW(child, WM_NOTIFYFORMAT, (WPARAM)pager, NF_REQUERY); + ok(ret == NFR_UNICODE, "Expect %d, got %ld\n", NFR_UNICODE, ret); + ret = SendMessageW(pager, WM_NOTIFYFORMAT, (WPARAM)child, NF_QUERY); + ok(ret == NFR_UNICODE, "Expect %d, got %ld\n", NFR_UNICODE, ret); + + DestroyWindow(parent); + } + + 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 & 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); + 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); + /* 64bit Windows will try to free the text pointer even if it's application provided when handling + * HDN_GETDISPINFOW. Deliberate leak here. */ + else if(notify_test_info.unicode == HDN_GETDISPINFOW) + *text = heap_strdup(receive_data->write_pointer); + 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 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 void notify_datetime_handler(NMDATETIMEFORMATA *nm) +{ + const struct notify_test_datetime_format *data = test_datetime_format_data + notify_test_info.sub_test_id; + if (data->expect_pszformat) + ok(!lstrcmpA(data->expect_pszformat, nm->pszFormat), "Sub test %d expect %s, got %s\n", + notify_test_info.sub_test_id, data->expect_pszformat, nm->pszFormat); + ok(nm->pszDisplay == nm->szDisplay, "Test %d expect %p, got %p\n", notify_test_info.sub_test_id, nm->szDisplay, + nm->pszDisplay); + if (data->write_szdisplay) memcpy(nm->szDisplay, data->write_szdisplay, data->write_szdisplay_size); + if (data->write_pszdisplay) nm->pszDisplay = data->write_pszdisplay; +} + +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) + { + /* 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; + } + /* Date and Time Picker */ + case DTN_FORMATA: + { + notify_datetime_handler((NMDATETIMEFORMATA *)hdr); + break; + } + case DTN_FORMATQUERYA: + { + NMDATETIMEFORMATQUERYA *nmdtfq = (NMDATETIMEFORMATQUERYA *)hdr; + notify_generic_text_handler((CHAR **)&nmdtfq->pszFormat, NULL); + break; + } + case DTN_WMKEYDOWNA: + { + NMDATETIMEWMKEYDOWNA *nmdtkd = (NMDATETIMEWMKEYDOWNA *)hdr; + notify_generic_text_handler((CHAR **)&nmdtkd->pszFormat, NULL); + break; + } + case DTN_USERSTRINGA: + { + NMDATETIMESTRINGA *nmdts = (NMDATETIMESTRINGA *)hdr; + notify_generic_text_handler((CHAR **)&nmdts->pszUserString, NULL); + break; + } + /* Header */ + case HDN_BEGINDRAG: + case HDN_ENDDRAG: + case HDN_BEGINFILTEREDIT: + case HDN_ENDFILTEREDIT: + case HDN_DROPDOWN: + case HDN_FILTERCHANGE: + case HDN_ITEMKEYDOWN: + case HDN_ITEMSTATEICONCLICK: + case HDN_OVERFLOWCLICK: + { + NMHEADERW *nmhd = (NMHEADERW *)hdr; + ok(!lstrcmpW(nmhd->pitem->pszText, test_w), "Expect %s, got %s\n", wine_dbgstr_w(test_w), + wine_dbgstr_w(nmhd->pitem->pszText)); + ok(!lstrcmpW(((HD_TEXTFILTERW *)nmhd->pitem->pvFilter)->pszText, test_w), "Expect %s, got %s\n", + wine_dbgstr_w(test_w), wine_dbgstr_w(((HD_TEXTFILTERW *)nmhd->pitem->pvFilter)->pszText)); + break; + } + case HDN_BEGINTRACKA: + case HDN_DIVIDERDBLCLICKA: + case HDN_ENDTRACKA: + case HDN_ITEMCHANGEDA: + case HDN_ITEMCHANGINGA: + case HDN_ITEMCLICKA: + case HDN_ITEMDBLCLICKA: + case HDN_TRACKA: + { + NMHEADERA *nmhd = (NMHEADERA *)hdr; + ok(!lstrcmpA(nmhd->pitem->pszText, test_a), "Expect %s, got %s\n", test_a, nmhd->pitem->pszText); + ok(!lstrcmpA(((HD_TEXTFILTERA *)nmhd->pitem->pvFilter)->pszText, test_a), "Expect %s, got %s\n", test_a, + ((HD_TEXTFILTERA *)nmhd->pitem->pvFilter)->pszText); + break; + } + case HDN_GETDISPINFOA: + { + NMHDDISPINFOA *nmhddi = (NMHDDISPINFOA *)hdr; + notify_generic_text_handler(&nmhddi->pszText, &nmhddi->cchTextMax); + break; + } + /* List View */ + case LVN_BEGINLABELEDITA: + case LVN_ENDLABELEDITA: + case LVN_GETDISPINFOA: + case LVN_SETDISPINFOA: + { + NMLVDISPINFOA *nmlvdi = (NMLVDISPINFOA *)hdr; + notify_generic_text_handler(&nmlvdi->item.pszText, &nmlvdi->item.cchTextMax); + break; + } + case LVN_GETINFOTIPA: + { + NMLVGETINFOTIPA *nmlvgit = (NMLVGETINFOTIPA *)hdr; + notify_generic_text_handler(&nmlvgit->pszText, &nmlvgit->cchTextMax); + break; + } + case LVN_INCREMENTALSEARCHA: + case LVN_ODFINDITEMA: + { + NMLVFINDITEMA *nmlvfi = (NMLVFINDITEMA *)hdr; + notify_generic_text_handler((CHAR **)&nmlvfi->lvfi.psz, NULL); + break; + } + /* 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; + } + /* Tooltip */ + case TTN_GETDISPINFOA: + { + notify_tooltip_handler((NMTTDISPINFOA *)hdr); + break; + } + /* Tree View */ + case TVN_BEGINLABELEDITA: + case TVN_ENDLABELEDITA: + case TVN_GETDISPINFOA: + case TVN_SETDISPINFOA: + { + NMTVDISPINFOA *nmtvdi = (NMTVDISPINFOA *)hdr; + notify_generic_text_handler(&nmtvdi->item.pszText, &nmtvdi->item.cchTextMax); + break; + } + case TVN_GETINFOTIPA: + { + NMTVGETINFOTIPA *nmtvgit = (NMTVGETINFOTIPA *)hdr; + notify_generic_text_handler(&nmtvgit->pszText, &nmtvgit->cchTextMax); + break; + } + case TVN_SINGLEEXPAND: + case TVN_BEGINDRAGA: + case TVN_BEGINRDRAGA: + case TVN_ITEMEXPANDEDA: + case TVN_ITEMEXPANDINGA: + case TVN_DELETEITEMA: + case TVN_SELCHANGINGA: + case TVN_SELCHANGEDA: + { + NMTREEVIEWA *nmtv = (NMTREEVIEWA *)hdr; + if (notify_test_info.handler_id == TVITEM_NEW_HANDLER) + notify_generic_text_handler((CHAR **)&nmtv->itemNew.pszText, &nmtv->itemNew.cchTextMax); + else + notify_generic_text_handler((CHAR **)&nmtv->itemOld.pszText, &nmtv->itemOld.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; + notify_test_info.handler_id = para->handler_id; + + 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); + + 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_datetime(HWND pager) +{ + const struct notify_test_datetime_format *data; + NMDATETIMEFORMATW nmdtf; + INT i; + + for (i = 0; i < ARRAY_SIZE(test_datetime_format_data); i++) + { + data = test_datetime_format_data + i; + notify_test_info.sub_test_id = i; + + memset(&nmdtf, 0, sizeof(nmdtf)); + if(data->send_pszformat) nmdtf.pszFormat = data->send_pszformat; + nmdtf.pszDisplay = nmdtf.szDisplay; + send_notify(pager, DTN_FORMATW, DTN_FORMATA, (LPARAM)&nmdtf, TRUE); + if (data->return_szdisplay) + ok(!lstrcmpW(nmdtf.szDisplay, data->return_szdisplay), "Sub test %d expect %s, got %s\n", i, + wine_dbgstr_w(data->return_szdisplay), wine_dbgstr_w(nmdtf.szDisplay)); + if (data->return_pszdisplay) + ok(!lstrcmpW(nmdtf.pszDisplay, data->return_pszdisplay), "Sub test %d expect %s, got %s\n", i, + wine_dbgstr_w(data->return_pszdisplay), wine_dbgstr_w(nmdtf.pszDisplay)); + } +} + +static void test_wm_notify_header(HWND pager) +{ + NMHEADERW nmh = {{0}}; + HDITEMW hdi = {0}; + HD_TEXTFILTERW hdtf = {0}; + + hdi.mask = HDI_TEXT | HDI_FILTER; + hdi.pszText = test_w; + hdtf.pszText = test_w; + nmh.pitem = &hdi; + nmh.pitem->pvFilter = &hdtf; + send_notify(pager, HDN_BEGINDRAG, HDN_BEGINDRAG, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_ENDDRAG, HDN_ENDDRAG, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_BEGINFILTEREDIT, HDN_BEGINFILTEREDIT, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_ENDFILTEREDIT, HDN_ENDFILTEREDIT, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_DROPDOWN, HDN_DROPDOWN, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_FILTERCHANGE, HDN_FILTERCHANGE, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_ITEMKEYDOWN, HDN_ITEMKEYDOWN, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_ITEMSTATEICONCLICK, HDN_ITEMSTATEICONCLICK, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_OVERFLOWCLICK, HDN_OVERFLOWCLICK, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_BEGINTRACKW, HDN_BEGINTRACKA, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_DIVIDERDBLCLICKW, HDN_DIVIDERDBLCLICKA, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_ENDTRACKW, HDN_ENDTRACKA, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_ITEMCHANGEDW, HDN_ITEMCHANGEDA, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_ITEMCHANGINGW, HDN_ITEMCHANGINGA, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_ITEMCLICKW, HDN_ITEMCLICKA, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_ITEMDBLCLICKW, HDN_ITEMDBLCLICKA, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_TRACKW, HDN_TRACKA, (LPARAM)&nmh, 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"; + HWND parent, pager; + /* Combo Box Ex */ + static NMCOMBOBOXEXW nmcbe; + /* Date and Time Picker */ + static NMDATETIMEFORMATQUERYW nmdtfq; + static NMDATETIMEWMKEYDOWNW nmdtkd; + static NMDATETIMESTRINGW nmdts; + /* Header */ + static NMHDDISPINFOW nmhddi; + /* List View */ + static NMLVDISPINFOW nmlvdi; + static NMLVGETINFOTIPW nmlvgit; + static NMLVFINDITEMW nmlvfi; + /* Tool Bar */ + static NMTBRESTORE nmtbr; + static NMTBSAVE nmtbs; + static NMTOOLBARW nmtb; + static NMTBDISPINFOW nmtbdi; + static NMTBGETINFOTIPW nmtbgit; + /* Tree View */ + static NMTVDISPINFOW nmtvdi; + static NMTVGETINFOTIPW nmtvgit; + static NMTREEVIEWW nmtv; + 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}, + /* Date and Time Picker */ + {&nmdtfq, sizeof(nmdtfq), NULL, 0, (WCHAR **)&nmdtfq.pszFormat, NULL, DTN_FORMATQUERYW, DTN_FORMATQUERYA, + CONVERT_SEND}, + {&nmdtkd, sizeof(nmdtkd), NULL, 0, (WCHAR **)&nmdtkd.pszFormat, NULL, DTN_WMKEYDOWNW, DTN_WMKEYDOWNA, + CONVERT_SEND}, + {&nmdts, sizeof(nmdts), NULL, 0, (WCHAR **)&nmdts.pszUserString, NULL, DTN_USERSTRINGW, DTN_USERSTRINGA, + CONVERT_SEND}, + /* Header */ + {&nmhddi, sizeof(nmhddi), &nmhddi.mask, HDI_TEXT, &nmhddi.pszText, &nmhddi.cchTextMax, HDN_GETDISPINFOW, + HDN_GETDISPINFOA, SEND_EMPTY_IF_NULL | CONVERT_SEND | CONVERT_RECEIVE}, + /* List View */ + {&nmlvfi, sizeof(nmlvfi), &nmlvfi.lvfi.flags, LVFI_STRING, (WCHAR **)&nmlvfi.lvfi.psz, NULL, + LVN_INCREMENTALSEARCHW, LVN_INCREMENTALSEARCHA, CONVERT_SEND}, + {&nmlvfi, sizeof(nmlvfi), &nmlvfi.lvfi.flags, LVFI_SUBSTRING, (WCHAR **)&nmlvfi.lvfi.psz, NULL, LVN_ODFINDITEMW, + LVN_ODFINDITEMA, CONVERT_SEND}, + {&nmlvdi, sizeof(nmlvdi), &nmlvdi.item.mask, LVIF_TEXT, &nmlvdi.item.pszText, &nmlvdi.item.cchTextMax, + LVN_BEGINLABELEDITW, LVN_BEGINLABELEDITA, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE}, + {&nmlvdi, sizeof(nmlvdi), &nmlvdi.item.mask, LVIF_TEXT, &nmlvdi.item.pszText, &nmlvdi.item.cchTextMax, + LVN_ENDLABELEDITW, LVN_ENDLABELEDITA, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE}, + {&nmlvdi, sizeof(nmlvdi), &nmlvdi.item.mask, LVIF_TEXT, &nmlvdi.item.pszText, &nmlvdi.item.cchTextMax, + LVN_GETDISPINFOW, LVN_GETDISPINFOA, DONT_CONVERT_SEND | CONVERT_RECEIVE}, + {&nmlvdi, sizeof(nmlvdi), &nmlvdi.item.mask, LVIF_TEXT, &nmlvdi.item.pszText, &nmlvdi.item.cchTextMax, + LVN_SETDISPINFOW, LVN_SETDISPINFOA, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE}, + {&nmlvgit, sizeof(nmlvgit), NULL, 0, &nmlvgit.pszText, &nmlvgit.cchTextMax, LVN_GETINFOTIPW, LVN_GETINFOTIPA, + 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}, + {&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}, + /* Tree View */ + {&nmtvdi, sizeof(nmtvdi), &nmtvdi.item.mask, TVIF_TEXT, &nmtvdi.item.pszText, &nmtvdi.item.cchTextMax, + TVN_BEGINLABELEDITW, TVN_BEGINLABELEDITA, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE}, + {&nmtvdi, sizeof(nmtvdi), &nmtvdi.item.mask, TVIF_TEXT, &nmtvdi.item.pszText, &nmtvdi.item.cchTextMax, + TVN_ENDLABELEDITW, TVN_ENDLABELEDITA, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE}, + {&nmtvdi, sizeof(nmtvdi), &nmtvdi.item.mask, TVIF_TEXT, &nmtvdi.item.pszText, &nmtvdi.item.cchTextMax, + TVN_GETDISPINFOW, TVN_GETDISPINFOA, ZERO_SEND | DONT_CONVERT_SEND| CONVERT_RECEIVE}, + {&nmtvdi, sizeof(nmtvdi), &nmtvdi.item.mask, TVIF_TEXT, &nmtvdi.item.pszText, &nmtvdi.item.cchTextMax, + TVN_SETDISPINFOW, TVN_SETDISPINFOA, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE}, + {&nmtvgit, sizeof(nmtvgit), NULL, 0, &nmtvgit.pszText, &nmtvgit.cchTextMax, TVN_GETINFOTIPW, TVN_GETINFOTIPA, + DONT_CONVERT_SEND | CONVERT_RECEIVE}, + {&nmtv, sizeof(nmtv), &nmtv.itemNew.mask, TVIF_TEXT, &nmtv.itemNew.pszText, &nmtv.itemNew.cchTextMax, + TVN_SINGLEEXPAND, TVN_SINGLEEXPAND, DONT_CONVERT_SEND | DONT_CONVERT_RECEIVE, TVITEM_NEW_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemOld.mask, TVIF_TEXT, &nmtv.itemOld.pszText, &nmtv.itemOld.cchTextMax, + TVN_SINGLEEXPAND, TVN_SINGLEEXPAND, DONT_CONVERT_SEND | DONT_CONVERT_RECEIVE, TVITEM_OLD_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemNew.mask, TVIF_TEXT, &nmtv.itemNew.pszText, &nmtv.itemNew.cchTextMax, + TVN_BEGINDRAGW, TVN_BEGINDRAGA, CONVERT_SEND, TVITEM_NEW_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemOld.mask, TVIF_TEXT, &nmtv.itemOld.pszText, &nmtv.itemOld.cchTextMax, + TVN_BEGINDRAGW, TVN_BEGINDRAGA, DONT_CONVERT_SEND, TVITEM_OLD_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemNew.mask, TVIF_TEXT, &nmtv.itemNew.pszText, &nmtv.itemNew.cchTextMax, + TVN_BEGINRDRAGW, TVN_BEGINRDRAGA, CONVERT_SEND, TVITEM_NEW_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemOld.mask, TVIF_TEXT, &nmtv.itemOld.pszText, &nmtv.itemOld.cchTextMax, + TVN_BEGINRDRAGW, TVN_BEGINRDRAGA, DONT_CONVERT_SEND, TVITEM_OLD_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemNew.mask, TVIF_TEXT, &nmtv.itemNew.pszText, &nmtv.itemNew.cchTextMax, + TVN_ITEMEXPANDEDW, TVN_ITEMEXPANDEDA, CONVERT_SEND, TVITEM_NEW_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemOld.mask, TVIF_TEXT, &nmtv.itemOld.pszText, &nmtv.itemOld.cchTextMax, + TVN_ITEMEXPANDEDW, TVN_ITEMEXPANDEDA, DONT_CONVERT_SEND, TVITEM_OLD_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemNew.mask, TVIF_TEXT, &nmtv.itemNew.pszText, &nmtv.itemNew.cchTextMax, + TVN_ITEMEXPANDINGW, TVN_ITEMEXPANDINGA, CONVERT_SEND, TVITEM_NEW_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemOld.mask, TVIF_TEXT, &nmtv.itemOld.pszText, &nmtv.itemOld.cchTextMax, + TVN_ITEMEXPANDINGW, TVN_ITEMEXPANDINGA, DONT_CONVERT_SEND, TVITEM_OLD_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemNew.mask, TVIF_TEXT, &nmtv.itemNew.pszText, &nmtv.itemNew.cchTextMax, + TVN_DELETEITEMW, TVN_DELETEITEMA, DONT_CONVERT_SEND, TVITEM_NEW_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemOld.mask, TVIF_TEXT, &nmtv.itemOld.pszText, &nmtv.itemOld.cchTextMax, + TVN_DELETEITEMW, TVN_DELETEITEMA, CONVERT_SEND, TVITEM_OLD_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemNew.mask, TVIF_TEXT, &nmtv.itemNew.pszText, &nmtv.itemNew.cchTextMax, + TVN_SELCHANGINGW, TVN_SELCHANGINGA, CONVERT_SEND, TVITEM_NEW_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemOld.mask, TVIF_TEXT, &nmtv.itemOld.pszText, &nmtv.itemOld.cchTextMax, + TVN_SELCHANGINGW, TVN_SELCHANGINGA, CONVERT_SEND, TVITEM_OLD_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemNew.mask, TVIF_TEXT, &nmtv.itemNew.pszText, &nmtv.itemNew.cchTextMax, + TVN_SELCHANGEDW, TVN_SELCHANGEDA, CONVERT_SEND, TVITEM_NEW_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemOld.mask, TVIF_TEXT, &nmtv.itemOld.pszText, &nmtv.itemOld.cchTextMax, + TVN_SELCHANGEDW, TVN_SELCHANGEDA, CONVERT_SEND, TVITEM_OLD_HANDLER} + }; + 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); + + /* Tests for those that can't be covered by generic text test helper */ + test_wm_notify_comboboxex(pager); + test_wm_notify_datetime(pager); + test_wm_notify_header(pager); + test_wm_notify_tooltip(pager); + + DestroyWindow(parent); + UnregisterClassA(class, GetModuleHandleA(NULL)); +} + +static void init_functions(void) +{ + HMODULE mod = LoadLibraryA("comctl32.dll"); + +#define X(f) p##f = (void*)GetProcAddress(mod, #f); + X(InitCommonControlsEx); +#undef X pSetWindowSubclass = (void*)GetProcAddress(mod, (LPSTR)410); +} + +START_TEST(pager) +{ + INITCOMMONCONTROLSEX iccex; + + init_functions(); + + iccex.dwSize = sizeof(iccex); + iccex.dwICC = ICC_PAGESCROLLER_CLASS; + pInitCommonControlsEx(&iccex); init_msg_sequences(sequences, NUM_MSG_SEQUENCES); @@ -345,4 +1332,8 @@ START_TEST(pager) ok(parent_wnd != NULL, "Failed to create parent window!\n"); test_pager(); + test_wm_notifyformat(); + test_wm_notify(); + + DestroyWindow(parent_wnd); } diff --git a/modules/rostests/winetests/comctl32/progress.c b/modules/rostests/winetests/comctl32/progress.c index b911021be0..497cb47d3c 100644 --- a/modules/rostests/winetests/comctl32/progress.c +++ b/modules/rostests/winetests/comctl32/progress.c @@ -258,7 +258,7 @@ static void test_PBM_STEPIT(void) HWND progress; int i, j; - for (i = 0; i < sizeof(stepit_tests)/sizeof(stepit_tests[0]); i++) + for (i = 0; i < ARRAY_SIZE(stepit_tests); i++) { struct stepit_test *test = &stepit_tests[i]; LRESULT ret; diff --git a/modules/rostests/winetests/comctl32/propsheet.c b/modules/rostests/winetests/comctl32/propsheet.c index 25d7cdec89..7d9da3f72b 100644 --- a/modules/rostests/winetests/comctl32/propsheet.c +++ b/modules/rostests/winetests/comctl32/propsheet.c @@ -280,7 +280,6 @@ static void test_disableowner(void) psh.pfnCallback = disableowner_callback; p = pPropertySheetA(&psh); - todo_wine ok(p == 0, "Expected 0, got %ld\n", p); ok(IsWindowEnabled(parenthwnd) != 0, "parent window should be enabled\n"); DestroyWindow(parenthwnd); @@ -1147,6 +1146,46 @@ static void test_CreatePropertySheetPage(void) } } +static void test_bad_control_class(void) +{ + PROPSHEETPAGEA psp; + PROPSHEETHEADERA psh; + HPROPSHEETPAGE hpsp; + INT_PTR ret; + + memset(&psp, 0, sizeof(psp)); + psp.dwSize = sizeof(psp); + psp.hInstance = GetModuleHandleA(NULL); + U(psp).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_BAD_CONTROL); + psp.pfnDlgProc = page_dlg_proc; + + hpsp = pCreatePropertySheetPageA(&psp); + ok(hpsp != 0, "CreatePropertySheetPage failed\n"); + + memset(&psh, 0, sizeof(psh)); + psh.dwSize = PROPSHEETHEADERA_V1_SIZE; + psh.nPages = 1; + psh.hwndParent = GetDesktopWindow(); + U3(psh).phpage = &hpsp; + +#ifndef __REACTOS__ /* FIXME: Inspect why this causes a hang */ + ret = pPropertySheetA(&psh); + ok(ret == 0, "got %ld\n", ret); +#endif + + /* Need to recreate hpsp otherwise the test fails under Windows */ + hpsp = pCreatePropertySheetPageA(&psp); + ok(hpsp != 0, "CreatePropertySheetPage failed\n"); + U3(psh).phpage = &hpsp; + + psh.dwFlags = PSH_MODELESS; + ret = pPropertySheetA(&psh); + ok(ret != 0, "got %ld\n", ret); + + ok(IsWindow((HWND)ret), "bad window handle %#lx\n", ret); + DestroyWindow((HWND)ret); +} + static void init_functions(void) { HMODULE hComCtl32 = LoadLibraryA("comctl32.dll"); @@ -1172,6 +1211,7 @@ START_TEST(propsheet) init_functions(); + test_bad_control_class(); test_title(); test_nopage(); test_disableowner(); diff --git a/modules/rostests/winetests/comctl32/rebar.c b/modules/rostests/winetests/comctl32/rebar.c index b3e78009f5..501638c758 100644 --- a/modules/rostests/winetests/comctl32/rebar.c +++ b/modules/rostests/winetests/comctl32/rebar.c @@ -828,7 +828,7 @@ static DWORD resize_numtests = 0; RECT r; \ int value; \ const rbresize_test_result_t *res = &resize_results[resize_numtests++]; \ - assert(resize_numtests <= sizeof(resize_results)/sizeof(resize_results[0])); \ + assert(resize_numtests <= ARRAY_SIZE(resize_results)); \ GetWindowRect(hRebar, &r); \ MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); \ if ((dwStyles[i] & (CCS_NOPARENTALIGN|CCS_NODIVIDER)) == CCS_NOPARENTALIGN) {\ @@ -855,7 +855,7 @@ static void test_resize(void) CCS_TOP | WS_BORDER, CCS_NOPARENTALIGN | CCS_NODIVIDER | WS_BORDER, CCS_NORESIZE | WS_BORDER, CCS_NOMOVEY | WS_BORDER}; - const int styles_count = sizeof(dwStyles) / sizeof(dwStyles[0]); + const int styles_count = ARRAY_SIZE(dwStyles); int i; for (i = 0; i < styles_count; i++) diff --git a/modules/rostests/winetests/comctl32/resources.h b/modules/rostests/winetests/comctl32/resources.h index 3a89cd7baf..53522c0a09 100644 --- a/modules/rostests/winetests/comctl32/resources.h +++ b/modules/rostests/winetests/comctl32/resources.h @@ -39,6 +39,7 @@ #define IDD_PROP_PAGE_WITH_CUSTOM_DEFAULT_BUTTON 34 #define IDD_PROP_PAGE_MESSAGE_TEST 35 #define IDD_PROP_PAGE_ERROR 36 +#define IDD_PROP_PAGE_BAD_CONTROL 37 #define IDC_PS_EDIT1 1000 #define IDC_PS_EDIT2 1001 diff --git a/modules/rostests/winetests/comctl32/rsrc.rc b/modules/rostests/winetests/comctl32/rsrc.rc index 327aa225e1..6eb4fe8a0a 100644 --- a/modules/rostests/winetests/comctl32/rsrc.rc +++ b/modules/rostests/winetests/comctl32/rsrc.rc @@ -78,6 +78,13 @@ FONT 8, "MS Shell Dlg" { } +IDD_PROP_PAGE_BAD_CONTROL DIALOG 0, 0, 100, 100 +STYLE WS_POPUP | WS_CAPTION | WS_CLIPSIBLINGS | WS_VISIBLE +FONT 8, "MS Shell Dlg" +{ + CONTROL "", -1, "invalid class", 0, 0, 0, 0, 0 +} + STRINGTABLE { IDS_TBADD1 "abc" diff --git a/modules/rostests/winetests/comctl32/status.c b/modules/rostests/winetests/comctl32/status.c index 2308974761..c7d9989218 100644 --- a/modules/rostests/winetests/comctl32/status.c +++ b/modules/rostests/winetests/comctl32/status.c @@ -127,7 +127,7 @@ static int CALLBACK check_height_font_enumproc(ENUMLOGFONTEXA *enumlf, NEWTEXTME if (type != TRUETYPE_FONTTYPE) facename = enumlf->elfLogFont.lfFaceName; - for (i = 0; i < sizeof(sizes)/sizeof(sizes[0]); i++) + for (i = 0; i < ARRAY_SIZE(sizes); i++) { HFONT hFont; TEXTMETRICA tm; @@ -586,6 +586,131 @@ static void test_notify(void) ok(g_got_contextmenu, "WM_RBUTTONUP did not activate the context menu!\n"); } +static void test_sizegrip(void) +{ + HWND hwndStatus; + LONG style; + RECT rc, rcClient; + POINT pt; + int width, r; + + hwndStatus = CreateWindowA(SUBCLASS_NAME, "", WS_CHILD|WS_VISIBLE|SBARS_SIZEGRIP, + 0, 0, 100, 100, g_hMainWnd, NULL, NULL, NULL); + + style = GetWindowLongPtrA(g_hMainWnd, GWL_STYLE); + width = GetSystemMetrics(SM_CXVSCROLL); + + GetClientRect(hwndStatus, &rcClient); + + pt.x = rcClient.right; + pt.y = rcClient.top; + ClientToScreen(hwndStatus, &pt); + rc.left = pt.x - width; + rc.right = pt.x; + rc.top = pt.y; + + pt.y = rcClient.bottom; + ClientToScreen(hwndStatus, &pt); + rc.bottom = pt.y; + + /* check bounds when not maximized */ + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left, rc.top)); + expect(HTBOTTOMRIGHT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left - 1, rc.top)); + expect(HTCLIENT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left, rc.top - 1)); + expect(HTBOTTOMRIGHT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right, rc.bottom)); + expect(HTBOTTOMRIGHT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right + 1, rc.bottom)); + expect(HTBOTTOMRIGHT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right, rc.bottom + 1)); + expect(HTBOTTOMRIGHT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right - 1, rc.bottom - 1)); + expect(HTBOTTOMRIGHT, r); + + /* not maximized and right-to-left */ + SetWindowLongA(hwndStatus, GWL_EXSTYLE, WS_EX_LAYOUTRTL); + + pt.x = rcClient.right; + ClientToScreen(hwndStatus, &pt); + rc.left = pt.x + width; + rc.right = pt.x; + + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left, rc.top)); + expect(HTBOTTOMLEFT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left + 1, rc.top)); + expect(HTCLIENT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left, rc.top - 1)); + expect(HTBOTTOMLEFT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right, rc.bottom)); + expect(HTBOTTOMLEFT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right - 1, rc.bottom)); + expect(HTBOTTOMLEFT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right, rc.bottom + 1)); + expect(HTBOTTOMLEFT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right + 1, rc.bottom - 1)); + expect(HTBOTTOMLEFT, r); + + /* maximize with left-to-right */ + SetWindowLongA(g_hMainWnd, GWL_STYLE, style|WS_MAXIMIZE); + SetWindowLongA(hwndStatus, GWL_EXSTYLE, 0); + + GetClientRect(hwndStatus, &rcClient); + + pt.x = rcClient.right; + pt.y = rcClient.top; + ClientToScreen(hwndStatus, &pt); + rc.left = pt.x - width; + rc.right = pt.x; + rc.top = pt.y; + + pt.y = rcClient.bottom; + ClientToScreen(hwndStatus, &pt); + rc.bottom = pt.y; + + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left, rc.top)); + expect(HTCLIENT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left - 1, rc.top)); + expect(HTCLIENT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left, rc.top - 1)); + expect(HTNOWHERE, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right, rc.bottom)); + expect(HTNOWHERE, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right + 1, rc.bottom)); + expect(HTNOWHERE, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right, rc.bottom + 1)); + expect(HTNOWHERE, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right - 1, rc.bottom - 1)); + expect(HTCLIENT, r); + + /* maximized with right-to-left */ + SetWindowLongA(hwndStatus, GWL_EXSTYLE, WS_EX_LAYOUTRTL); + + pt.x = rcClient.right; + ClientToScreen(hwndStatus, &pt); + rc.left = pt.x + width; + rc.right = pt.x; + + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left, rc.top)); + expect(HTCLIENT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left + 1, rc.top)); + expect(HTCLIENT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left, rc.top - 1)); + expect(HTNOWHERE, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right, rc.bottom)); + expect(HTNOWHERE, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right - 1, rc.bottom)); + expect(HTNOWHERE, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right, rc.bottom + 1)); + expect(HTNOWHERE, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right + 1, rc.bottom - 1)); + expect(HTCLIENT, r); + + SetWindowLongA(g_hMainWnd, GWL_STYLE, style); + DestroyWindow(hwndStatus); +} + static void init_functions(void) { HMODULE hComCtl32 = LoadLibraryA("comctl32.dll"); @@ -620,4 +745,5 @@ START_TEST(status) test_status_ownerdraw(); test_gettext(); test_notify(); + test_sizegrip(); } diff --git a/modules/rostests/winetests/comctl32/subclass.c b/modules/rostests/winetests/comctl32/subclass.c index 75a134352e..41ba06560a 100644 --- a/modules/rostests/winetests/comctl32/subclass.c +++ b/modules/rostests/winetests/comctl32/subclass.c @@ -218,46 +218,61 @@ static LRESULT WINAPI wnd_proc_sub(HWND hwnd, UINT message, WPARAM wParam, LPARA static void test_subclass(void) { + BOOL ret; HWND hwnd = CreateWindowExA(0, "TestSubclass", "Test subclass", WS_OVERLAPPEDWINDOW, 100, 100, 200, 200, 0, 0, 0, NULL); ok(hwnd != NULL, "failed to create test subclass wnd\n"); - pSetWindowSubclass(hwnd, wnd_proc_sub, 2, 0); + ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 2, 0); + ok(ret == TRUE, "Expected TRUE\n"); SendMessageA(hwnd, WM_USER, 1, 0); SendMessageA(hwnd, WM_USER, 2, 0); ok_sequence(Sub_BasicTest, "Basic"); - pSetWindowSubclass(hwnd, wnd_proc_sub, 2, DELETE_SELF); + ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 2, DELETE_SELF); + ok(ret == TRUE, "Expected TRUE\n"); SendMessageA(hwnd, WM_USER, 1, 1); ok_sequence(Sub_DeletedTest, "Deleted"); SendMessageA(hwnd, WM_USER, 1, 0); ok_sequence(Sub_AfterDeletedTest, "After Deleted"); - pSetWindowSubclass(hwnd, wnd_proc_sub, 2, 0); + ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 2, 0); + ok(ret == TRUE, "Expected TRUE\n"); orig_proc_3 = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)wnd_proc_3); SendMessageA(hwnd, WM_USER, 1, 0); SendMessageA(hwnd, WM_USER, 2, 0); ok_sequence(Sub_OldAfterNewTest, "Old after New"); - pSetWindowSubclass(hwnd, wnd_proc_sub, 4, 0); + ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 4, 0); + ok(ret == TRUE, "Expected TRUE\n"); SendMessageA(hwnd, WM_USER, 1, 0); ok_sequence(Sub_MixTest, "Mix"); /* Now the fun starts */ - pSetWindowSubclass(hwnd, wnd_proc_sub, 4, SEND_NEST); + ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 4, SEND_NEST); + ok(ret == TRUE, "Expected TRUE\n"); SendMessageA(hwnd, WM_USER, 1, 1); ok_sequence(Sub_MixAndNestTest, "Mix and nest"); - pSetWindowSubclass(hwnd, wnd_proc_sub, 4, SEND_NEST | DELETE_SELF); + ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 4, SEND_NEST | DELETE_SELF); + ok(ret == TRUE, "Expected TRUE\n"); SendMessageA(hwnd, WM_USER, 1, 1); ok_sequence(Sub_MixNestDelTest, "Mix, nest, del"); - pSetWindowSubclass(hwnd, wnd_proc_sub, 4, 0); - pSetWindowSubclass(hwnd, wnd_proc_sub, 5, DELETE_PREV); + ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 4, 0); + ok(ret == TRUE, "Expected TRUE\n"); + ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 5, DELETE_PREV); + ok(ret == TRUE, "Expected TRUE\n"); SendMessageA(hwnd, WM_USER, 1, 1); ok_sequence(Sub_MixDelPrevTest, "Mix and del prev"); + ret = pSetWindowSubclass(NULL, wnd_proc_sub, 1, 0); + ok(ret == FALSE, "Expected FALSE\n"); + + ret = pSetWindowSubclass(hwnd, NULL, 1, 0); + ok(ret == FALSE, "Expected FALSE\n"); + DestroyWindow(hwnd); } diff --git a/modules/rostests/winetests/comctl32/taskdialog.c b/modules/rostests/winetests/comctl32/taskdialog.c index 3fcc634e07..35dde1cce5 100644 --- a/modules/rostests/winetests/comctl32/taskdialog.c +++ b/modules/rostests/winetests/comctl32/taskdialog.c @@ -29,15 +29,21 @@ #include "v6util.h" #include "msg.h" +#ifdef __REACTOS__ +#define WM_KEYF1 0x004d +#endif + #define WM_TD_CALLBACK (WM_APP) /* Custom dummy message to wrap callback notifications */ #define NUM_MSG_SEQUENCES 1 #define TASKDIALOG_SEQ_INDEX 0 #define TEST_NUM_BUTTONS 10 /* Number of custom buttons to test with */ +#define TEST_NUM_RADIO_BUTTONS 3 #define ID_START 20 /* Lower IDs might be used by the system */ #define ID_START_BUTTON (ID_START + 0) +#define ID_START_RADIO_BUTTON (ID_START + 20) static HRESULT (WINAPI *pTaskDialogIndirect)(const TASKDIALOGCONFIG *, int *, int *, BOOL *); static HRESULT (WINAPI *pTaskDialog)(HWND, HINSTANCE, const WCHAR *, const WCHAR *, const WCHAR *, @@ -121,6 +127,261 @@ static const struct message_info msg_return_press_custom10[] = { 0 } }; +static const struct message_info msg_send_click_ok[] = +{ + { TDM_CLICK_BUTTON, IDOK, 0 }, + { 0 } +}; + +static const struct message_info msg_send_f1[] = +{ + { WM_KEYF1, 0, 0, 0}, + { 0 } +}; + +static const struct message_info msg_got_tdn_help[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_f1 }, + { TDN_HELP, 0, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +/* Three radio buttons */ +static const struct message_info msg_return_default_radio_button_1[] = +{ + { TDN_CREATED, 0, 0, S_OK, NULL }, + { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_return_default_radio_button_2[] = +{ + { TDN_CREATED, 0, 0, S_OK, NULL }, + { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON + 1, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_return_default_radio_button_3[] = +{ + { TDN_CREATED, 0, 0, S_OK, NULL }, + { TDN_RADIO_BUTTON_CLICKED, -2, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_select_first_radio_button[] = +{ + { TDM_CLICK_RADIO_BUTTON, ID_START_RADIO_BUTTON, 0 }, + { 0 } +}; + +static const struct message_info msg_return_first_radio_button[] = +{ + { TDN_CREATED, 0, 0, S_OK, NULL }, + { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON + 1, 0, S_OK, msg_select_first_radio_button }, + { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_select_first_disabled_radio_button_and_press_ok[] = +{ + { TDM_ENABLE_RADIO_BUTTON, ID_START_RADIO_BUTTON, 0 }, + { TDM_CLICK_RADIO_BUTTON, ID_START_RADIO_BUTTON, 0 }, + { TDM_CLICK_BUTTON, IDOK, 0 }, + { 0 } +}; + +static const struct message_info msg_return_default_radio_button_clicking_disabled[] = +{ + { TDN_CREATED, 0, 0, S_OK, NULL }, + { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON + 1, 0, S_OK, msg_select_first_disabled_radio_button_and_press_ok }, + { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON, 0, S_OK, NULL }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_return_no_default_radio_button_flag[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_click_ok }, + { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON, 0, S_OK, NULL }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_return_no_default_radio_button_id_and_flag[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_select_negative_id_radio_button[] = +{ + { TDM_CLICK_RADIO_BUTTON, -2, 0 }, + { 0 } +}; + +static const struct message_info msg_return_press_negative_id_radio_button[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_select_negative_id_radio_button }, + { TDN_RADIO_BUTTON_CLICKED, -2, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_send_all_common_button_click[] = +{ + { TDM_CLICK_BUTTON, IDOK, 0 }, + { TDM_CLICK_BUTTON, IDYES, 0 }, + { TDM_CLICK_BUTTON, IDNO, 0 }, + { TDM_CLICK_BUTTON, IDCANCEL, 0 }, + { TDM_CLICK_BUTTON, IDRETRY, 0 }, + { TDM_CLICK_BUTTON, IDCLOSE, 0 }, + { TDM_CLICK_BUTTON, ID_START_BUTTON + 99, 0 }, + { 0 } +}; + +static const struct message_info msg_press_nonexistent_buttons[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_all_common_button_click }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_FALSE, NULL }, + { TDN_BUTTON_CLICKED, IDYES, 0, S_FALSE, NULL }, + { TDN_BUTTON_CLICKED, IDNO, 0, S_FALSE, NULL }, + { TDN_BUTTON_CLICKED, IDCANCEL, 0, S_FALSE, NULL }, + { TDN_BUTTON_CLICKED, IDRETRY, 0, S_FALSE, NULL }, + { TDN_BUTTON_CLICKED, IDCLOSE, 0, S_FALSE, NULL }, + { TDN_BUTTON_CLICKED, ID_START_BUTTON + 99, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_send_all_common_button_click_with_command[] = +{ + { WM_COMMAND, MAKEWORD(IDOK, BN_CLICKED), 0 }, + { WM_COMMAND, MAKEWORD(IDYES, BN_CLICKED), 0 }, + { WM_COMMAND, MAKEWORD(IDNO, BN_CLICKED), 0 }, + { WM_COMMAND, MAKEWORD(IDCANCEL, BN_CLICKED), 0 }, + { WM_COMMAND, MAKEWORD(IDRETRY, BN_CLICKED), 0 }, + { WM_COMMAND, MAKEWORD(IDCLOSE, BN_CLICKED), 0 }, + { WM_COMMAND, MAKEWORD(ID_START_BUTTON + 99, BN_CLICKED), 0 }, + { WM_COMMAND, MAKEWORD(IDOK, BN_CLICKED), 0 }, + { 0 } +}; + +static const struct message_info msg_press_nonexistent_buttons_with_command[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_all_common_button_click_with_command }, + { TDN_BUTTON_CLICKED, ID_START_BUTTON, 0, S_FALSE, NULL }, + { TDN_BUTTON_CLICKED, ID_START_BUTTON, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_send_nonexistent_radio_button_click[] = +{ + { TDM_CLICK_RADIO_BUTTON, ID_START_RADIO_BUTTON + 99, 0 }, + { TDM_CLICK_BUTTON, IDOK, 0 }, + { 0 } +}; + +static const struct message_info msg_press_nonexistent_radio_button[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_nonexistent_radio_button_click }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_return_default_verification_unchecked[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_return_default_verification_checked[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_uncheck_verification[] = +{ + { TDM_CLICK_VERIFICATION, FALSE, 0 }, + { 0 } +}; + +static const struct message_info msg_return_verification_unchecked[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_uncheck_verification }, + { TDN_VERIFICATION_CLICKED, FALSE, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_check_verification[] = +{ + { TDM_CLICK_VERIFICATION, TRUE, 0 }, + { 0 } +}; + +static const struct message_info msg_return_verification_checked[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_check_verification }, + { TDN_VERIFICATION_CLICKED, TRUE, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static TASKDIALOGCONFIG navigated_info = {0}; + +static const struct message_info msg_send_navigate[] = +{ + { TDM_NAVIGATE_PAGE, 0, (LPARAM)&navigated_info, 0}, + { 0 } +}; + +static const struct message_info msg_return_navigated_page[] = +{ + { TDN_CREATED, 0, 0, S_OK, NULL }, + { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON, 0, S_OK, msg_send_navigate }, + { TDN_DIALOG_CONSTRUCTED, 0, 0, S_OK, NULL }, + { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON, 0, S_OK, NULL }, + { TDN_NAVIGATED, 0, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_send_close[] = +{ + { WM_CLOSE, 0, 0, 0}, + { 0 } +}; + +static const struct message_info msg_handle_wm_close[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_close }, + { TDN_BUTTON_CLICKED, IDCANCEL, 0, S_FALSE, msg_send_close }, + { TDN_BUTTON_CLICKED, IDCANCEL, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_send_close_then_ok[] = +{ + { WM_CLOSE, 0, 0, 0}, + { TDM_CLICK_BUTTON, IDOK, 0 }, + { 0 } +}; + +static const struct message_info msg_handle_wm_close_without_cancel_button[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_close_then_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + static void init_test_message(UINT message, WPARAM wParam, LPARAM lParam, struct message *msg) { msg->message = WM_TD_CALLBACK; @@ -131,16 +392,18 @@ static void init_test_message(UINT message, WPARAM wParam, LPARAM lParam, struct msg->stage = 0; } -#define run_test(info, expect_button, seq, context) \ - run_test_(info, expect_button, seq, context, \ - sizeof(seq)/sizeof(seq[0]) - 1, __FILE__, __LINE__) +#define run_test(info, expect_button, expect_radio_button, verification_checked, seq, context) \ + run_test_(info, expect_button, expect_radio_button, verification_checked, seq, context, \ + ARRAY_SIZE(seq) - 1, __FILE__, __LINE__) -static void run_test_(TASKDIALOGCONFIG *info, int expect_button, const struct message_info *test_messages, - const char *context, int test_messages_len, const char *file, int line) +static void run_test_(TASKDIALOGCONFIG *info, int expect_button, int expect_radio_button, BOOL verification_checked, + const struct message_info *test_messages, const char *context, int test_messages_len, + const char *file, int line) { struct message *msg, *msg_start; int ret_button = 0; int ret_radio = 0; + BOOL ret_verification = FALSE; HRESULT hr; int i; @@ -157,12 +420,14 @@ static void run_test_(TASKDIALOGCONFIG *info, int expect_button, const struct me current_message_info = test_messages; flush_sequences(sequences, NUM_MSG_SEQUENCES); - hr = pTaskDialogIndirect(info, &ret_button, &ret_radio, NULL); + hr = pTaskDialogIndirect(info, &ret_button, &ret_radio, &ret_verification); ok_(file, line)(hr == S_OK, "TaskDialogIndirect() failed, got %#x.\n", hr); ok_sequence_(sequences, TASKDIALOG_SEQ_INDEX, msg_start, context, FALSE, file, line); ok_(file, line)(ret_button == expect_button, "Wrong button. Expected %d, got %d\n", expect_button, ret_button); + ok_(file, line)(ret_radio == expect_radio_button, + "Wrong radio button. Expected %d, got %d\n", expect_radio_button, ret_radio); heap_free(msg_start); } @@ -220,16 +485,17 @@ static void test_callback(void) info.pfCallback = taskdialog_callback_proc; info.lpCallbackData = test_ref_data; - run_test(&info, IDOK, msg_return_press_ok, "Press VK_RETURN."); + run_test(&info, IDOK, 0, FALSE, msg_return_press_ok, "Press VK_RETURN."); } static void test_buttons(void) { TASKDIALOGCONFIG info = {0}; - - TASKDIALOG_BUTTON custom_buttons[TEST_NUM_BUTTONS]; + static const DWORD command_link_flags[] = {0, TDF_USE_COMMAND_LINKS, TDF_USE_COMMAND_LINKS_NO_ICON}; + TASKDIALOG_BUTTON custom_buttons[TEST_NUM_BUTTONS], radio_buttons[TEST_NUM_RADIO_BUTTONS]; const WCHAR button_format[] = {'%','0','2','d',0}; - WCHAR button_titles[TEST_NUM_BUTTONS * 3]; /* Each button has two digits as title, plus null-terminator */ + /* Each button has two digits as title, plus null-terminator */ + WCHAR button_titles[TEST_NUM_BUTTONS * 3], radio_button_titles[TEST_NUM_BUTTONS * 3]; int i; info.cbSize = sizeof(TASKDIALOGCONFIG); @@ -247,48 +513,359 @@ static void test_buttons(void) } custom_buttons[TEST_NUM_BUTTONS - 1].nButtonID = -1; + /* Init radio buttons */ + for (i = 0; i < TEST_NUM_RADIO_BUTTONS; i++) + { + WCHAR *text = &radio_button_titles[i * 3]; + wsprintfW(text, button_format, i); + + radio_buttons[i].pszButtonText = text; + radio_buttons[i].nButtonID = ID_START_RADIO_BUTTON + i; + } + radio_buttons[TEST_NUM_RADIO_BUTTONS - 1].nButtonID = -2; + /* Test nDefaultButton */ /* Test common buttons with invalid default ID */ info.nDefaultButton = 0; /* Should default to first created button */ info.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_YES_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON; - run_test(&info, IDOK, msg_return_press_ok, "default button: unset default"); + run_test(&info, IDOK, 0, FALSE, msg_return_press_ok, "default button: unset default"); info.dwCommonButtons = TDCBF_YES_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON; - run_test(&info, IDYES, msg_return_press_yes, "default button: unset default"); + run_test(&info, IDYES, 0, FALSE, msg_return_press_yes, "default button: unset default"); info.dwCommonButtons = TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON; - run_test(&info, IDNO, msg_return_press_no, "default button: unset default"); + run_test(&info, IDNO, 0, FALSE, msg_return_press_no, "default button: unset default"); info.dwCommonButtons = TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON; - run_test(&info, IDRETRY, msg_return_press_retry, "default button: unset default"); + run_test(&info, IDRETRY, 0, FALSE, msg_return_press_retry, "default button: unset default"); info.dwCommonButtons = TDCBF_CANCEL_BUTTON | TDCBF_CLOSE_BUTTON; - run_test(&info, IDCANCEL, msg_return_press_cancel, "default button: unset default"); + run_test(&info, IDCANCEL, 0, FALSE, msg_return_press_cancel, "default button: unset default"); + + /* Custom buttons could be command links */ + for (i = 0; i < ARRAY_SIZE(command_link_flags); i++) + { + info.dwFlags = command_link_flags[i]; + + /* Test with all common and custom buttons and invalid default ID */ + info.nDefaultButton = 0xff; /* Random ID, should also default to first created button */ + info.cButtons = TEST_NUM_BUTTONS; + info.pButtons = custom_buttons; + run_test(&info, ID_START_BUTTON, 0, FALSE, msg_return_press_custom1, + "default button: invalid default, with common buttons - 1"); + + info.nDefaultButton = -1; /* Should work despite button ID -1 */ + run_test(&info, -1, 0, FALSE, msg_return_press_custom10, "default button: invalid default, with common buttons - 2"); + + info.nDefaultButton = -2; /* Should also default to first created button */ + run_test(&info, ID_START_BUTTON, 0, FALSE, msg_return_press_custom1, + "default button: invalid default, with common buttons - 3"); + + /* Test with only custom buttons and invalid default ID */ + info.dwCommonButtons = 0; + run_test(&info, ID_START_BUTTON, 0, FALSE, msg_return_press_custom1, + "default button: invalid default, no common buttons"); + + /* Test with common and custom buttons and valid default ID */ + info.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_YES_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON + | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON; + info.nDefaultButton = IDRETRY; + run_test(&info, IDRETRY, 0, FALSE, msg_return_press_retry, "default button: valid default - 1"); + + /* Test with common and custom buttons and valid default ID */ + info.nDefaultButton = ID_START_BUTTON + 3; + run_test(&info, ID_START_BUTTON + 3, 0, FALSE, msg_return_press_custom4, "default button: valid default - 2"); + } - /* Test with all common and custom buttons and invalid default ID */ - info.nDefaultButton = 0xff; /* Random ID, should also default to first created button */ + /* Test radio buttons */ + info.nDefaultButton = 0; + info.cButtons = 0; + info.pButtons = 0; + info.dwCommonButtons = TDCBF_OK_BUTTON; + info.cRadioButtons = TEST_NUM_RADIO_BUTTONS; + info.pRadioButtons = radio_buttons; + + /* Test default first radio button */ + run_test(&info, IDOK, ID_START_RADIO_BUTTON, FALSE, msg_return_default_radio_button_1, + "default radio button: default first radio button"); + + /* Test default radio button */ + info.nDefaultRadioButton = ID_START_RADIO_BUTTON + 1; + run_test(&info, IDOK, info.nDefaultRadioButton, FALSE, msg_return_default_radio_button_2, + "default radio button: default radio button"); + + /* Test default radio button with -2 */ + info.nDefaultRadioButton = -2; + run_test(&info, IDOK, info.nDefaultRadioButton, FALSE, msg_return_default_radio_button_3, + "default radio button: default radio button with id -2"); + + /* Test default radio button after clicking the first, messages still work even radio button is disabled */ + info.nDefaultRadioButton = ID_START_RADIO_BUTTON + 1; + run_test(&info, IDOK, ID_START_RADIO_BUTTON, FALSE, msg_return_first_radio_button, + "default radio button: radio button after clicking"); + + /* Test radio button after disabling and clicking the first */ + info.nDefaultRadioButton = ID_START_RADIO_BUTTON + 1; + run_test(&info, IDOK, ID_START_RADIO_BUTTON, FALSE, msg_return_default_radio_button_clicking_disabled, + "default radio button: disable radio button before clicking"); + + /* Test no default radio button, TDF_NO_DEFAULT_RADIO_BUTTON is set, TDN_RADIO_BUTTON_CLICKED will still be received, just radio button not selected */ + info.nDefaultRadioButton = ID_START_RADIO_BUTTON; + info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON; + run_test(&info, IDOK, info.nDefaultRadioButton, FALSE, msg_return_no_default_radio_button_flag, + "default radio button: no default radio flag"); + + /* Test no default radio button, TDF_NO_DEFAULT_RADIO_BUTTON is set and nDefaultRadioButton is 0. + * TDN_RADIO_BUTTON_CLICKED will not be sent, and just radio button not selected */ + info.nDefaultRadioButton = 0; + info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON; + run_test(&info, IDOK, 0, FALSE, msg_return_no_default_radio_button_id_and_flag, + "default radio button: no default radio id and flag"); + + /* Test no default radio button, TDF_NO_DEFAULT_RADIO_BUTTON is set and nDefaultRadioButton is invalid. + * TDN_RADIO_BUTTON_CLICKED will not be sent, and just radio button not selected */ + info.nDefaultRadioButton = 0xff; + info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON; + run_test(&info, IDOK, 0, FALSE, msg_return_no_default_radio_button_id_and_flag, + "default radio button: no default flag, invalid id"); + + info.nDefaultRadioButton = 0; + info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON; + run_test(&info, IDOK, -2, FALSE, msg_return_press_negative_id_radio_button, + "radio button: manually click radio button with negative id"); + + /* Test sending clicks to non-existent buttons. Notification of non-existent buttons will be sent */ info.cButtons = TEST_NUM_BUTTONS; info.pButtons = custom_buttons; - run_test(&info, ID_START_BUTTON, msg_return_press_custom1, "default button: invalid default, with common buttons - 1"); + info.cRadioButtons = TEST_NUM_RADIO_BUTTONS; + info.pRadioButtons = radio_buttons; + info.dwCommonButtons = 0; + info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON; + run_test(&info, ID_START_BUTTON + 99, 0, FALSE, msg_press_nonexistent_buttons, "sends click to non-existent buttons"); - info.nDefaultButton = -1; /* Should work despite button ID -1 */ - run_test(&info, -1, msg_return_press_custom10, "default button: invalid default, with common buttons - 2"); + /* Non-existent button clicks sent by WM_COMMAND won't generate TDN_BUTTON_CLICKED except IDOK. + * And will get the first existent button identifier instead of IDOK */ + run_test(&info, ID_START_BUTTON, 0, FALSE, msg_press_nonexistent_buttons_with_command, + "sends click to non-existent buttons with WM_COMMAND"); - info.nDefaultButton = -2; /* Should also default to first created button */ - run_test(&info, ID_START_BUTTON, msg_return_press_custom1, "default button: invalid default, with common buttons - 3"); + /* Non-existent radio button won't get notifications */ + run_test(&info, IDOK, 0, FALSE, msg_press_nonexistent_radio_button, "sends click to non-existent radio buttons"); +} - /* Test with only custom buttons and invalid default ID */ - info.dwCommonButtons = 0; - run_test(&info, ID_START_BUTTON, msg_return_press_custom1, "default button: invalid default, no common buttons"); +static void test_help(void) +{ + TASKDIALOGCONFIG info = {0}; - /* Test with common and custom buttons and valid default ID */ - info.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_YES_BUTTON | TDCBF_NO_BUTTON - | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON; - info.nDefaultButton = IDRETRY; - run_test(&info, IDRETRY, msg_return_press_retry, "default button: valid default - 1"); + info.cbSize = sizeof(TASKDIALOGCONFIG); + info.pfCallback = taskdialog_callback_proc; + info.lpCallbackData = test_ref_data; + info.dwCommonButtons = TDCBF_OK_BUTTON; + + run_test(&info, IDOK, 0, FALSE, msg_got_tdn_help, "send f1"); +} + +struct timer_notification_data +{ + DWORD last_elapsed_ms; + DWORD num_fired; +}; + +static HRESULT CALLBACK taskdialog_callback_proc_timer(HWND hwnd, UINT notification, + WPARAM wParam, LPARAM lParam, LONG_PTR ref_data) +{ + struct timer_notification_data *data = (struct timer_notification_data *)ref_data; + + if (notification == TDN_TIMER) + { + DWORD elapsed_ms; + int delta; + + elapsed_ms = (DWORD)wParam; + + if (data->num_fired == 3) + ok(data->last_elapsed_ms > elapsed_ms, "Expected reference time update.\n"); + else + { + delta = elapsed_ms - data->last_elapsed_ms; + ok(delta > 0, "Expected positive time tick difference.\n"); + } + data->last_elapsed_ms = elapsed_ms; + + if (data->num_fired == 3) + PostMessageW(hwnd, TDM_CLICK_BUTTON, IDOK, 0); + + ++data->num_fired; + return data->num_fired == 3 ? S_FALSE : S_OK; + } + + return S_OK; +} + +static void test_timer(void) +{ + struct timer_notification_data data = { 0 }; + TASKDIALOGCONFIG info = { 0 }; + + info.cbSize = sizeof(TASKDIALOGCONFIG); + info.pfCallback = taskdialog_callback_proc_timer; + info.lpCallbackData = (LONG_PTR)&data; + info.dwFlags = TDF_CALLBACK_TIMER; + info.dwCommonButtons = TDCBF_OK_BUTTON; + + pTaskDialogIndirect(&info, NULL, NULL, NULL); +} + +static HRESULT CALLBACK taskdialog_callback_proc_progress_bar(HWND hwnd, UINT notification, WPARAM wParam, + LPARAM lParam, LONG_PTR ref_data) +{ + unsigned long ret; + LONG flags = (LONG)ref_data; + if (notification == TDN_CREATED) + { + /* TDM_SET_PROGRESS_BAR_STATE */ + ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_STATE, PBST_NORMAL, 0); + ok(ret == PBST_NORMAL, "Expect state: %d got state: %lx\n", PBST_NORMAL, ret); + ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_STATE, PBST_PAUSED, 0); + ok(ret == PBST_NORMAL, "Expect state: %d got state: %lx\n", PBST_NORMAL, ret); + ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_STATE, PBST_ERROR, 0); + /* Progress bar has fixme on handling PBM_SETSTATE message */ + todo_wine ok(ret == PBST_PAUSED, "Expect state: %d got state: %lx\n", PBST_PAUSED, ret); + ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_STATE, PBST_NORMAL, 0); + todo_wine ok(ret == PBST_ERROR, "Expect state: %d got state: %lx\n", PBST_ERROR, ret); + + /* TDM_SET_PROGRESS_BAR_RANGE */ + ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_RANGE, 0, MAKELPARAM(0, 200)); + ok(ret == MAKELONG(0, 100), "Expect range:%x got:%lx\n", MAKELONG(0, 100), ret); + ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_RANGE, 0, MAKELPARAM(0, 200)); + ok(ret == MAKELONG(0, 200), "Expect range:%x got:%lx\n", MAKELONG(0, 200), ret); + + /* TDM_SET_PROGRESS_BAR_POS */ + if (flags & TDF_SHOW_MARQUEE_PROGRESS_BAR) + { + ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_POS, 1, 0); + ok(ret == 0, "Expect position:%x got:%lx\n", 0, ret); + ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_POS, 2, 0); + ok(ret == 0, "Expect position:%x got:%lx\n", 0, ret); + } + else + { + ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_POS, 1, 0); + ok(ret == 0, "Expect position:%x got:%lx\n", 0, ret); + ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_POS, 2, 0); + ok(ret == 1, "Expect position:%x got:%lx\n", 1, ret); + } + + SendMessageW(hwnd, TDM_CLICK_BUTTON, IDOK, 0); + } + + return S_OK; +} + +static void test_progress_bar(void) +{ + TASKDIALOGCONFIG info = {0}; + + info.cbSize = sizeof(TASKDIALOGCONFIG); + info.dwFlags = TDF_SHOW_PROGRESS_BAR; + info.pfCallback = taskdialog_callback_proc_progress_bar; + info.lpCallbackData = (LONG_PTR)info.dwFlags; + info.dwCommonButtons = TDCBF_OK_BUTTON; + pTaskDialogIndirect(&info, NULL, NULL, NULL); + + info.dwFlags = TDF_SHOW_MARQUEE_PROGRESS_BAR; + info.lpCallbackData = (LONG_PTR)info.dwFlags; + pTaskDialogIndirect(&info, NULL, NULL, NULL); + + info.dwFlags = TDF_SHOW_PROGRESS_BAR | TDF_SHOW_MARQUEE_PROGRESS_BAR; + info.lpCallbackData = (LONG_PTR)info.dwFlags; + pTaskDialogIndirect(&info, NULL, NULL, NULL); +} + +static void test_verification_box(void) +{ + TASKDIALOGCONFIG info = {0}; + WCHAR textW[] = {'t', 'e', 'x', 't', 0}; + + info.cbSize = sizeof(TASKDIALOGCONFIG); + info.pfCallback = taskdialog_callback_proc; + info.lpCallbackData = test_ref_data; + info.dwCommonButtons = TDCBF_OK_BUTTON; + + /* TDF_VERIFICATION_FLAG_CHECKED works even if pszVerificationText is not set */ + run_test(&info, IDOK, 0, FALSE, msg_return_default_verification_unchecked, "default verification box: unchecked"); + + info.dwFlags = TDF_VERIFICATION_FLAG_CHECKED; + run_test(&info, IDOK, 0, FALSE, msg_return_default_verification_checked, "default verification box: checked"); + + info.pszVerificationText = textW; + run_test(&info, IDOK, 0, FALSE, msg_return_default_verification_unchecked, "default verification box: unchecked"); + + info.dwFlags = TDF_VERIFICATION_FLAG_CHECKED; + run_test(&info, IDOK, 0, FALSE, msg_return_default_verification_checked, "default verification box: checked"); + + run_test(&info, IDOK, 0, FALSE, msg_return_verification_unchecked, + "default verification box: default checked and then unchecked"); + + info.dwFlags = 0; + run_test(&info, IDOK, 0, FALSE, msg_return_verification_checked, + "default verification box: default unchecked and then checked"); +} + +static void test_navigate_page(void) +{ + TASKDIALOGCONFIG info = {0}; + static const WCHAR textW[] = {'t', 'e', 'x', 't', 0}; + static const WCHAR button_format[] = {'%', '0', '2', 'd', 0}; + TASKDIALOG_BUTTON radio_buttons[TEST_NUM_RADIO_BUTTONS]; + WCHAR radio_button_titles[TEST_NUM_BUTTONS * 3]; + int i; + + /* Init radio buttons */ + for (i = 0; i < TEST_NUM_RADIO_BUTTONS; i++) + { + WCHAR *text = &radio_button_titles[i * 3]; + wsprintfW(text, button_format, i); + + radio_buttons[i].pszButtonText = text; + radio_buttons[i].nButtonID = ID_START_RADIO_BUTTON + i; + } + + info.cbSize = sizeof(TASKDIALOGCONFIG); + info.pfCallback = taskdialog_callback_proc; + info.lpCallbackData = test_ref_data; + info.dwCommonButtons = TDCBF_OK_BUTTON; + info.cRadioButtons = TEST_NUM_RADIO_BUTTONS; + info.pRadioButtons = radio_buttons; + + navigated_info = info; + navigated_info.pszVerificationText = textW; + navigated_info.dwFlags = TDF_VERIFICATION_FLAG_CHECKED; + + run_test(&info, IDOK, ID_START_RADIO_BUTTON, TRUE, msg_return_navigated_page, "navigate page: default"); + + /* TDM_NAVIGATE_PAGE doesn't check cbSize. + * And null taskconfig pointer crash applicatioin, thus doesn't check pointer either */ + navigated_info.cbSize = 0; + run_test(&info, IDOK, ID_START_RADIO_BUTTON, TRUE, msg_return_navigated_page, "navigate page: invalid taskconfig cbSize"); +} + +static void test_wm_close(void) +{ + TASKDIALOGCONFIG info = {0}; + + info.cbSize = sizeof(TASKDIALOGCONFIG); + info.pfCallback = taskdialog_callback_proc; + info.lpCallbackData = test_ref_data; + ... 537 lines suppressed ...
6 years
1
0
0
0
[reactos] 01/01: [PSDK] Update commctrl.h. CORE-15682
by Amine Khaldi
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=07f4be3faf6af846dd4e9…
commit 07f4be3faf6af846dd4e93be2490c8a5ac361ad9 Author: Amine Khaldi <amine.khaldi(a)reactos.org> AuthorDate: Fri Jan 25 13:15:42 2019 +0100 Commit: Amine Khaldi <amine.khaldi(a)reactos.org> CommitDate: Fri Jan 25 13:15:42 2019 +0100 [PSDK] Update commctrl.h. CORE-15682 --- sdk/include/psdk/commctrl.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sdk/include/psdk/commctrl.h b/sdk/include/psdk/commctrl.h index 8809f26a6f..9c99741083 100644 --- a/sdk/include/psdk/commctrl.h +++ b/sdk/include/psdk/commctrl.h @@ -845,6 +845,8 @@ extern "C" { #define HDN_ENDFILTEREDIT (HDN_FIRST-15) #define HDN_ITEMSTATEICONCLICK (HDN_FIRST-16) #define HDN_ITEMKEYDOWN (HDN_FIRST-17) +#define HDN_DROPDOWN (HDN_FIRST-18) +#define HDN_OVERFLOWCLICK (HDN_FIRST-19) #define HDN_ITEMCHANGING __MINGW_NAME_AW(HDN_ITEMCHANGING) #define HDN_ITEMCHANGED __MINGW_NAME_AW(HDN_ITEMCHANGED) @@ -3130,6 +3132,9 @@ extern "C" { #define LVN_GETDISPINFO __MINGW_NAME_AW(LVN_GETDISPINFO) #define LVN_SETDISPINFO __MINGW_NAME_AW(LVN_SETDISPINFO) +#define LVN_INCREMENTALSEARCHA (LVN_FIRST-62) +#define LVN_INCREMENTALSEARCHW (LVN_FIRST-63) + #define LVIF_DI_SETITEM 0x1000 #define LV_DISPINFOA NMLVDISPINFOA @@ -4319,6 +4324,8 @@ typedef struct { #define DTM_GETMCFONT (DTM_FIRST+10) #define DateTime_GetMonthCalFont(hdp) SNDMSG(hdp,DTM_GETMCFONT,0,0) +#define DTM_GETIDEALSIZE (DTM_FIRST+15) + #define DTS_UPDOWN 0x1 #define DTS_SHOWNONE 0x2 #define DTS_SHORTDATEFORMAT 0x0 @@ -4618,6 +4625,10 @@ typedef struct { #define BCM_SETSHIELD (BCM_FIRST+0xC) #define Button_SetElevationRequiredState(hwnd,frequired) (BOOL)SNDMSG((hwnd),BCM_SETSHIELD,0,(LPARAM)(frequired)) +#define BCM_SETNOTE (BCM_FIRST + 9) +#define BCM_GETNOTE (BCM_FIRST + 10) +#define BCM_GETNOTELENGTH (BCM_FIRST + 11) + typedef struct tagNMBCHOTITEM { NMHDR hdr; DWORD dwFlags;
6 years
1
0
0
0
[reactos] 01/01: [CMD_WINETEST] Sync with Wine Staging 4.0. CORE-15682
by Amine Khaldi
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=58aee30e9967642d82d2a…
commit 58aee30e9967642d82d2a425e7e7d3e4c759adc2 Author: Amine Khaldi <amine.khaldi(a)reactos.org> AuthorDate: Fri Jan 25 13:15:06 2019 +0100 Commit: Amine Khaldi <amine.khaldi(a)reactos.org> CommitDate: Fri Jan 25 13:15:06 2019 +0100 [CMD_WINETEST] Sync with Wine Staging 4.0. CORE-15682 --- modules/rostests/winetests/cmd/batch.c | 19 +- modules/rostests/winetests/cmd/test_builtins.cmd | 299 ++++++++++++++++++++- .../rostests/winetests/cmd/test_builtins.cmd.exp | 122 +++++++-- 3 files changed, 416 insertions(+), 24 deletions(-) diff --git a/modules/rostests/winetests/cmd/batch.c b/modules/rostests/winetests/cmd/batch.c index 71966090e9..162e9cfa15 100644 --- a/modules/rostests/winetests/cmd/batch.c +++ b/modules/rostests/winetests/cmd/batch.c @@ -25,7 +25,7 @@ static char workdir[MAX_PATH]; static DWORD workdir_len; static char drive[2]; -static const DWORD drive_len = sizeof(drive)/sizeof(drive[0]); +static const DWORD drive_len = ARRAY_SIZE(drive); static char path[MAX_PATH]; static DWORD path_len; static char shortpath[MAX_PATH]; @@ -165,6 +165,7 @@ static const char *compare_line(const char *out_line, const char *out_end, const static const char path_cmd[] = {'@','p','a','t','h','@'}; static const char shortpath_cmd[] = {'@','s','h','o','r','t','p','a','t','h','@'}; static const char space_cmd[] = {'@','s','p','a','c','e','@'}; + static const char spaces_cmd[] = {'@','s','p','a','c','e','s','@'}; static const char tab_cmd[] = {'@','t','a','b','@'}; static const char or_broken_cmd[] = {'@','o','r','_','b','r','o','k','e','n','@'}; @@ -223,6 +224,15 @@ static const char *compare_line(const char *out_line, const char *out_end, const } else { err = out_end; } + }else if(exp_ptr+sizeof(spaces_cmd) <= exp_end + && !memcmp(exp_ptr, spaces_cmd, sizeof(spaces_cmd))) { + exp_ptr += sizeof(spaces_cmd); + if(out_ptr < out_end && *out_ptr == ' ') { + while (out_ptr < out_end && *out_ptr == ' ') out_ptr++; + continue; + } else { + err = out_end; + } }else if(exp_ptr+sizeof(tab_cmd) <= exp_end && !memcmp(exp_ptr, tab_cmd, sizeof(tab_cmd))) { exp_ptr += sizeof(tab_cmd); @@ -250,10 +260,8 @@ static const char *compare_line(const char *out_line, const char *out_end, const while(exp_ptr+sizeof(or_broken_cmd) <= exp_end && memcmp(exp_ptr, or_broken_cmd, sizeof(or_broken_cmd))) exp_ptr++; - if(!exp_ptr) - return err; - exp_ptr += sizeof(or_broken_cmd); + if (exp_ptr > exp_end) return err; out_ptr = out_line; err = NULL; continue; @@ -469,8 +477,7 @@ START_TEST(batch) } else { path_len = 1; /* \ */ } - shortpath_len = GetShortPathNameA(path, shortpath, - sizeof(shortpath)/sizeof(shortpath[0])); + shortpath_len = GetShortPathNameA(path, shortpath, ARRAY_SIZE(shortpath)); argc = winetest_get_mainargs(&argv); if(argc > 2) diff --git a/modules/rostests/winetests/cmd/test_builtins.cmd b/modules/rostests/winetests/cmd/test_builtins.cmd index f6846283c7..f4dfc9f26d 100644 --- a/modules/rostests/winetests/cmd/test_builtins.cmd +++ b/modules/rostests/winetests/cmd/test_builtins.cmd @@ -179,6 +179,8 @@ if exist foo (type foo) else echo not supported echo --- redirections within IF statements if 1==1 echo foo1>bar type bar & del bar +if 1==1 echo foo2>>bar +type bar & del bar echo ----- if 1==1 (echo foo2>bar) else echo baz2>bar type bar & del bar @@ -418,6 +420,7 @@ if 1==1 (echo n1) else echo n2|echo n3 if 1==1 (echo o1) else echo o2&&echo o3 if 1==1 (echo p1) else echo p2||echo p3 if 1==1 (echo q1) else echo q2&echo q3 +echo --- echo --- chain else (if false) if 1==0 echo a1 else echo a2 if 1==0 echo b1|echo b2 else echo b3 @@ -506,6 +509,65 @@ rem Only the final quote ends the string set "WINE_FOO=apple"banana"grape"orange echo '%WINE_FOO%' set WINE_FOO= +rem set PATH must work with quotes +set PATH_BACKUP=%PATH% +mkdir folder +mkdir "fol;der" +echo echo I'm here! > "fol;der\sub1.bat" +echo echo I'm here! > folder\sub1.bat +set PATH=nothing;"fol;der" +call sub1 +set PATH="folder +call sub1 +set PATH=folder" +call sub1 +del "fol;der\sub1.bat" +del folder\sub1.bat +rmdir "fol;der" +rmdir folder +PATH=%PATH_BACKUP% + +echo ------------ Testing 'choice' ------------ + +rem Windows XP and Windows 2000 do not come with choice +rem echo is used for @or_broken@ formatting +choice /C:ABC /M "Example message" /D A /T:0 +if %ERRORLEVEL% EQU 9009 ( + echo choice unavailable +) +echo %ERRORLEVEL% +choice /C ABC "/M:Example message" /D:B /T 0 +if %ERRORLEVEL% EQU 9009 ( + echo choice unavailable +) +echo %ERRORLEVEL% +choice /C def /D:f /T:0 +if %ERRORLEVEL% EQU 9009 ( + echo choice unavailable +) +echo %ERRORLEVEL% +REM If a pipe fails due to a nonexistent command +REM it will stop the whole program's execution +if %ERRORLEVEL% NEQ 9009 ( + echo Y | choice /C ABCXYZ /D A /T 2 +) +if %ERRORLEVEL% EQU 9009 ( + echo choice unavailable +) +echo %ERRORLEVEL% +choice /C ABC /N /D A /T 0 +if %ERRORLEVEL% EQU 9009 ( + echo choice unavailable +) +echo %ERRORLEVEL% +choice /C abcABC /CS /D:A /T:0 +if %ERRORLEVEL% EQU 9009 ( + echo choice unavailable +) +echo %ERRORLEVEL% +rem intentional error +choice /C abcABC /D:A /T:0 >NUL 2>NUL +echo %ERRORLEVEL% echo ------------ Testing variable expansion ------------ call :setError 0 @@ -667,6 +729,15 @@ echo '%~xs1' goto :eof :endEchoFuns +echo ------------ Testing parameter zero ------------ +call :func parm1 parm2 +goto :endParm0 +:func +echo %~0 %~1 +echo [%0] [%~d0] [%~p0] [%~n0] [%~x0] [%~s0] +goto :EOF +:endParm0 + echo ------------ Testing variable delayed expansion ------------ rem NT4 doesn't support this echo --- default mode (load-time expansion) @@ -907,6 +978,13 @@ if %elseIF% == 1 ( ) else ( echo else if seems to be broken ) +if "x" == "a" ( + echo broken1 +) else ( + echo expected1 + if "y" == "b" echo broken2 + echo expected post-embedded if +) echo --- case sensitivity with and without /i option if bar==BAR echo if does not default to case sensitivity if not bar==BAR echo if seems to default to case sensitivity @@ -979,6 +1057,69 @@ for %%i in (%WINE_STR_PARMS%) do ( for %%i in (%WINE_STR_PARMS%) do ( for %%j in (%WINE_STR_PARMS%) do ( call :GTRtest %%i %%j)) + +echo ------------ Testing if/exist ------------ +mkdir subdir +echo something>subdir\bar +echo something else>foo +if exist foo ( + echo exist explicit works +) else ( + echo ERROR exist explicit broken +) +if exist bar ( + echo ERROR exist explicit unknown file broken +) else ( + echo exist explicit unknown file works +) +if exist subdir\bar ( + echo exist explicit in subdir works +) else ( + echo ERROR exist explicit in subdir broken +) +if exist fo* ( + echo exist simple wildcard works +) else ( + echo ERROR exist simple wildcard broken +) +if exist subdir\ba* ( + echo exist wildcard works +) else ( + echo ERROR exist wildcard broken +) +if not exist subdir\ba* ( + echo ERROR negate exist wildcard broken +) else ( + echo negate exist wildcard works +) +if exist idontexist\ba* ( + echo ERROR exist wildcard bad subdir broken +) else ( + echo exist wildcard bad subdir broken works +) +if exist subdir ( + echo exist subdir ok +) else ( + echo ERROR exist subdir not working +) +if exist subdir\. ( + echo exist subdir with . ok +) else ( + echo ERROR exist subdir with . not working +) +if exist subdir\ ( + echo exist subdir with \ ok +) else ( + echo ERROR exist subdir with \ not working +) +if exist "subdir\" ( + echo exist subdir with \ and quotes ok +) else ( + echo ERROR exist subdir with \ and quotes not working +) +del foo subdir\bar +rd subdir + echo ------ for numbers if -1 LSS 1 (echo negative numbers handled) if not -1 LSS -10 (echo negative numbers handled) @@ -1112,9 +1253,16 @@ mkdir foobar & cd foobar mkdir foo mkdir bar mkdir baz +mkdir pop echo > bazbaz echo --- basic wildcards for %%i in (ba*) do echo %%i +echo --- wildcards in subdirs +echo something>pop\bar1 +echo something>pop\bar2.txt +echo something>pop\bar3 +for %%f in (pop\ba*) do ( call echo %%f ) +rmdir /s/q pop echo --- for /d for /d %%i in (baz foo bar) do echo %%i 2>&1 rem Confirm we don't match files: @@ -1321,6 +1469,45 @@ for /L %%i in (2,2,1) do ( echo %%i echo FAILED ) +echo --- rems inside for loops +for /f %%i IN ("hello") DO ( + REM foo|echo ERROR unexpected execution 1 + @REM foo|echo ERROR unexpected execution 2 + @ REM foo|echo ERROR unexpected execution 3 +) +echo --- ifs inside for loops +for %%i in (test) do ( + echo a1 + if 1==1 ( + echo b1 + ) else ( + echo c1 + ) + echo d1 +) +for %%i in (test) do ( + echo a2 + if 1==1 ( + echo b2 + ) else echo c2 + echo d2 +) +for %%i in (test) do ( + echo a3 + if 1==0 ( + echo b3 + ) else echo c3 + echo d3 +) +for %%i in (test) do ( + echo a4 + if 1==0 ( + echo b4 + ) else ( + echo c4 + ) + echo d4 +) echo --- set /a goto :testseta @@ -1563,8 +1750,11 @@ mkdir foobar & cd foobar echo ------ string argument rem NT4 does not support usebackq for /F %%i in ("a b c") do echo %%i +for /F %%i in ( "a b c" ) do echo X%%iX for /f usebackq %%i in ('a b c') do echo %%i>output_file if not exist output_file (echo no output) else (type output_file & del output_file) +for /f usebackq %%i in ( 'a b c' ) do echo X%%iX>output_file +if not exist output_file (echo no output) else (type output_file & del output_file) for /f %%i in ("a ") do echo %%i for /f usebackq %%i in ('a ') do echo %%i>output_file if not exist output_file (echo no output) else (type output_file & del output_file) @@ -1614,9 +1804,13 @@ if "%CD%"=="" goto :SkipFORFcmdNT4 for /f %%i in ('echo.Passed1') do echo %%i for /f "usebackq" %%i in (`echo.Passed2`) do echo %%i for /f usebackq %%i in (`echo.Passed3`) do echo %%i +for /f "usebackq" %%i in (`"c:\windows\system32\cmd.exe" /C echo Passed4`) do echo %%i +for /f "usebackq" %%i in (`""c:\windows\system32\cmd.exe" /C echo Passed5"`) do echo %%i +for /f %%i in ( 'echo.Passed6' ) do echo %%i +for /f "usebackq" %%i in ( `echo.Passed7` ) do echo %%i goto :ContinueFORF :SkipFORFcmdNT4 -for /l %%i in (1,1,3) do echo Missing functionality - Broken%%i +for /l %%i in (1,1,7) do echo Missing functionality - Broken%%i :ContinueFORF rem FIXME: Rest not testable right now in wine: not implemented and would need rem preliminary grep-like program implementation (e.g. like findstr or fc) even @@ -1694,6 +1888,13 @@ for /f "tokens=1,2,3*" %%i in ("a b c d e f g") do echo h=%%h i=%%i j=%%j k=%%k for /f "tokens=1,1,3*" %%i in ("a b c d e f g") do echo h=%%h i=%%i j=%%j k=%%k l=%%l m=%%m n=%%n o=%%o for /f "tokens=2,2,3*" %%i in ("a b c d e f g") do echo h=%%h i=%%i j=%%j k=%%k l=%%l m=%%m n=%%n o=%%o for /f "tokens=3,2,3*" %%i in ("a b c d e f g") do echo h=%%h i=%%i j=%%j k=%%k l=%%l m=%%m n=%%n o=%%o +rem Special case tokens=* or tokens=n,* +echo 3.14>testfile +FOR /F "tokens=*" %%A IN (testfile) DO @echo 1:%%A,%%B +FOR /F "tokens=1*" %%A IN (testfile) DO @echo 2:%%A,%%B +FOR /F "tokens=2*" %%A IN (testfile) DO @echo 3:%%A,%%B +FOR /F "tokens=1,* delims=." %%A IN (testfile) DO @echo 4:%%A,%%B +del testfile cd .. rd /s/q foobar echo ------ parameter splitting @@ -1706,6 +1907,12 @@ goto :forFParameterSplittingEnd echo %~0 %~1 %~2 %~3 %~4 %~5 goto :eof :forFParameterSplittingEnd +echo 3.14>testfile +FOR /F "delims=. tokens=*" %%A IN (testfile) DO @echo 4:%%A,%%B +FOR /F "delims=. tokens=1*" %%A IN (testfile) DO @echo 5:%%A,%%B +FOR /F "delims=. tokens=2*" %%A IN (testfile) DO @echo 6:%%A,%%B +FOR /F "delims=. tokens=3*" %%A IN (testfile) DO @echo 7:%%A,%%B +del testfile echo ------------ Testing del ------------ echo abc > file @@ -2977,6 +3184,57 @@ echo FAILURE at dest 10 :dest10:this is also ignored echo Correctly ignored trailing information +rem Testing which label is reached when there are many options +echo Begin: +set nextlabel= +call :sub +set nextlabel=middle +goto :sub + +:sub +echo ..First sub +if not "%nextlabel%"=="" goto :%nextlabel% +goto :EOF + +:sub +echo ..Second sub +if not "%nextlabel%"=="" goto :%nextlabel% +goto :EOF + +:middle +echo Middle: +set nextlabel= +call :sub +set nextlabel=nearend +goto :sub + +:sub +echo ..Third sub +if not "%nextlabel%"=="" goto :%nextlabel% +goto :EOF + +:nearend +echo Near end: +set nextlabel= +call :sub +set nextlabel=end +goto :sub + +:sub +echo ..Fourth sub +if not "%nextlabel%"=="" goto :%nextlabel% +goto :EOF + +:end +echo At end: +set nextlabel= +call :sub +set nextlabel=done +goto :sub + +:done +echo Finished + echo ------------ Testing PATH ------------ set WINE_backup_path=%path% set path=original @@ -2988,6 +3246,45 @@ path set path=%WINE_backup_path% set WINE_backup_path= +echo ------------ Testing start /W ------------ +echo start /W failed to wait>foobar.txt +start /W "" cmd /C "ping -n1 & echo start /W seems to really wait>foobar.txt"& type foobar.txt& del foobar.txt + +echo ------------ Testing changing the drive letter ---------- +pushd C:\ + +echo Normal: +call :setError 0 +C: +if errorlevel 1 echo Normal drive change failed + +echo Normal+space +call :setError 0 +C:@space@ +if errorlevel 1 echo Normal+space drive change failed + +echo Normal+space+garbage +call :setError 0 +C: garbage +if errorlevel 1 echo Normal+space+garbage drive change failed + +call :setError 0 +echo Quoted should fail +"C:" +if not errorlevel 1 echo quoted drive change unexpectedly worked + +echo Normal+tab +call :setError 0 +C:@tab@ +if errorlevel 1 echo Normal+tab drive change failed + +echo Normal+tab+garbage +call :setError 0 +C:@tab@garbagetab +if errorlevel 1 echo Normal+tab+garbage drive change failed + +popd + echo ------------ Testing combined CALLs/GOTOs ------------ echo @echo off>foo.cmd echo goto :eof>>foot.cmd diff --git a/modules/rostests/winetests/cmd/test_builtins.cmd.exp b/modules/rostests/winetests/cmd/test_builtins.cmd.exp index 5512fee072..d78d91a6cf 100644 --- a/modules/rostests/winetests/cmd/test_builtins.cmd.exp +++ b/modules/rostests/winetests/cmd/test_builtins.cmd.exp @@ -205,7 +205,8 @@ food21 @todo_wine@foo7@space@@space@@or_broken@not supported@space@ @todo_wine@foo@or_broken@not supported --- redirections within IF statements -@todo_wine@foo1 +foo1 +foo2 ----- foo2 foo3 @@ -428,8 +429,9 @@ n1 o1 p1 q1 +@todo_wine@--- --- chain else (if false) -@todo_wine@j3 +j3 --- k3 l3 @@ -474,6 +476,23 @@ foo 'jim fred' 'jim' 'apple"banana"grape' +I'm here!@space@ +I'm here!@space@ +I'm here!@space@ +------------ Testing 'choice' ------------ +@todo_wine@Example message [A,B,C]?A@or_broken@choice unavailable +@todo_wine@1@or_broken@9009 +@todo_wine@Example message [A,B,C]?B@or_broken@choice unavailable +@todo_wine@2@or_broken@9009 +@todo_wine@[D,E,F]?F@or_broken@choice unavailable +@todo_wine@3@or_broken@9009 +@todo_wine@[A,B,C,X,Y,Z]?Y@or_broken@choice unavailable +@todo_wine@5@or_broken@9009 +@todo_wine@A@or_broken@choice unavailable +@todo_wine@1@or_broken@9009 +@todo_wine@[a,b,c,A,B,C]?A@or_broken@choice unavailable +@todo_wine@4@or_broken@9009 +@todo_wine@255@or_broken@9009 ------------ Testing variable expansion ------------ ~p0 should be path containing batch file @path@ @@ -526,9 +545,9 @@ N '.OOL' '.TABC' '' -@todo_wine@'@drive@@shortpath@R S'@or_broken@'' -@todo_wine@'@drive@@shortpath@T'@or_broken@'' -@todo_wine@'@drive@@shortpath@ABCDEFGHIJK.LMNOP'@or_broken@'' +'@drive@@shortpath@R S'@or_broken@'' +'@drive@@shortpath@T'@or_broken@'' +'@drive@@shortpath@ABCDEFGHIJK.LMNOP'@or_broken@'' ''@or_broken@'%~ai' ''@or_broken@'%~ai' '--a------'@or_broken@'--a--------'@or_broken@'--a--c---'@or_broken@'%~ai' @@ -562,9 +581,9 @@ N '.OOL' '.TABC' '' -@todo_wine@'@drive@@shortpath@R S'@or_broken@'' -@todo_wine@'@drive@@shortpath@T'@or_broken@'' -@todo_wine@'@drive@@shortpath@ABCDEFGHIJK.LMNOP'@or_broken@'' +'@drive@@shortpath@R S'@or_broken@'' +'@drive@@shortpath@T'@or_broken@'' +'@drive@@shortpath@ABCDEFGHIJK.LMNOP'@or_broken@'' @drive@@path@ @drive@@path@ @drive@ @@ -573,6 +592,9 @@ N @drive@ '' '.eh'@or_broken@'' +------------ Testing parameter zero ------------ +:func parm1 +[:func] [@drive@] [@path@] [test] [.cmd] [@drive@@shortpath@test.cmd] ------------ Testing variable delayed expansion ------------ --- default mode (load-time expansion) foo @@ -657,6 +679,8 @@ if seems not to detect /c as parameter else if seems to work else if seems to work else if seems to work +expected1 +expected post-embedded if --- case sensitivity with and without /i option if seems to default to case sensitivity if /i seems to work @@ -768,6 +792,18 @@ BA GTR B BA GTR AB BA GTR AA AA GTR A +------------ Testing if/exist ------------ +exist explicit works +exist explicit unknown file works +exist explicit in subdir works +exist simple wildcard works +exist wildcard works +negate exist wildcard works +exist wildcard bad subdir broken works +exist subdir ok +exist subdir with . ok +exist subdir with \ ok +exist subdir with \ and quotes ok ------ for numbers negative numbers handled negative numbers handled @@ -909,6 +945,10 @@ B C B D --- basic wildcards bazbaz +--- wildcards in subdirs +pop\bar1@space@ +pop\bar2.txt@space@ +pop\bar3@space@ --- for /d baz@space@ foo@space@ @@ -970,6 +1010,20 @@ ErrorLevel 0 -1 1 3 +--- rems inside for loops +--- ifs inside for loops +a1 +b1 +d1 +a2 +b2 +d2 +a3 +c3 +d3 +a4 +c4 +d4 --- set /a ------ individual operations WINE_foo correctly 3 @@ -1141,7 +1195,9 @@ WINE_bar correctly 6@or_broken@ERROR: WINE_bar incorrectly 5 [6] --- for /F ------ string argument a +XaX a@or_broken@no output +XaX@or_broken@no output a a@or_broken@no output a @@ -1177,6 +1233,10 @@ c Passed1@or_broken@Missing functionality - Broken1 Passed2@or_broken@Missing functionality - Broken2 Passed3@or_broken@Missing functionality - Broken3 +Passed4@or_broken@Missing functionality - Broken4 +Passed5@or_broken@Missing functionality - Broken5 +Passed6@or_broken@Missing functionality - Broken6 +Passed7@or_broken@Missing functionality - Broken7 ------ eol option and@or_broken@Broken NT4 functionality1 Line@or_broken@Broken NT4 functionality2 @@ -1222,9 +1282,15 @@ h=%h i=a j=b k=c l=d e f g m=%m n=%n o=%o@or_broken@h=%h i=a j=b k=c l=d e f g m h=%h i=a j=c k= l= m=%m n=%n o=%o@or_broken@h=%h i=a j=c k= l= m= n=%n o=%o h=%h i=b j=c k= l= m=%m n=%n o=%o@or_broken@h=%h i=b j=c k= l= m= n=%n o=%o h=%h i=b j=c k= l= m=%m n=%n o=%o@or_broken@h=%h i=b j=c k= l= m= n=%n o=%o +1:3.14,%B +2:3.14, +4:3,14 ------ parameter splitting :forFParameterSplittingFunc myparam1=myvalue1 myparam2=myparam2 mytest@space@@space@@space@ :forFParameterSplittingFunc myparam1=myvalue1 myparam2=myparam2 mytest@space@@space@@space@ +4:3.14,%B +5:3,14 +6:14, ------------ Testing del ------------ deleting 'file' errorlevel is 0, good @@ -1340,22 +1406,22 @@ bar\baz removed @drive@@path@foobar @pwd@ ------------ Testing attrib ------------ -A @drive@@path@foobar\foo@or_broken@A @drive@@path@foobar\foo@or_broken@A I @drive@@path@foobar\foo +A@spaces@@drive@@path@foobar\foo@or_broken@A I@spaces@@drive@@path@foobar\foo --- read-only attribute -A R @drive@@path@foobar\foo@or_broken@A R @drive@@path@foobar\foo@or_broken@A R I @drive@@path@foobar\foo +A R@spaces@@drive@@path@foobar\foo@or_broken@A R I@spaces@@drive@@path@foobar\foo foo foo original contents Read-only file not deleted Read-only file forcibly deleted --- recursive behaviour -A @drive@@path@foobar\baz\level2@or_broken@A @drive@@path@foobar\baz\level2@or_broken@A I @drive@@path@foobar\baz\level2 -A R @drive@@path@foobar\level1@or_broken@A R @drive@@path@foobar\level1@or_broken@A R I @drive@@path@foobar\level1 -A R @drive@@path@foobar\baz\level2@or_broken@A R @drive@@path@foobar\baz\level2@or_broken@A R I @drive@@path@foobar\baz\level2 -A @drive@@path@foobar\bar@or_broken@A @drive@@path@foobar\bar@or_broken@A I @drive@@path@foobar\bar +A@spaces@@drive@@path@foobar\baz\level2@or_broken@A I@spaces@@drive@@path@foobar\baz\level2 +A R@spaces@@drive@@path@foobar\level1@or_broken@A R I@spaces@@drive@@path@foobar\level1 +A R@spaces@@drive@@path@foobar\baz\level2@or_broken@A R I@spaces@@drive@@path@foobar\baz\level2 +A@spaces@@drive@@path@foobar\bar@or_broken@A I@spaces@@drive@@path@foobar\bar --- folders processing - @drive@@path@foobar@or_broken@ @drive@@path@foobar@or_broken@ I @drive@@path@foobar -@todo_wine@ R @drive@@path@foobar\baz@or_broken@ R @drive@@path@foobar\baz@or_broken@ @drive@@path@foobar\baz@or_broken@ R I @drive@@path@foobar\baz -A @drive@@path@foobar\baz\toto@or_broken@A @drive@@path@foobar\baz\toto@or_broken@A I @drive@@path@foobar\baz\toto +@spaces@@drive@@path@foobar@or_broken@ I@spaces@@drive@@path@foobar +@todo_wine@ R@spaces@@drive@@path@foobar\baz@or_broken@@spaces@@drive@@path@foobar\baz@or_broken@ R I@spaces@@drive@@path@foobar\baz +A@spaces@@drive@@path@foobar\baz\toto@or_broken@A I@spaces@@drive@@path@foobar\baz\toto toto lulu file created in read-only dir @@ -1570,10 +1636,32 @@ goto with redirections worked Ignoring double colons worked label with mixed whitespace and no echo worked Correctly ignored trailing information +Begin: +..First sub +..First sub +Middle: +..Third sub +..Third sub +Near end: +..Fourth sub +..Fourth sub +At end: +..First sub +..First sub +Finished ------------ Testing PATH ------------ PATH=original PATH=try2 PATH=try3 +------------ Testing start /W ------------ +start /W seems to really wait +------------ Testing changing the drive letter ---------- +Normal: +Normal+space +Normal+space+garbage +Quoted should fail +Normal+tab +Normal+tab+garbage ------------ Testing combined CALLs/GOTOs ------------ world cheball
6 years
1
0
0
0
[reactos] 01/01: [AVIFIL32] Sync with Wine Staging 4.0. CORE-15682
by Amine Khaldi
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=379c53db187aa48a34c7f…
commit 379c53db187aa48a34c7f9bad94728bddf26ee81 Author: Amine Khaldi <amine.khaldi(a)reactos.org> AuthorDate: Fri Jan 25 13:14:32 2019 +0100 Commit: Amine Khaldi <amine.khaldi(a)reactos.org> CommitDate: Fri Jan 25 13:14:32 2019 +0100 [AVIFIL32] Sync with Wine Staging 4.0. CORE-15682 --- dll/win32/avifil32/api.c | 9 ++++----- media/doc/README.WINE | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/dll/win32/avifil32/api.c b/dll/win32/avifil32/api.c index 63a3530591..37fa94169a 100644 --- a/dll/win32/avifil32/api.c +++ b/dll/win32/avifil32/api.c @@ -1119,10 +1119,10 @@ HRESULT WINAPI AVIBuildFilterW(LPWSTR szFilter, LONG cbFilter, BOOL fSaving) HeapFree(GetProcessHeap(), 0, lp); /* add "All files" "*.*" filter if enough space left */ - size = LoadStringW(AVIFILE_hModule, IDS_ALLFILES, - szAllFiles, (sizeof(szAllFiles) - sizeof(all_files))/sizeof(WCHAR)) + 1; + size = LoadStringW(AVIFILE_hModule, IDS_ALLFILES, szAllFiles, + ARRAY_SIZE(szAllFiles) - ARRAY_SIZE(all_files)) + 1; memcpy( szAllFiles + size, all_files, sizeof(all_files) ); - size += sizeof(all_files) / sizeof(WCHAR); + size += ARRAY_SIZE(all_files); if (cbFilter > size) { memcpy(szFilter, szAllFiles, size * sizeof(szAllFiles[0])); @@ -2109,8 +2109,7 @@ HRESULT WINAPI EditStreamSetInfoA(PAVISTREAM pstream, LPAVISTREAMINFOA asi, return AVIERR_BADSIZE; memcpy(&asiw, asi, sizeof(asiw) - sizeof(asiw.szName)); - MultiByteToWideChar(CP_ACP, 0, asi->szName, -1, - asiw.szName, sizeof(asiw.szName)/sizeof(WCHAR)); + MultiByteToWideChar(CP_ACP, 0, asi->szName, -1, asiw.szName, ARRAY_SIZE(asiw.szName)); return EditStreamSetInfoW(pstream, &asiw, sizeof(asiw)); } diff --git a/media/doc/README.WINE b/media/doc/README.WINE index 6b8579bf94..e17ef7cf84 100644 --- a/media/doc/README.WINE +++ b/media/doc/README.WINE @@ -48,7 +48,7 @@ reactos/dll/win32/advpack # Synced to WineStaging-4.0 reactos/dll/win32/atl # Synced to WineStaging-4.0 reactos/dll/win32/atl80 # Synced to WineStaging-3.3 reactos/dll/win32/atl100 # Synced to WineStaging-3.3 -reactos/dll/win32/avifil32 # Synced to WineStaging-3.9 +reactos/dll/win32/avifil32 # Synced to WineStaging-4.0 reactos/dll/win32/bcrypt # Synced to WineStaging-1.9.23 reactos/dll/win32/browseui # Out of sync reactos/dll/win32/cabinet # Synced to WineStaging-3.3
6 years
1
0
0
0
[reactos] 01/01: [ATL100_WINETEST] Sync with Wine Staging 4.0. CORE-15682
by Amine Khaldi
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=0d35c5159be80cb09d813…
commit 0d35c5159be80cb09d81348ececf402283f06743 Author: Amine Khaldi <amine.khaldi(a)reactos.org> AuthorDate: Fri Jan 25 13:14:00 2019 +0100 Commit: Amine Khaldi <amine.khaldi(a)reactos.org> CommitDate: Fri Jan 25 13:14:00 2019 +0100 [ATL100_WINETEST] Sync with Wine Staging 4.0. CORE-15682 --- modules/rostests/winetests/atl100/atl.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/rostests/winetests/atl100/atl.c b/modules/rostests/winetests/atl100/atl.c index fc195cb85b..4adc2f6f3d 100644 --- a/modules/rostests/winetests/atl100/atl.c +++ b/modules/rostests/winetests/atl100/atl.c @@ -304,8 +304,8 @@ static void test_typelib(void) FreeLibrary(inst); len = SysStringLen(path); - ok(len > sizeof(scrrun_dll_suffixW)/sizeof(WCHAR) - && lstrcmpiW(path+len-sizeof(scrrun_dll_suffixW)/sizeof(WCHAR), scrrun_dll_suffixW), + ok(len > ARRAY_SIZE(scrrun_dll_suffixW) + && lstrcmpiW(path+len-ARRAY_SIZE(scrrun_dll_suffixW), scrrun_dll_suffixW), "unexpected path %s\n", wine_dbgstr_w(path)); SysFreeString(path); ok(typelib != NULL, "typelib == NULL\n"); @@ -320,8 +320,8 @@ static void test_typelib(void) FreeLibrary(inst); len = SysStringLen(path); - ok(len > sizeof(mshtml_tlb_suffixW)/sizeof(WCHAR) - && lstrcmpiW(path+len-sizeof(mshtml_tlb_suffixW)/sizeof(WCHAR), mshtml_tlb_suffixW), + ok(len > ARRAY_SIZE(mshtml_tlb_suffixW) + && lstrcmpiW(path+len-ARRAY_SIZE(mshtml_tlb_suffixW), mshtml_tlb_suffixW), "unexpected path %s\n", wine_dbgstr_w(path)); SysFreeString(path); ok(typelib != NULL, "typelib == NULL\n"); @@ -964,6 +964,8 @@ static void test_AtlAxCreateControl(void) ok(hr == S_OK, "got 0x%08x\n", hr); ok(container != NULL, "returned %p!\n", container); ok(control != NULL, "returned %p\n", control); + IUnknown_Release(container); + IUnknown_Release(control); DestroyWindow(hwnd); container = NULL;
6 years
1
0
0
0
← Newer
1
...
8
9
10
11
12
13
14
...
30
Older →
Jump to page:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Results per page:
10
25
50
100
200