Активация 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);
}
}
Поэтому, когда мы локально на (удаленной) машине, разделы работают с moniker и 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+ в другом разделе
2 ответа
ТЛ; др
Согласно документации 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
Теория и предлагаемое решение, если оно было поддержано в 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
Удаленный COM должен быть доступен через очередь или DCOM. Вам необходимо экспортировать прокси приложения на сервер при доступе через DCOM. И установите прокси на клиентском ПК.
Тип активации COM должен быть настроен как "Серверное приложение" для экспорта прокси приложения.
После установки прокси приложения клиент может напрямую позвонить
moniker = $"new:{new Guid(MsgInClassId)}";
try
{
var M = Marshal.BindToMoniker(moniker);
}
Для раздела он предназначен для показа каждому пользователю с собственным набором приложений. Если текущий раздел связан с пользователем, раздел не должен быть записан в кодах.