11

我想访问远程服务器上的分区 COM+ 应用程序。我试过这个:

using COMAdmin
using System.Runtime.InteropServices;

_serverName = myRemoteServer;
_partionName = myPartionName;
_message = myMessage;
ICOMAdminCatalog2 catalog = new COMAdminCatalog();
        catalog.Connect(_serverName);
        string moniker = string.Empty;
        string MsgInClassId = "E3BD1489-30DD-4380-856A-12B959502BFD";

        //we are using partitions
        if (!string.IsNullOrEmpty(_partitionName))
        {
            COMAdminCatalogCollection partitions = catalog.GetCollection("Partitions");
            partitions.Populate();
            string partitionId = string.Empty;


            foreach (ICatalogObject item in partitions)
            {
                if (item.Name == _partitionName)
                {
                    partitionId = item.Key;
                    break;
                }
            }
            if (!string.IsNullOrEmpty(partitionId) )
            {
                moniker = $"partition:{partitionId}/new:{new Guid(MsgInClassId)}";
                try
                {
                    var M = (IMsgInManager)Marshal.BindToMoniker(moniker);
                    M.AddMsg(_message);
                }
                catch (Exception ex)
                {

                    throw new Exception($"We can not use: {_partitionName} with Id {partitionId}. {ex.ToString()}");
                }                
            }
            else
            {
                throw;
            }
        }
        else
//we don't have partitions and this will work
            {
                Type T = Type.GetTypeFromCLSID(new Guid(MsgInClassId), _serverName, true);
                var M = (IMsgInManager)Activator.CreateInstance(T);
                M.AddMsg(_message);
            }

        }

因此,当我们在(远程)机器上本地时,分区正在使用名字对象和 Marshal.BindToMoniker。但是当我尝试从我的机​​器远程执行相同操作时,我从 Marshal.BindToMoniker 收到一个错误,即 Partitons 未启用。因为在我的机器上没有启用分区。

Message = "COM+ partitions are currently disabled. (Exception from HRESULT: 0x80110824)"

如何使用 Marshal.BindToMoniker 在远程服务器上运行。它是我可以添加到名字字符串的东西吗?

moniker = $"server:_server/partition:{partitionId}/new:{new Guid(MsgInClassId)}"

我的问题与此非常相似: COM+ object activation in a different partition

4

2 回答 2

3

tl;dr
根据 MS 文档,有一种方法可以通过在 BIND_OPTS2 结构中设置 pServerInfo 来绑定名字对象。不幸的是,这不适用于 COM 类名字对象。

请参阅: https ://msdn.microsoft.com/en-us/library/windows/desktop/ms694513(v=vs.85).aspx 其中表示 *pServerInfo:

COM 的新类名字对象当前不支持 pServerInfo 标志。

但也许只是尝试您的方案,并且在将来的某个时间它可能会受到支持(或者已经支持并且文档是错误的)。

另请参阅: http
://thrysoee.dk/InsideCOM+/ch11c.htm 它还在脚注中说它不适用于类名称:http: //thrysoee.dk/InsideCOM+/footnotes.htm#CH1104

如果 c# 支持,理论和建议的解决方案
免责声明:我无法测试代码,因为我没有测试设置。这不是我的想法。有点伪代码。

为此,您必须自己编写 COM/Moniker 调用。为此,您可以将 microsoft 实施的来源作为起点。BindToMoniker 的实现如下:

    public static Object BindToMoniker(String monikerName) 
    {
        Object obj = null; 
        IBindCtx bindctx = null; 
        CreateBindCtx(0, out bindctx);

        UInt32 cbEaten;
        IMoniker pmoniker = null;
        MkParseDisplayName(bindctx, monikerName, out cbEaten, out pmoniker);

        BindMoniker(pmoniker, 0, ref IID_IUnknown, out obj);
        return obj; 
    } 

CreateBindCtxMkParseDisplayName并且BindMoniker是 OLE32.dll 函数。

IBindCtx具有更改绑定上下文的方法。为此,您可以调用IBindCtx.GetBindContext(out BIND_OPTS2)并将设置更改为您需要的设置。然后使用 设置新的绑定上下文IBindCtx.SetBindContext(BIND_OPTS2)。所以基本上你自己的代码版本看起来像这样(伪代码):

    public static Object BindToMoniker(String monikerName) 
    {
        Object obj = null; 
        IBindCtx bindctx = null; 
        CreateBindCtx(0, out bindctx);

        BIND_OPTS2 bindOpts;
        bindOpts.cbStruct = Marshal.SizeOf(BIND_OPTS2);
        bindctx.GetBindOptions(ref bindOpts);
        // Make your settings that you need. For example:
        bindOpts.dwClassContext = CLSCTX_REMOTE_SERVER;
        // Anything else ?
        bindOpts.pServerInfo = new COSERVERINFO{pwszName = "serverName"};
        bindctx.SetBindOptions(ref bindOpts);

        UInt32 cbEaten;
        IMoniker pmoniker = null;
        MkParseDisplayName(bindctx, monikerName, out cbEaten, out pmoniker);

        BindMoniker(pmoniker, 0, ref IID_IUnknown, out obj);
        return obj; 
    } 

如前所述,不幸的是,无法用 C# 编写开箱即用的代码。甚至 OLE32.dll 方法声明 CreateBindCtx、MkParseDisplayName 和 BindMoniker 都是在 Marshal.cs 中私有声明的,因此您必须在项目中再次声明它们。

但是我们很幸运IBindCtx使用 aBIND_OPTS2和 BIND_OPTS2 结构定义本身的声明。它们被声明在Microsoft.VisualStudio.OLE.Interop(无论如何在这个命名空间中的有趣声明)。所以你可以尝试使用它们,因为在 Marshal 对象和 marshal.cs 中只使用了 BIND_OPTS 结构。我不知道这是否是框架的一部分并且可以再分发(我对此表示怀疑),但对于测试这应该足够好。如果可行,则可以在您自己的解决方案中再次声明这些内容。

关于所用函数的一些信息:
BindMoniker
CreateBindCtx
MkParseDisplayName
BIND_OPTS2

于 2017-02-20T20:51:57.473 回答
0

远程 COM 需要通过 Queue 或 DCOM 访问。通过 DCOM 访问时,需要在服务器上导出应用程序代理。并在客户端 PC 中安装代理。

COM 激活类型必须配置为“服务器应用程序”才能导出应用程序代理。

安装应用代理后,客户端可以直接调用

moniker = $"new:{new Guid(MsgInClassId)}";
try
{
    var M = Marshal.BindToMoniker(moniker);
}

对于分区,它旨在向每个用户展示自己的应用程序集。如果当前分区与用户相关联,则该分区不需要写代码。

于 2017-02-24T13:11:17.713 回答