Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: ActiveX/СОМ/CORBA > Проблема передачи множества функций в поток


Автор: Caduser 24.1.2007, 02:50
Есть множество некоторых функций сервера приложений которые вызываются в клиенте так:
Код

CDS1.Data := DCOM.AppServer.Func1;
CDS2.Data := DCOM.AppServer.Func2;
CDS3.Data := DCOM.AppServer.Func3;
// и так далее, всего более 100 различных функций

необходимо поместить вызов этих функций в поток, как это сделать?
Пробовал так:
Поместить объект DCOM в поток очень просто:
Код

type
  TAppThread = class(TThread)
  private
    { Private declarations }
    DCOM: TDCOMConnection;
    FstFuncName: string;
  protected
    procedure Execute; override;
  public
    constructor Create;
    destructor  Destory;
  end;

implementation

{ AppThread }

constructor TAppThread.Create;
begin
  inherited Create(True);
  DCOM := TDCOMConnection.Create(nil);
  DCOM.ServerGUID := '{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}';
  DCOM.ComputerName := 'COMP01';
  DCOM.Open;
  FreeOnTerminate := false;
end;

destructor TAppThread.Destory;
begin
  DCOM.Close;
  DCOM.Free;
end;

procedure TAppThread.Execute;
begin
  { Place thread code here }
  if FstNameFunc='Func1' then
    DCOM.AppServer.Func1
  else
  if FstNameFunc='Func2' then
    DCOM.AppServer.Func2
  else
  if FstNameFunc='Func3' then
    DCOM.AppServer.Func3;
end;


initialization
  CoInitializeEx(NIL, COINIT_MULTITHREADED);
finalization
  CoUninitialize;

затем из основной программы:
Код

procedure Func1;
// это обертка функции Func1
begin
  MyThread.FstNameFunc:='Func1';
  MyThread.Resume;
end;

procedure Func2;
// это обертка функции Func2
begin
  MyThread.FstNameFunc:='Func2';
  MyThread.Resume;
end;
// и т.д.

Однако этот способ не эффективен т.к.: 
1. будет замедлять скорость вызова пропорционально увеличению числа функций. 
2. ситуация усложняется тем, что разным функциям нужно передавать различные параметры (например: DCOM.AppServer.Func5(par1, par2, par3);
Прошу помощи в поиске эффективного решения (например с помощью переменной вариантного типа)???

Автор: CatATonik 24.1.2007, 09:29
Единственное универсальное решение которое приходит мне в голову, это передавать потоку название функции и список параметров, в виде вариантного массива, затем вызывать соответствующую функцию через IDispatch.Invoke.
Гемморойно, зато универсально.

Автор: Caduser 24.1.2007, 09:49
Цитата(CatATonik @ 24.1.2007,  09:29)
Единственное универсальное решение которое приходит мне в голову, это передавать потоку название функции и список параметров, в виде вариантного массива, затем вызывать соответствующую функцию через IDispatch.Invoke.
Гемморойно, зато универсально.

пусть геморойно, мне важно чтоб скорость вызова функции снизилось не значительно. 
IDispatch.Invoke А можно хотябы приблизительно накидать, как это будет выглядеть?
Может как то так?
Код

var
  Disp:IDispatch;
  DispID:integer;
begin
  Disp:=DCOM.GetAppServer; 
  Disp.GetIDsOfNames('Func1',1,DispID);  //получаем числовой индефикатор имени "Func1"
  Disp.Invoke(DispID,....); //вызывает метод по индификатору.
end;

Автор: CatATonik 24.1.2007, 10:05
Вот примерный набросок, также смотри функцию DispatchInvoke в модуле ComObj.

Код


var
  FuncName: WideString;
  DispId: TDispId;
  Params: TDISPPARAMS;
  ExcepInfo: TExcepInfo;
  Result: PVariant;
  Server: IDispatch;
begin
  Server := Connection.GetServer as IDispatch;
  Server.GetIDsOfNames(GUID_NULL, @FuncName, 1, GetThreadLocale, @DispId);

  Params.rgvarg := @Args;
  Params.rgdispidNamedArgs := nil;
  Params.cArgs := ArgCount;
  Params.cNamedArgs := 0;

  Server.Invoke(DispId, GUID_NULL, 0, DISPATCH_METHOD, Params, Result, @ExcepInfo, nil);
end;


Автор: Caduser 25.1.2007, 08:42
Спасибо CatATonik, чувствую ответ уже близко. Во всяком случае этот код:
Код

  Server := Connection.GetServer as IDispatch;
  FuncName := 'Func1';
  OleCheck(Server.GetIDsOfNames(GUID_NULL, @FuncName, 1, GetThreadLocale, @DispId));

DispId функции возвращает верно, но безнадёжно спотыкается на:
Код

  FillChar(Params, SizeOf(DispParams), 0);
  OleCheck(Server.Invoke(DispId, GUID_NULL, GetThreadLocale, DISPATCH_METHOD,
                  Params, Result, @ExcepInfo, nil));

с ошибкой "OLE error C0000005"  smile 
тестовая функция не имеет параметров, но подозреваю что дело в переменной Params: TDispParams, не понятно как её заполнять? 
Вообще есть библиотека типов сервера (_TLB.pas) может его как то прикурить сюда? например DispId можно оттуда взять, тогда не нужен будет метод GetIDsOfNames.

Автор: CatATonik 25.1.2007, 09:45
Так трудно сказать в чем дело. Может Result не установлен?

Автор: Caduser 26.1.2007, 05:25
Цитата(CatATonik @ 25.1.2007,  09:45)
Так трудно сказать в чем дело. Может Result не установлен?

Заработало! Подвела меня невнимательность  smile  просто Result должен быть OleVariant, а в Invoke нужно передавать указатель на Result:
Код

Invoke(DispId, GUID_NULL, GetThreadLocale, DISPATCH_METHOD,
            Params, @Result, @ExcepInfo, nil)

Спасибо, CatATonik!

P.S.
Единственный недостаток всего этого - неудобно формировать параметры для функций, уж больно их много у меня. Особенно обидно что все функции уже описаны в _TLB.pas , теперь задумался - а может раннее связывание можно организовать,  и быстрее будет работать, и проверка корректности вызовов сразу будет?

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)