我成功地调用CoSetProxyBlanket上的代理(如果那是它正确的术语),然后我调用QueryInterface上相同的代理服务器,但我收到的0X80070005结果( “拒绝访问”)。但是,如果我第一次打电话CoInitializeSecurity (我试图避免)具有相同的凭据,然后调用成功。
问:
我怎样才能顺利拿到我需要的接口,而不必调用CoInitializeSecurity?据我所知,一个进程只能调用这个方法一次,因此它与制作dll不兼容,通常可以用对CoSetProxyBlanket的调用来代替。
详情:
我与建设自己的OPC客户端,可以不匹配的用户帐户不同的域运行的计算机进行通信实验。
首先,我创建了域名,用户名和密码,身份结构,它是在服务器上有效:
COAUTHINFO authInfo;
COAUTHIDENTITY authIdentity;
authIdentity.Domain = (unsigned short *) w_domain;
authIdentity.DomainLength = wcslen(w_domain);
authIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
authIdentity.Password = (unsigned short *) w_password;
authIdentity.PasswordLength = wcslen(w_password);
authIdentity.User = (unsigned short *) w_username;
authIdentity.UserLength = wcslen(w_username);
authInfo.dwAuthnLevel = RPC_C_AUTHN_LEVEL_CALL;
authInfo.dwAuthnSvc = RPC_C_AUTHN_WINNT;
authInfo.dwAuthzSvc = RPC_C_AUTHZ_NONE;
authInfo.dwCapabilities = EOAC_NONE;
authInfo.dwImpersonationLevel = RPC_C_IMP_LEVEL_IMPERSONATE;
authInfo.pAuthIdentityData = &authIdentity;
authInfo.pwszServerPrincName = NULL;
ServerInfo.pAuthInfo = &authInfo;
然后我可以调用CoCreateInstanceEx
与此服务器信息的获取句柄(m_IOPCServer
)到我的OPC服务器(IID_IOPCServer
)。
我获得句柄后,我发现,这是必要再次设置更多的权限(见How does impersonation in DCOM work?)与此调用:
hr = CoSetProxyBlanket(m_IOPCServer, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE,
&authIdentity, EOAC_NONE);
在此之后我能够成功获得一个句柄一个OPC项目组:
hr = m_IOPCServer->AddGroup(L"", FALSE, reqUptRate, clientHandle,
NULL, NULL, lcid, &m_hServerGroup, &revisedUptRate,
IID_IOPCItemMgt,(LPUNKNOWN*)&m_IOPCItemMgt);
然而,当我尝试使用此代码:
hr = m_IOPCItemMgt->QueryInterface(IID_IOPCSyncIO, (void**)&m_IOPCSyncIO);
结果是0x80070005(“访问被拒绝”)。即使我在m_IOPCItemMgt上成功调用了CoSetProxyBlanket,情况也是如此。但是,如果我第一次调用CoInitializeSecurity,则调用成功。
我相信与How does impersonation in DCOM work?相关的问题在于QueryInterface函数是一种对象创建形式,因此它不会像AddGroup这样的其他方法调用使用相同的安全性。但是在微软的参考QueryInterface中,在实现者的注释中,它使得听起来像QueryInterface不应该检查ACL,并且在返回值下,Access Denied没有被提及为可能。我不认为这个问题是特定于实现的,但是因为我已经在一些众所周知的商业OPC服务器(例如Matrikon Simulation Server)以及不实现任何额外安全性的开源LightOPC上尝试了我的代码。
我猜测什么,我需要做的是找到一种方法来复制该命令
hr = m_IOPCItemMgt->QueryInterface(IID_IOPCSyncIO, (void**)&m_IOPCSyncIO);
,但这样做的同时也提供authIdentity
。这可能吗?它可以通过CoCreateInstanceEx或CoGetClassObject或其他COM调用完成吗?
这并没有帮助我。我怎样才能调用QueryInterface与进程不同的安全上下文?例如,我怎样才能创建一个程序连接到多个使用不同凭证的OPC服务器? – bruceceng
只需注意“返回值不足,访问被拒绝就不被视为可能性”:在COM中,一组成功代码被认为是接口合约的一部分,而错误代码则不成立。也就是说,即使没有记录,接口方法也可以返回它想要的任何错误,而不会破坏“接口合同”。有许多错误代码(比如RPC_xxxx),几乎可以返回任何(D)COM方法,并且每个方法都没有记录它们。所记录的错误代码因此是“用于说明”,那些值得解释的错误代码,但仅此而已。 – ZbynekZ
对QuerryInterface调用中的安全上下文没有太多控制。您可以在代理中实现自定义代理并调整CoInitializeSecurity,但每个代理都会有一个安全上下文。有关自定义代理的更多信息,请访问:https://msdn.microsoft.com/en-us/library/windows/desktop/ms682432(v=vs.85).aspx –