Задача: реализовать взаимодействие с собственным сервером автоматизации (созданным в EXE-файле) в локальной сети без применения DCOM ввиду известных проблем (закрытый администраторами порт 135, сложности с настройками безопасности и проч.).
Много ссылок перекопал (в том числе и схожую ветку
http://pda.delphimaster.net/?id=1241400963&n=8). Написал тестовую программу, работающую опционально в режиме сервера или клиента.
В первом случае программа открывает серверный сокет, локально создает мой тестовый объект автоматизации, к которому подключает создаваемый IRpcStubBuffer.
Во втором - клиентское активное TCP-подключение и IRpcProxyBuffer, подключенный к собственной реализации IRpcChannelBuffer.
Код IRpcChannelBuffer.SendReceive
function TRpcChannel.SendReceive(var message: TRpcOleMessage;
var status: Longint): HResult; stdcall;
var
TmpBuf: TDinamicByteArray;
begin
case fDCOMProxy.FType of
ctClient:
begin
fDCOMProxy.FCurConnector := FOwner;
TmpBuf := nil;
AddIntegerToDArray(0, TmpBuf);
MakeBuffer(message, FOwner.FIID, TmpBuf);
PInteger(@TmpBuf[0])^ := Length(TmpBuf) - sizeof(integer);
ResetEvent(fDCOMProxy.WaitRxBufEvent);
fDCOMProxy.cs1.Socket.SendBuf(TmpBuf[0], Length(TmpBuf));
repeat
case MsgWaitForMultipleObjects(1, fDCOMProxy.WaitRxBufEvent, false,
INFINITE, QS_ALLINPUT) of
WAIT_OBJECT_0: break;
else
Application.ProcessMessages;
end;
until false;
TmpBuf := fDCOMProxy.ProxyRxBuf;
MakeMessage(TmpBuf, FOwner.FIID, message);
end;
ctServer:;
ctLocal:
begin
fDCOMProxy.FCurConnector := FOwner;
fDCOMProxy.FCurConnector.IStub.Invoke(message, Self);
end;
end;
Result := S_OK;
end;
где
type
TDinamicByteArray = array of byte;
procedure MakeMessage(Buf: TDinamicByteArray; out IID: TIID;
out OleMessage: TRpcOleMessage);
var
Index: integer;
begin
ZeroMemory(@OleMessage, Sizeof(OleMessage));
CopyMemory(@IID, @Buf[0], sizeof(IID));
Index := sizeof(IID);
OleMessage.dataRepresentation := GetIntegerFromDArray(Index, Buf);
OleMessage.iMethod := GetIntegerFromDArray(Index, Buf);
OleMessage.rpcFlags := GetIntegerFromDArray(Index, Buf);
OleMessage.cbBuffer := GetIntegerFromDArray(Index, Buf);
OleMessage.Buffer := GlobalAllocPtr(GPTR, OleMessage.cbBuffer);
CopyMemory(OleMessage.Buffer, @Buf[Index], OleMessage.cbBuffer);
end;
procedure MakeBuffer(OleMessage: TRpcOleMessage; IID: TIID;
var Buf: TDinamicByteArray);
begin
AddDataToDarray(IID, sizeof(IID), Buf);
AddIntegerToDArray(OleMessage.dataRepresentation, Buf);
AddIntegerToDArray(OleMessage.iMethod, Buf);
AddIntegerToDArray(OleMessage.rpcFlags, Buf);
AddIntegerToDArray(OleMessage.cbBuffer, Buf);
AddDataToDarray(OleMessage.Buffer^, OleMessage.cbBuffer, Buf);
end;
В общем все сводится к пересылке в неизменном виде содержимого TRpcOleMessage по TCP/IP, вызов на строне сервера IStub.Invoke() и обратная пересылка ответа.
Решение работает на локальной машине при указании контекста dwDestContext := CLSCTX_LOCAL_SERVER в методе IRpcChannelBuffer.GetDestCtx().
Проблемы начинаются с удаленной пересылкой интерфейсов. Используемые интерфейсы сервера автоматизации наследуются от IDispatch и поддаются автоматическому маршалингу (по крайней мере при работе в контексте CLSCTX_LOCAL_SERVER). Однако при работе в сети выскакивает ошибка: "не найден указанный источник экспорта объекта". Замена контекста на CLSCTX_REMOTE_SERVER приводит к неработоспособности даже в локальном взаимодейтвии Proxy/stub в контексте одного процесса, выдавая при попытке пересылки интерфейса ошибку "заглушке переданы неправильные данные".
Собственно, вопрос: можно ли заставить работать такой механизм (только без применения ручного маршалинга реализацией IMarshal, ведь DCOM как-то умеет пересылать интерфейсы по сети)?