我正在尝试连接到远程计算机上的 DCOM 对象,该计算机以与 C# 应用程序不同的用户身份登录。我已经让我的代码在同一台计算机上运行时连接到同一个 DCOM 对象,如下所示:
MyDCOMType dcomObject;
Type remoteSessionContextType = Type.GetTypeFromProgID("ServerApp.MyDCOMType");
dcomObject = (MyDCOMType)Activator.CreateInstance(remoteSessionContextType);
string version = dcomObject.GetVersion();
MyDCOMType 是一种在 DCOM 应用程序(一个 VB6 应用程序)中实现的类型,我在我的项目中添加了它作为引用。我已经能够在 C# 代码中创建它的一个实例,并使用预期的结果调用所有方法。现在我试图让它连接到远程机器上的同一个对象。我试图像这样模拟远程系统上的用户(省略了变量定义和错误处理):
if (LogonUser(
userName,
domain,
password,
LOGON32_LOGON_NEW_CREDENTIALS,
LOGON32_PROVIDER_WINNT50,
out token) != false)
{
if (DuplicateToken(token, (int)_SECURITY_IMPERSONATION_LEVEL.SecurityDelegation, out tokenDuplicate) != false)
{
impersonationContext = WindowsIdentity.Impersonate(tokenDuplicate.DangerousGetHandle());
}
}
然后是类似的代码来创建对象:
MyDCOMType dcomObject;
Type remoteSessionContextType = Type.GetTypeFromProgID("ServerApp.MyDCOMType", ipAddress);
dcomObject = (MyDCOMType)Activator.CreateInstance(remoteSessionContextType);
string version = dcomObject.GetVersion();
除了 CreateInstance 调用引发 UnauthorizedAccessException 错误,错误代码为 80070005 以进行未经授权的访问。
另一件事是我有 C++ 代码,它可以完美地完成同样的事情。这段代码是这样的:
COAUTHINFO AuthInfo;
COAUTHIDENTITY AuthIdentity;
COSERVERINFO ServerInfo;
MULTI_QI Results;
HRESULT hr;
BSTR version;
_MyDCOMType *pSession = NULL;
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.dwReserved1 = 0;
ServerInfo.dwReserved2 = 0;
ServerInfo.pAuthInfo = &AuthInfo;
ServerInfo.pwszName = w_nodename;
hr = CoCreateInstanceEx(clsid, NULL, CLSCTX_ALL, &ServerInfo, (ULONG) 1, &Results);
hr = CoSetProxyBlanket(Results.pItf, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, &AuthIdentity, EOAC_NONE);
pSession = (_MyDCOMType *)Results.pItf;
hr = pSession->raw_GetVersion(&version);
它连接到远程服务器,创建 DCOM 对象,并从中获取数据就好了。我想弄清楚如何让我的 C# 代码做同样的事情。有任何想法吗?我是否搞砸了进入函数的枚举之一?我是否错过了一些重要的电话或做错了什么?作为背景,我的计算机和我尝试连接的计算机都在域帐户下登录,但彼此都没有权限。 C++ 代码模拟远程用户以实现 DCOM 连接。我正在尝试以这样一种方式进行 .NET 模拟,以便在给定手动输入的用户名和密码的情况下对网络连接有效。
或者,如果这是不可能的,我想我可以用相同的 C++ 代码编写一个小的 C++/CLR DLL 来处理 DCOM 连接部分并从 C# 代码中引用它,但我希望避免额外的复杂性.
最佳答案
我敲了一会儿,没有任何进展,所以我放弃了,写了一个 C++/CLR DLL,它工作得很好。代码如下:
MyDCOMTalker::MyDCOMTalker(String ^ipAddress, String ^username, String ^password, String ^domain)
{
COAUTHINFO AuthInfo;
COSERVERINFO ServerInfo;
MULTI_QI Results;
CLSID clsid;
AuthIdentity = new COAUTHIDENTITY;
long DSStatus;
BSTR umVersion;
AuthIdentity->Domain = (unsigned short *)Marshal::StringToHGlobalAuto(domain).ToPointer();
AuthIdentity->DomainLength = domain->Length;
AuthIdentity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
AuthIdentity->Password = (unsigned short *)Marshal::StringToHGlobalAuto(password).ToPointer();
AuthIdentity->PasswordLength = password->Length;
AuthIdentity->User = (unsigned short *)Marshal::StringToHGlobalAuto(username).ToPointer();
AuthIdentity->UserLength = username->Length;
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.dwReserved1 = 0;
ServerInfo.dwReserved2 = 0;
ServerInfo.pAuthInfo = &AuthInfo;
ServerInfo.pwszName = (LPWSTR)Marshal::StringToHGlobalAuto(ipAddress).ToPointer();
Results.pIID = &_uuidof(_MyDCOMType);
Results.pItf = NULL;
Results.hr = 0;
CoInitialize(NULL);
Marshal::ThrowExceptionForHR(CLSIDFromProgID(L"MyDcomDLL.MyDCOMType", &clsid));
Marshal::ThrowExceptionForHR(CoCreateInstanceEx(clsid, NULL, CLSCTX_ALL, &ServerInfo, (ULONG) 1, &Results));
Marshal::ThrowExceptionForHR(CoSetProxyBlanket(Results.pItf, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE, AuthIdentity, EOAC_NONE));
pSession = (_MyDCOMType*)Results.pItf;
Marshal::ThrowExceptionForHR(pSession->raw_GetVersion(&DSStatus, &umVersion));
if (DSStatus == 0 && umVersion != NULL)
version = Marshal::PtrToStringBSTR((System::IntPtr)umVersion);
else
version = String::Empty;
}
使用类定义:
public ref class MyDCOMTalker
{
private:
_MyDCOMType *pSession;
String ^version;
COAUTHIDENTITY *AuthIdentity;
public:
MyDCOMTalker(String ^ipAddress, String ^username, String ^password, String ^domain);
};
通常的警告适用 - 我删除了更简单代码的实际功能和错误检查/处理细节,而且我真的不太了解 C++/CLR 或 COM 和 DCOM,所以可能有些事情可以更容易地完成。如果您知道在 C# 中有任何改进或方法,请酌情评论/回答。
关于c# - 如何在 C# 中为 DCOM 连接模拟另一个用户?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/13278831/