我正在尝试连接到远程计算机上的 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/

10-11 11:58