If the read ProgID only has a CurVer key and no CLSID key, it will directly return CO_E_CCLASSSTRING, causing the program to fail to continue running.
Add the key value for finding CurVer.
-- v13: combase:clsid_from_string_reg() add read of CurVer key value. ole32:Do not zero out when not a CLSID ole32/tests:Remove now succeeding todo_wine
From: Maotong Zhang zmtong1988@gmail.com
--- dlls/ole32/tests/compobj.c | 11 ----------- 1 file changed, 11 deletions(-)
diff --git a/dlls/ole32/tests/compobj.c b/dlls/ole32/tests/compobj.c index 3f358a8ef8e..b5a4e88b132 100644 --- a/dlls/ole32/tests/compobj.c +++ b/dlls/ole32/tests/compobj.c @@ -526,15 +526,11 @@ static void test_CLSIDFromProgID(void) ok(!ret, "Failed to create a test key.\n");
hr = CLSIDFromProgID(L"MyApp.DocumentTest", &clsid); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(IsEqualCLSID(&clsid, &CLSID_non_existent), "Unexpected clsid %s.\n", wine_dbgstr_guid(&clsid));
hr = CLSIDFromProgID(L"MyApp.DocumentTest.1", &clsid); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(IsEqualCLSID(&clsid, &CLSID_non_existent), "Unexpected clsid %s.\n", wine_dbgstr_guid(&clsid));
hr = CLSIDFromProgID(L"MyApp.DocumentTest.2", &clsid); @@ -552,15 +548,11 @@ static void test_CLSIDFromProgID(void) ok(IsEqualCLSID(&clsid, &CLSID_NULL), "Unexpected clsid %s.\n", wine_dbgstr_guid(&clsid));
hr = CLSIDFromString(L"MyApp.DocumentTest", &clsid); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(IsEqualCLSID(&clsid, &CLSID_non_existent), "Unexpected clsid %s.\n", wine_dbgstr_guid(&clsid));
hr = CLSIDFromString(L"MyApp.DocumentTest.1", &clsid); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(IsEqualCLSID(&clsid, &CLSID_non_existent), "Unexpected clsid %s.\n", wine_dbgstr_guid(&clsid));
hr = CLSIDFromString(L"MyApp.DocumentTest.2", &clsid); @@ -569,15 +561,12 @@ static void test_CLSIDFromProgID(void)
clsid = CLSID_StdFont; hr = CLSIDFromString(L"MyApp.DocumentTest.3", &clsid); - todo_wine ok(hr == REGDB_E_INVALIDVALUE, "Unexpected hr %#lx.\n", hr); - todo_wine ok(IsEqualCLSID(&clsid, &CLSID_StdFont), "Unexpected clsid %s.\n", wine_dbgstr_guid(&clsid));
clsid = CLSID_StdFont; hr = CLSIDFromString(L"MyApp.DocumentTest.5", &clsid); ok(hr == CO_E_CLASSSTRING, "Unexpected hr %#lx.\n", hr); - todo_wine ok(IsEqualCLSID(&clsid, &CLSID_StdFont), "Unexpected clsid %s.\n", wine_dbgstr_guid(&clsid));
RegDeleteTreeW(HKEY_CLASSES_ROOT, L"MyApp.DocumentTest");
From: Maotong Zhang zmtong1988@gmail.com
--- dlls/combase/combase.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/dlls/combase/combase.c b/dlls/combase/combase.c index b3a1c9bd8fc..dee1df02697 100644 --- a/dlls/combase/combase.c +++ b/dlls/combase/combase.c @@ -1333,8 +1333,11 @@ static BOOL guid_from_string(LPCWSTR s, GUID *id)
if (!s || s[0] != '{') { - memset(id, 0, sizeof(*id)); - if (!s) return TRUE; + if (!s) + { + memset(id, 0, sizeof(*id)); + return TRUE; + } return FALSE; }
From: Maotong Zhang zmtong1988@gmail.com
--- dlls/combase/combase.c | 136 ++++++++++++++++++++++++++++++++++------- 1 file changed, 114 insertions(+), 22 deletions(-)
diff --git a/dlls/combase/combase.c b/dlls/combase/combase.c index dee1df02697..f1aa74cc356 100644 --- a/dlls/combase/combase.c +++ b/dlls/combase/combase.c @@ -1386,35 +1386,92 @@ static BOOL guid_from_string(LPCWSTR s, GUID *id) return FALSE; }
-static HRESULT clsid_from_string_reg(LPCOLESTR progid, CLSID *clsid) +struct visited_node { - WCHAR buf2[CHARS_IN_GUID]; - LONG buf2len = sizeof(buf2); - HKEY xhkey; - WCHAR *buf; + LPCOLESTR progid; + struct visited_node *next; +};
- memset(clsid, 0, sizeof(*clsid)); - buf = malloc((lstrlenW(progid) + 8) * sizeof(WCHAR)); - if (!buf) return E_OUTOFMEMORY; +static HRESULT clsid_from_string_reg(LPCOLESTR progid, CLSID *clsid, BOOL forceassign) +{ + HRESULT ret = CO_E_CLASSSTRING; + HKEY xhkey = NULL; + WCHAR szclsid[256] = {0}; + LONG cbclsid = sizeof(szclsid); + static struct visited_node *visitedhead = NULL; + struct visited_node *cur = NULL, *p = NULL; + BOOL hitloop = FALSE;
- lstrcpyW(buf, progid); - lstrcatW(buf, L"\CLSID"); - if (open_classes_key(HKEY_CLASSES_ROOT, buf, MAXIMUM_ALLOWED, &xhkey)) + if (progid == NULL) { - free(buf); - WARN("couldn't open key for ProgID %s\n", debugstr_w(progid)); + *clsid = CLSID_NULL; + return E_INVALIDARG; + } + + if (*progid == 0) return CO_E_CLASSSTRING; + + for (p = visitedhead; p; p = p->next) + { + if (lstrcmpiW(progid, p->progid) == 0) + { + hitloop = TRUE; + break; + } } - free(buf);
- if (RegQueryValueW(xhkey, NULL, buf2, &buf2len)) + if (hitloop) + return forceassign ? CO_E_CLASSSTRING : REGDB_E_INVALIDVALUE; + + cur = (struct visited_node *)LocalAlloc(LMEM_FIXED, sizeof(struct visited_node)); + if (!cur) + return E_OUTOFMEMORY; + + cur->progid = progid; + cur->next = visitedhead; + visitedhead = cur; + + if (SUCCEEDED(open_classes_key(HKEY_CLASSES_ROOT, progid, MAXIMUM_ALLOWED, &xhkey))) { - RegCloseKey(xhkey); - WARN("couldn't query clsid value for ProgID %s\n", debugstr_w(progid)); - return CO_E_CLASSSTRING; + if (RegQueryValueW(xhkey, L"CLSID", szclsid, &cbclsid) != ERROR_SUCCESS) + { + LONG cbcurver = 0; + if (RegQueryValueW(xhkey, L"CurVer", NULL, &cbcurver) == ERROR_SUCCESS) + { + if (cbcurver > 0) + { + WCHAR *szcurver = (WCHAR *)LocalAlloc(LMEM_FIXED, cbcurver); + if (szcurver && RegQueryValueW(xhkey, L"CurVer", szcurver, &cbcurver) == ERROR_SUCCESS) + { + if (cbcurver >= sizeof(WCHAR)) + szcurver[(cbcurver / sizeof(WCHAR)) - 1] = 0; + + if (szcurver[0] != 0 && lstrcmpiW(szcurver, progid) != 0) + { + RegCloseKey(xhkey); + ret = clsid_from_string_reg(szcurver, clsid, forceassign); + LocalFree(szcurver); + goto cleanup; + } + } + if (szcurver) LocalFree(szcurver); + } + } + } + else + ret = guid_from_string(szclsid, clsid) ? S_OK : REGDB_E_INVALIDVALUE; } - RegCloseKey(xhkey); - return guid_from_string(buf2, clsid) ? S_OK : CO_E_CLASSSTRING; + +cleanup: + if (xhkey) + RegCloseKey(xhkey); + + if (visitedhead == cur) + visitedhead = cur->next; + + LocalFree(cur); + + return ret; }
/****************************************************************************** @@ -1423,6 +1480,7 @@ static HRESULT clsid_from_string_reg(LPCOLESTR progid, CLSID *clsid) HRESULT WINAPI DECLSPEC_HOTPATCH CLSIDFromProgID(LPCOLESTR progid, CLSID *clsid) { ACTCTX_SECTION_KEYED_DATA data; + HRESULT hr;
if (!progid || !clsid) return E_INVALIDARG; @@ -1437,7 +1495,11 @@ HRESULT WINAPI DECLSPEC_HOTPATCH CLSIDFromProgID(LPCOLESTR progid, CLSID *clsid) return S_OK; }
- return clsid_from_string_reg(progid, clsid); + hr = clsid_from_string_reg(progid, clsid, TRUE); + if (FAILED(hr)) + *clsid = GUID_NULL; + + return hr; }
/****************************************************************************** @@ -1461,13 +1523,43 @@ HRESULT WINAPI CLSIDFromString(LPCOLESTR str, LPCLSID clsid) if (!clsid) return E_INVALIDARG;
+ if (str == NULL) + { + *clsid = GUID_NULL; + return S_OK; + } + + if (*str == 0) + return CO_E_CLASSSTRING; + if (guid_from_string(str, clsid)) return S_OK;
/* It appears a ProgID is also valid */ - hr = clsid_from_string_reg(str, &tmp_id); + hr = clsid_from_string_reg(str, &tmp_id, FALSE); if (SUCCEEDED(hr)) + { *clsid = tmp_id; + return hr; + } + + /* Validate the content of str: only allow letters, digits, and '.' + If any other character is found, reset clsid to GUID_NULL and return CO_E_CLASSSTRING */ + if(str[0] != '{') + { + for (size_t i = 0; str[i]; i++) + { + WCHAR c = str[i]; + if (!((c >= L'0' && c <= L'9') || + (c >= L'a' && c <= L'z') || + (c >= L'A' && c <= L'Z') || + (c == L'.'))) + { + *clsid = GUID_NULL; + return CO_E_CLASSSTRING; + } + } + }
return hr; }
Do you have an actual application that depends on this?
On Mon Oct 20 03:09:17 2025 +0000, Nikolay Sivov wrote:
Do you have an actual application that depends on this?
There is currently no application that depends on this component. I wrote a simple test program, and it behaves the same as your test program.
According to the MSDN documentation for CLSIDFromProgID, if the specified ProgID cannot be found, the function will create an OLE 1 CLSID and add a corresponding CLSID entry in the registry.
During my tests, I noticed that on Windows, it looks up the CurVer key, while on Wine, it does not.