![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Обращение к удаленном OLE-объекту
GrBob © (2006-06-27 11:07:00)Здравствуйте.
На удаленном компьютере есть OLE-объект, у которого известно только название. (ну и соответственно легко получается CLSID, в реестре на данном компьютере информация об объекте есть). Вопрос: можно ли как-нибудь к нему обраться? CreateOLEObject работает только на текущей машине, а CreateRemoteComObject возвращает интерфейс, название которого я не знаю. (Как его получить?)
Заранее спасибо за ответы.
AbrosimovA (2006-07-11 14:27:00) [1]Это дополнение к коду из 5-го поста для процедуры
procedure EnumInterface(Dispatch: IDispatch; Entries: TStrings; Functions: Boolean);:
type TArgInfo = record
Total: integer;
Optional: integer;
ArgsType: array[0..100] of TVARTYPE;
end;
var ArgInfo: array of TArgInfo;
SetLength(ArgInfo,TypeAttr.cFuncs);
//Общее число параметров метода
ArgInfo[iMethod].Total:=FuncDesc.cParams;
//Число необязательных параметров из них
ArgInfo[iMethod].Optional:=FuncDesc.cParamsOpt;параметров
//типы всех параметров
for i:=0 to FuncDesc.cParams-1 do ArgInfo[iMethod].ArgsType[i]:=TVARTYPE(FuncDesc.lprgelemdescParam[i].tdesc.vt);< /CODE>
GrBob (2006-07-11 12:31:00) [2]
> Как программно узнать, что нужно передавать через DispParams
> для безошибочного выполнения методов через IDispatch.Invoke
Советую взглянуть в сторону интерфейсов ITypeLib и ITypeInfo. Сам еще не разобрался, но имхо должно подойти. Мне как раз сейчас это тоже понадобилось, просто времени совсем нет читать, если разберешься, отпишись :)
(2006-07-09 20:53:00) [3]Удалено модератором
AbrosimovA (2006-07-07 16:54:00) [4]Не могу найти ответа на следующий вопрос:
Как программно узнать, что нужно передавать через DispParamsдля безошибочного выполнения методов через IDispatch.Invoke
AbrosimovA (2006-07-07 14:44:00) [5]Проверено на методе Addинтерфейса Documentsобъекта Word.Application- работает.
var iSrv: IDispatch;
DID: integer;
Value: OleVariant;
DID:=StrToInt(ListView2.Selected.Caption);
Value:=GetDispIDFromName(iSrv, ListView2.Items[iIndex].SubItems[0], DID);
case VarType(Value) of
varDispatch:
begin
//Connect;
ISrv:=IDispatch(Value);
end;
varEmpty:
begin
//Exec;
try
SetInvokeMethod(iSrv,DID);
except
on E: Exception do begin
StatusBar1.SimpleText:=E.Message;
end
end
end;
end;
procedure SetInvokeMethod(const Dispatch: IDispatch; const ID: integer);
var
DispParams: TDispParams;
Args: array[0..1] of TVariantArg;
ExcepInfo : TExcepInfo;
iError : UINT;
begin
Args[0].vt:= VT_ERROR;
Args[0].scode := DISP_E_PARAMNOTFOUND;
Args[1].vt:= VT_ERROR;
Args[1].scode := DISP_E_PARAMNOTFOUND;
FillChar(DispParams, SizeOf (DispParams), 0);
with DispParams do begin
rgvarg := @Args;
cArgs := 2;
cNamedArgs := 0;
end;
OleCheck (Dispatch.Invoke(ID, GUID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD, DispParams, nil, @ExcepInfo, @iError));
end;
GrBob (2006-07-07 12:37:00) [6]Аааа, ну это то конечно установлено :)
Ломброзо (2006-07-07 12:27:00) [7]Зайти в оснастку "Component Services" (на XP)->Свойства компьютера или dcomcnfg, проверить опцию, разрешающую использование DCOM.
GrBob (2006-07-07 12:24:00) [8]Права - я администратор как на своей, так и на удаленной (в данном случае опять же своей :) ) машине. Что необходимо настроить в DCOM?
Ломброзо (2006-07-07 12:13:00) [9]Да я так, на всякий случай.
Продолжаем разбираться дальше )
Как обстоят дела с настройками DCOM и правами?
GrBob (2006-07-07 11:51:00) [10]
> Скопируйте эту вашу библиотеку на свою машину и зарегистрируйте
> её через regsvr32
Она у меня зарегестрирована :) На своей машине прекрасно работает, а эта ошибка выдается даже если указать в качестве удаленной машины - свою.
Ломброзо (2006-07-07 11:38:00) [11]> Инвоке возвращает очень содержательную ошибку с текстом "Ошибка"
Скопируйте эту вашу библиотеку на свою машину и зарегистрируйте её через regsvr32
GrBob (2006-07-07 09:06:00) [12]
> AbrosimovA © (07.07.06 08:10) [14]
> Если только тебе заранее известны все методы и свойства
> исследуемого интерфейса.
Дак они мне известны (по крайней мере те, которые я хочу использовать), почему-то не работает :(
В общем вот такие вот первые итоги: привожу на примере службы индексирования, она описана в MSDN и стоит на Windows Server 2003 по умолчанию, да и не главное это :) Просто у Ворда слишком заумные методы, чтобы на нем тесты ставить:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, ExtCtrls, StdCtrls, ActiveX;
type
TForm1 = class(TForm)
ListBox1: TListBox;
ServNameEdit: TEdit;
Label4: TLabel;
ValuePropEdit: TEdit;
btnReadInterf: TButton;
BtnRemConnect: TButton;
StatusBar1: TStatusBar;
Button1: TButton;
AddCatalog: TButton;
GroupBox1: TGroupBox;
Label1: TLabel;
Edit1: TEdit;
Label2: TLabel;
Edit2: TEdit;
GroupBox2: TGroupBox;
Start: TButton;
Button4: TButton;
procedure BtnRemConnectClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure AddCatalogClick(Sender: TObject);
procedure StartClick(Sender: TObject);
private
procedure Invoke(const ID: integer; const DispParams: TDispParams);
procedure SetInvoke(const ID: integer);
procedure Exec(const Name: WideString; const DispParams: TDispParams);
public
end;
var
Form1: TForm1;
Srv: IDispatch;
DID: integer;
iIndex: integer;
implementation
{$R *.dfm}
uses ComObj, AxCtrls;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Srv := nil;
end;
procedure TForm1.SetInvoke(const ID: integer);
var
aDispParams: TDispParams;
aEI: TExcepInfo;
iError: UINT;
Command: OleVariant;
begin
FillChar(aDispParams, SizeOf (aDispParams), 0);
aDispParams.cArgs := 1;
Command := True;
GetMem(aDispParams.rgvarg, aDispParams.cArgs * SizeOf(TVariantArg));
aDispParams.rgvarg[0] := TVariantArg(OleVariant('C:\'));
aDispParams.rgvarg[1] := TVariantArg(OleVariant('D:\'));
OleCheck(Srv.Invoke(ID, GUID_NULL, 0,
DISPATCH_PROPERTYPUT, aDispParams, NIL, @aEI, @iError));
end;
procedure TForm1.Invoke(const ID: integer; const DispParams: TDispParams);
var
aDispParams: TDispParams;
aEI: TExcepInfo;
iError: UINT;
begin
aDispParams := DispParams;
OleCheck(Srv.Invoke(ID, GUID_NULL, 0,
DISPATCH_METHOD, aDispParams, NIL, @aEI, @iError));
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
iIndex := -1;
end;
procedure TForm1.BtnRemConnectClick(Sender: TObject);
var
CLSID: TGUID;
RemoteComp: WideString;
begin
CLSID := ProgIDToClassID('Microsoft.IsAdm');
try
RemoteComp := ServNameEdit.Text;
Srv := CreateRemoteComObject(RemoteComp, CLSID) as IDispatch;
except
Exit;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
PName: PWideChar;
iDispID: Integer;
Name : WideString;
begin
iIndex := Listbox1.ItemIndex;
ListBox1.Items.NameValueSeparator := '-';
Name := Listbox1.Items.ValueFromIndex[iIndex];
PName := PWideChar(Name);
if Srv.GetIDsOfNames(GUID_NULL, @PName, 1, GetThreadLocale, @iDispID) = S_OK then
SetInvoke(iDispID);
end;
procedure TForm1.Exec(const Name: WideString; const DispParams: TDispParams);
var
PName: PWideChar;
iDispID: Integer;
begin
PName := PWideChar(Name);
Srv.GetIDsOfNames(GUID_NULL, @PName, 1, GetThreadLocale, @iDispID);
Invoke(iDispID, DispParams);
end;
procedure TForm1.AddCatalogClick(Sender: TObject);
var
DispParams: TDispParams;
Catalog, Place: WideString;
begin
FillChar(DispParams, SizeOf (DispParams), 0);
DispParams.cArgs := 2;
GetMem(DispParams.rgvarg, DispParams.cArgs * SizeOf(TVariantArg));
try
Catalog := Edit1.Text;
Place := Edit2.Text;
DispParams.rgvarg[0] := TVariantArg(OleVariant(Place));
DispParams.rgvarg[1] := TVariantArg(OleVariant(Catalog));
Exec('AddCatalog', DispParams);
finally
FreeMem(DispParams.rgvarg, DispParams.cArgs * SizeOf(TVariantArg));
end;
end;
procedure TForm1.StartClick(Sender: TObject);
var
DispParams: TDispParams;
begin
FillChar(DispParams, SizeOf (DispParams), 0);
DispParams.cArgs := 0;
GetMem(DispParams.rgvarg, DispParams.cArgs * SizeOf(TVariantArg));
try
Exec('Start', DispParams);
finally
FreeMem(DispParams.rgvarg, DispParams.cArgs * SizeOf(TVariantArg));
end;
end;
end.
Что дальше - ума не приложу. Инвоке возвращает очень содержательную ошибку с текстом "Ошибка" :( В чем может быть дело?
AbrosimovA (2006-07-07 08:10:00) [13]
> Ломброзо © (07.07.06 02:10) [12]
> можно смело присвоить переменной типа OleVariant и вызывать
> всякие свойства и методы
Если только тебе заранее известны все методы и свойства исследуемого интерфейса.
GrBob (2006-07-07 08:07:00) [14]Предположим, так:
CLSID := ProgIDToClassID('Bla.Bla');
try
RemoteComp := ServNameEdit.Text;
Srv := CreateRemoteComObject(RemoteComp, CLSID) as IDispatch;
OleVariant(Srv).Start;
except
Exit;
end;
Получаю при вызове метода Start "Разрушительный сбой". Да и написана эта библиотека так, что библиотеки типов просто нет в списке импортируемых.
Ломброзо (2006-07-07 02:10:00) [15]Может я чего-то недопонимаю, но OleVariant устроен таким образом, что в том случае, если переменная типа OleVariant содержит указатель на интерфейс IDispatch, то все вызовы к "свойствам" и "методам" этой переменной транслируются в вызов IDispatch::Invoke(). Так что пользоваться IDispatch совершенно излишне. То бишь тот указатель на интерфейс, который был отмаршаллен с другого хоста, можно смело присвоить переменной типа OleVariant и вызывать всякие свойства и методы. Компилятор с разруливанием вызовов расправится сам.
Более того. Если эта библиотека, содержащая определение коклассов и интерфейсов, написана как положено и позволяет получить IDL, ничто не мешает импортировать библиотеку типов, сгенерировать из неё паскалевские заглушки и включить в проект.
GrBob (2006-07-06 17:09:00) [16]Да, необходимо. Но я еще поковырялся и нашел немного другой способ:
procedure TForm1.Button2Click(Sender: TObject);
var
DispParams: TDispParams;
egin
FillChar(DispParams, SizeOf (DispParams), 0);
DispParams.cArgs := 2;
GetMem(DispParams.rgvarg, DispParams.cArgs * SizeOf(TVariantArg));
try
DispParams.rgvarg[0] := TVariantArg(OleVariant('aaa'));
DispParams.rgvarg[1] := TVariantArg(OleVariant('bbb'));
Exec(Self.Caption, DispParams);
finally
FreeMem(DispParams.rgvarg, DispParams.cArgs * SizeOf(TVariantArg));
end;
end;
Функция Exec потом ищет метод, вызывает его ну и т.д., не суть важно. Вроде работает, но надо еще проверять, почему то иногда падает (возможно даже не в этом месте :)).
AbrosimovA (2006-07-06 16:54:00) [17]
> OleVariant(Args[0]) := 1; OleVariant(Args[1]) := 2; OleVariant(Args[2])
> := 3;
Так ли уж необходимо преобразовывать к олевариантному типу?
GrBob (2006-07-06 15:54:00) [18]Мне кажется, что писать для этого дополнительный объект - все же перебор. Продолжая разбираться со стандартным IDispatch'ем и его Invoke'ом я смог вызвать методы без параметров. С параметрами все намного грустнее. Нашел вот такой кусок кода:
var O : IDispatch;
Disp: TDispParams;
Args: array[0..2] of TVariantArg;
begin
O := CreateOleObject('Project1.Test');
// Присваиваю аргументы
OleVariant(Args[0]) := 1;
OleVariant(Args[1]) := 2;
OleVariant(Args[2]) := 3;
with Disp do begin
rgvarg := @Args;
cArgs := 3;
rgdispidNamedArgs := nil;
cNamedArgs := 0;
end;
O.Invoke(1,GUID_NULL,GetThreadLocale,DISPATCH_METHOD,Disp,nil,nil,nil);
end;
Я и сам пытался написать что-то подобное (естественно, уходя от CreateOLEObject), но он падает на преобразовании TVariantArg'a к ОлеВарианту. В МСДН вроде написано, что должно работать, да и у автора этого кода тоже все работало. У меня же - Invalid Variant type. Подскажите, в чем дело?
Медвед (2006-07-03 10:20:00) [19]нужно написать прокси-объект, реализующий IDispatch
пример есть в исходниках Borland Socket Server
AbrosimovA (2006-06-30 12:40:00) [20]
> осталось узнать как вызывать методы
Так как, я проект давно забросил, то в этом направлении я не копал.
Но думаю, в Invoke нужно использовать DISPATCH_METHODи правильно заполнить DispParams