我有一些使用 Windows Update API 执行 Microsoft Update 扫描的内部代码。因为有些客户端没有直接的 Internet 访问权限,所以我明确设置该WebProxy
属性指向我们的本地代理服务器。在测试期间(在 Windows 7 上),这似乎工作得很好。现在我在 Windows 10 上测试它(见脚注 1),似乎代理设置被忽略了。
Windows Update 客户端在 Windows 10中进行了重大修改,因此这可能是新版本客户端中的错误或未记录的限制,但另一方面,我以前使用 COM 的经验很少,所以我可能做错了什么.
观察结果,使用下面发布的测试代码:
代码在 Windows 7 上按需要运行,无论它在什么安全上下文中运行,也无论客户端是否具有直接 Internet 访问权限和/或在用户的 Internet 设置中配置的代理服务器。
该代码也适用于 Windows 10,前提是客户端可以直接访问 Internet。
如果客户端无法直接访问 Internet,但运行它的用户在其 Internet 设置中配置了合适的代理服务器,则该代码主要适用于 Windows 10。(见脚注 2。)
如果客户端没有直接的 Internet 访问权限并且运行它的用户没有配置合适的代理,则该代码在 Windows 10 上不起作用。它没有连接到代码中指定的代理,而是尝试连接到一系列外部 IP 地址;一旦所有这些连接尝试最终超时,它会在显示的行上返回 0x80072ee2, ERROR_INTERNET_TIMEOUT。(通过省略设置代理服务器的代码部分,我可以在 Windows 7 上获得非常相似的行为。)
此外,如果我故意将代码中的代理 URL 更改为指向不存在的服务器,则代码在 Windows 7 上停止运行,如预期的那样,但在 Windows 10 上的行为没有改变。因此,看起来 Windows 更新确实只是忽略了该WebProxy
属性。(正在设置属性;我可以从IUpdateSession
对象中读取它。)
更改传递优化下载模式似乎没有帮助。我已经尝试了通过组策略提供的所有不同模式。在代理 URL 中添加尾部斜杠会破坏 Windows 7 的代码,对 Windows 10 没有任何影响。使用纯 DNS 名称而不是 URL 在 Windows 7 上有效,但在 Windows 10 上没有任何区别。
由于代码最终旨在成为系统服务的一部分和/或远程运行,因此在用户级别配置代理设置不是一个理想的选择,尽管如果没有其他解决方案可用,我可能会依赖它。
这是我一直在测试的代码,是原始代码的缩减版。测试代码实际上不会处理结果(如果有的话),因为问题发生在该点之前。我隐藏了我们代理服务器的真实 DNS 名称,但 URL 是显示的形式。任何想要在他们自己的环境中测试代码的人当然都需要将它指向他们自己的代理。
#include <windows.h>
#include <wuapi.h>
#include <stdio.h>
#define stringize1(x) L#x
#define stringize(x) stringize1(x)
#define fail() fail_fn(L"Fatal error at line " stringize(__LINE__))
void fail_fn(wchar_t * msg)
{
wprintf(L"%s\n", msg);
exit(1);
}
int wmain(int argc, wchar_t ** argv)
{
IUpdateSearcher* updateSearcher;
IWebProxy* webProxy;
IUpdateServiceManager2* serviceManager;
IUpdateServiceRegistration* serviceRegistration;
IUpdateSession* updateSession;
ISearchResult* results;
BSTR searchString, proxyString, bstrServiceID;
HRESULT hr;
if((hr = CoInitialize(NULL)) != S_OK) {
fail();
}
hr = CoCreateInstance(&CLSID_UpdateServiceManager, NULL, CLSCTX_INPROC_SERVER,
&IID_IUpdateServiceManager2, (void **)&serviceManager);
if (hr != S_OK) fail();
bstrServiceID = SysAllocString(L"7971f918-a847-4430-9279-4a52d1efe18d");
serviceManager->lpVtbl->AddService2(serviceManager, bstrServiceID,
asfAllowPendingRegistration | asfRegisterServiceWithAU,
NULL, &serviceRegistration);
if (hr != S_OK) fail();
hr = CoCreateInstance(&CLSID_UpdateSession, NULL, CLSCTX_INPROC_SERVER,
&IID_IUpdateSession, (LPVOID*)&updateSession);
if (hr != S_OK) fail();
hr = CoCreateInstance(&CLSID_WebProxy, NULL, CLSCTX_INPROC_SERVER,
&IID_IWebProxy, (void **)&webProxy);
if (hr != S_OK) fail();
hr = webProxy->lpVtbl->put_AutoDetect(webProxy, VARIANT_FALSE);
if (hr != S_OK) fail();
proxyString = SysAllocString(L"http://proxy.contoso.co.nz:80");
if (proxyString == NULL) fail();
hr = webProxy->lpVtbl->put_Address(webProxy, proxyString);
if (hr != S_OK) fail();
hr = updateSession->lpVtbl->put_WebProxy(updateSession, webProxy);
if (hr != S_OK) fail();
hr = updateSession->lpVtbl->CreateUpdateSearcher(updateSession, &updateSearcher);
if (hr != S_OK) fail();
hr = updateSearcher->lpVtbl->put_ServerSelection(updateSearcher, ssOthers);
if (hr != S_OK) fail();
hr = updateSearcher->lpVtbl->put_ServiceID(updateSearcher, bstrServiceID);
if (hr != S_OK) fail();
searchString = SysAllocString(L"IsInstalled=0 and Type='Software'");
hr = updateSearcher->lpVtbl->Search(updateSearcher, searchString, &results);
if (hr != S_OK) /* fails here */
{
wprintf(L"Error %0x\n", hr);
fail();
}
wprintf(L"Update search completed successfully.\n");
CoUninitialize();
exit(0);
}
有什么办法可以让 Windows 10 上的这项工作与 Windows 7 上的工作方式相同吗?
(1)我运行的是Windows 10 LTSB 2016,这个和Windows 10 1607版基本相同,也就是Windows 10 Anniversary Update。我的大多数客户都没有 3 月份的更新,但其他方面都是最新的。我还确认在安装了 3 月更新的客户端上仍然会出现问题。
(2) 在测试过程中,使用用户配置的代理有两次失败,都是在新重装的机器上;一旦它开始工作,它就会继续工作。在这种情况下,扫描仍会尝试连接到各种外部 IP 地址,但这些连接超时的事实不会导致扫描失败。我怀疑这一切都与传递优化下载模式有关,但我仍在试验。
附录:运行代码的用户帐户在其 Internet 设置中配置了合适的代理服务器的情况仅在代码以交互方式运行时有效。在非交互式上下文中,例如服务或计划任务,这不起作用。目前,在我看来,除非您可以直接访问 Internet,否则在 Windows 10 机器上从服务访问 Microsoft Update 是不可能的。