Активация COM+ на удаленном сервере с разделами на C#

Я хочу получить доступ к разделенным приложениям 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, что разделы не включены. Потому что на моей машине разделы не включены.

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

Как я могу использовать Marshal.BindToMoniker для запуска на удаленном сервере. Могу ли я добавить что-то к строке моникера, т.е.

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

Мои вопросы очень похожи на это: активация объекта COM+ в другом разделе


person Hamrin    schedule 03.01.2017    source источник
comment
Вы уверены, что это не по замыслу? Сообщение об ошибке соответствует вашим настройкам. Вы должны связаться с Microsoft, я думаю. Также проверьте это: social.technet.microsoft.com/Forums/windows/en-US/   -  person Simon Mourier    schedule 17.02.2017
comment
Я думаю, вам каким-то образом нужно включить имя сервера в прозвище. Прямо сейчас вы используете только имя сервера для подключения к каталогу на сервере. Вы не используете его для создания объекта, как в случае, когда вы не используете разделы. Таким образом, вы на самом деле пытаетесь создать объект на своем локальном компьютере, где у вас не включены разделы. Решение, вероятно, состоит не в том, чтобы включать разделы локально, как это предлагается по ссылке, предоставленной @SimonMourier, потому что это позволит вам создать объект только локально, а это, вероятно, не то, что вам здесь нужно.   -  person Mikael Eriksson    schedule 17.02.2017
comment
@MikaelEriksson Теоретически возможно. На самом деле похоже, что в настоящее время может не поддерживаться. BindToMoniker реализован с вызовом CreateBindCtx (получает IBindCtx), MkParseDisplayName и, наконец, BindMoniker. Вы можете реализовать последовательность самостоятельно и вместо использования BindCtx по умолчанию (который имеет структуру BIND_OPTS) вы можете создать его самостоятельно со структурой BIND_OPTS2. У него есть pServerInfo с информацией о сервере. Теперь это хорошая часть. Плохая часть документации: моникер класса в настоящее время не учитывает флаг pServerInfo. Так что, похоже, сейчас это не сработает.   -  person Uwe Hafner    schedule 18.02.2017
comment
В компонентах в очереди есть ComputerName=cc/new:. Вы можете попробовать ComputerName: просто так. Но это больше похоже на хватание за соломинку ;-).   -  person Uwe Hafner    schedule 18.02.2017
comment
@Uwe Я думаю, вы имеете в виду COM's new class moniker does not currently honor the pServerInfo flag. здесь. Если вы добавите это в качестве ответа, у вас будет мой голос, и если не появится ничего лучше, вы также получите награду.   -  person Mikael Eriksson    schedule 20.02.2017
comment
@MikaelEriksson Да, это то, что я имею в виду. Тогда я напишу некоторые подробности со ссылками на ответ.   -  person Uwe Hafner    schedule 20.02.2017


Ответы (2)


tl;dr
Согласно документации MS, есть способ сделать это, установив pServerInfo в структуре BIND_OPTS2 для привязки моникера. К сожалению, это не работает для прозвища 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; 
    } 

CreateBindCtx, MkParseDisplayName и 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, использующим BIND_OPTS2 и само определение структуры BIND_OPTS2. Они объявлены в Microsoft.VisualStudio.OLE.Interop (в любом случае интересные объявления в этом пространстве имен). Таким образом, вы можете попробовать их использовать, потому что внутри объекта Marshal и marshal.cs используется только структура BIND_OPTS. Я не знаю, является ли это частью фреймворка и распространяемым (я сомневаюсь в этом), но для тестирования этого должно быть достаточно. Если это сработает, эти вещи могут быть снова объявлены в вашем собственном решении.

Некоторая информация об используемых функциях:
BindMoniker
CreateBindCtx
MkParseDisplayName
BIND_OPTS2

person Uwe Hafner    schedule 20.02.2017

Доступ к удаленному COM должен осуществляться с помощью Queue или DCOM. Вам необходимо экспортировать прокси приложения на сервер при доступе через DCOM. И установите прокси на клиентский ПК.

Тип активации COM должен быть настроен как «Серверное приложение», чтобы экспортировать прокси приложения.

После установки прокси приложения клиент может напрямую вызывать

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

Для раздела он предназначен для отображения каждого пользователя с собственным набором приложений. Если текущий раздел связан с пользователем, раздел не нужно записывать в кодах.

person VHao    schedule 24.02.2017