На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! ПРАВИЛА РАЗДЕЛА · FAQ раздела Delphi · Книги по Delphi
Обязательно выделяйте текст программы тегом [сode=pas] ... [/сode]. Для этого используйте кнопку [code=pas] в форме ответа или комбобокс, если нужно вставить код на языке, отличном от Дельфи/Паскаля.

Этот раздел предназначен для вопросов, посвященных разработке компонентов, а также для тестирования собственных бесплатных компонентов с открытым исходным кодом.

Здесь запрещается:
1. Размещать ссылки на какие-либо коммерческие компоненты, реализующие требуемую функциональность.
2. Обсуждать и тестировать коммерческие компоненты или компоненты с закрытым кодом.
3. Давать ссылки на сайты с исходным кодом компонентов. Все тестируемые исходные коды должы быть размещены на сайте ИСХОДНИКИ.RU.
Модераторы: Rouse_, DimaBr
  
> При закрытии программы , RealTime Error
    Доброе время суток.
    Я создала свой компонент по юниту, который мне когда-то подсказали на вашем форуме. Мне нужно было вылавливать событие сохранение документа Word при позднем связывании.
    Так выглядит часть юнита, которую я использую:
    ExpandedWrap disabled
      unit UnMyServerEventDispatch;
       
      interface
      uses
        Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
        Dialogs, StdCtrls, ExtCtrls, ActiveX, ComObj, unSTP, main;
       
      type
       
        TMyServerEventDispatch = class(TObject, IUnknown, IDispatch)
        private
          InternalRefCount : Integer;
        public
          { IUnknown }
          function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
          function _AddRef: Integer; stdcall;
          function _Release: Integer; stdcall;
          { IDispatch }
          function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
          function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
          function GetIDsOfNames(const IID: TGUID; Names: Pointer;
            NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
          function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
            Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
       
       
          constructor Create();
        end;
       
      implementation
      const
      //   WordEvent: TGUID = '{000209FE-0000-0000-C000-000000000046}'; // Word 2000
         WordEvent: TGUID = '{00020A00-0000-0000-C000-000000000046}'; // Word 2002
       
      function TMyServerEventDispatch.Invoke(DispID: Integer; const IID: TGUID;
        LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,
        ArgErr: Pointer): HResult;
      var v: olevariant;
      begin
         if (DispID = 8) then  //save
           begin
             unSTP.want_to_ask:=false;
             v:=ExtractFilePath(Application.ExeName)+'\temp\111.doc';
             unSTP.Wrd.ActiveDocument.SaveAs(v, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam);
             v:=ExtractFilePath(Application.ExeName)+'\temp\222.doc';
             unSTP.Wrd.ActiveDocument.SaveAs(v, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam);
             // ShowMessage(ClientDataSet1.FieldByName('ID_STP').AsString);
             if unstp.text_STP_or_notif=1 then
               begin
                 MainForm.ClientDataSet1.Edit;
                 MainForm.ClientDataSet1TEXT_STP.LoadFromFile(ExtractFilePath(Application.ExeName)+'\temp\111.doc');
                 ask_post:=false;
                 MainForm.ClientDataSet1.Post;
                 ask_post:=true;
                 SetForegroundWindow(MainForm.Handle);
               end;
             if unstp.text_STP_or_notif=2 then
               begin
                 MainForm.ClientDataSet2.Edit;
                 MainForm.ClientDataSet2TEXT_NOTIF.LoadFromFile(ExtractFilePath(Application.ExeName)+'\temp\111.doc');
                 ask_post:=false;
                 MainForm.ClientDataSet2.Post;
                 ask_post:=true;
                 SetForegroundWindow(MainForm.Handle);
               end;
           end;
         Result := S_OK;
      end;
       
      end.


    В основной программе я использовала его таким образом:
    ExpandedWrap disabled
                    Wrd:=CreateOleObject('Word.Application');
                    punk:=IUnknown(Wrd);
                    ComObj.InterfaceConnect(punk, WordEvent, MyWord1, Connection);
                    wrd.documents.open(path_stp);
                    Wrd.Visible := true;


    Но так неудобно было использовать этот класс, потому что для каждой программы нужно переписывать код класса TMyServerEventDispatch, где он обрабатывает сохранение файла.
    Я решила создать компонент, чтобы можно было писать обработку сохранения документа прямо из остновной программы.
    Вот как выглядет код этого компонента:
    ExpandedWrap disabled
      unit MyWord;
       
      interface
      uses
        Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
        Dialogs, StdCtrls, ExtCtrls, ActiveX, ComObj;
       
      type
       
        TWordSave = procedure(ASender: TObject) of object;
       
        TMyWord = class(TComponent, IUnknown, IDispatch)
        private
          InternalRefCount : Integer;
          FOnSave: TWordSave;
          { IUnknown }
          function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
          function _AddRef: Integer; stdcall;
          function _Release: Integer; stdcall;
          { IDispatch }
          function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
          function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
          function GetIDsOfNames(const IID: TGUID; Names: Pointer;
            NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
          function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
            Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
        public
          constructor Create(AOwner: TComponent); override;
          destructor Destroy; override;
        published
          property OnSave: TWordSave read FOnSave write FOnSave;
        end;
       
      procedure Register;
       
      implementation
      const
      //   WordEvent: TGUID = '{000209FE-0000-0000-C000-000000000046}'; // Word 2000
         WordEvent: TGUID = '{00020A00-0000-0000-C000-000000000046}'; // Word 2002
       
      procedure Register;
      begin
        RegisterComponents('Servers', [TMyWord]);
      end;
       
      function TMyWord.Invoke(DispID: Integer; const IID: TGUID;
        LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo,
        ArgErr: Pointer): HResult;
      //var v: olevariant;
      begin
         if (DispID = 8) then  //save
           begin
             if Assigned(FOnSave) then FOnSave(Self);
           end;
         Result := S_OK;
      end;
       
      end.


    В основной программе я пишу код собятия OnSave:
    ExpandedWrap disabled
             UnSTP.want_to_ask:=false;
             v:=ExtractFilePath(Application.ExeName)+'\temp\111.doc';
             UnSTP.Wrd.ActiveDocument.SaveAs(v, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam);
             v:=ExtractFilePath(Application.ExeName)+'\temp\222.doc';
             UnSTP.Wrd.ActiveDocument.SaveAs(v, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam);
             // ShowMessage(ClientDataSet1.FieldByName('ID_STP').AsString);
             if UnSTP.text_STP_or_notif=1 then
               begin
                 ClientDataSet1.Edit;
                 ClientDataSet1TEXT_STP.LoadFromFile(ExtractFilePath(Application.ExeName)+'\temp\111.doc');
                 ask_post:=false;
                 ClientDataSet1.Post;
                 ask_post:=true;
                 SetForegroundWindow(Handle);
               end;
             if UnSTP.text_STP_or_notif=2 then
               begin
                 ClientDataSet2.Edit;
                 ClientDataSet2TEXT_NOTIF.LoadFromFile(ExtractFilePath(Application.ExeName)+'\temp\111.doc');
                 ask_post:=false;
                 ClientDataSet2.Post;
                 ask_post:=true;
                 SetForegroundWindow(Handle);
               end;


    И у меня стала возникать ошибка уже после закрытия программы "RunTime Error". Она возникает, если срабатывает ComObj.InterfaceConnect(punk, WordEvent, MainForm.MyWord1, Connection);
    Я наверное слишком длинно рассказывала, но помогите мне, пожалуйста, я в тупике...
    Заранее благодарна
      Перекос, скорее всего из=за того что освобождаетс форма, но не оповещает об этом "MyWord", а тот пытается вызвать FOnSave...
      1) Тебе надо навеситься на форму при присвоении FOnSave, чтобы вовремя аннулировать указатель на метод (при освобождении формы).
      слежение можно сделать так:
      ExpandedWrap disabled
        type
          TMyWord = class(TComponent, IUnknown, IDispatch)
          private
           ...
             procedure SetOnSave( const AOnSave: TWordSave ); // <-- добавлено
          protected
                
          public
           ...
          published
            property OnSave: TWordSave read FOnSave write SetOnSave;
          end;
        ...
         
        type
           // вспомогательная структура для представления обработчика событий объекта
           //
           TEventStruc =
           packed record
              proc : pointer;
              case byte of
                 0: (objPtr: pointer);
                 1: (obj   : TObject)
           end;
         
        procedure TMyWord.SetOnSave( const Value: TWordOnSave );
        var
           ev: TEventStruc;
        begin
           if not CompareMem( @@FOnSave, @@Value, sizeof(TWordOnSave) ) then
           begin
              FOnSave := Value;
         
              // "навешиваемся" на компонент, чей метод обрабатывается...
              ev := TEventStruc(Value);
              if ev.obj is TComponent then
                 TComponent(ev.obj).FreeNotification(Self);
           end;
        end;
         
        procedure TMyWord.Notification( AComponent: TComponent;
          Operation: TOperation);
        begin
           inherited;
         
           if Operation = opRemove then
           begin
         
              // если освобождается объект, метод которого надо вызвать
              // для FOnSave -> очищаем FOnSave;
              if AComponent = TEventStruc(Self.FOnSave).obj then
                 FOnSave := nil;
           end;
        end;
         
        2) Также MyWord надо аккуратно особождать - через MyWord._Release. Если ты сделал компонент MyWord. то скорее всего его владелец - форма,
        которая его грохнет автоматически. Просто надо в деструкторе MyWord отцепиться от MS Word через InterfaceDisconnect !
        Последний параметр в InterfaceDisconnect - это тот же самый что и у InterfaceConnect: "Connection: longint", так что его придется хранить в самом TMyWord.


      Добавлено
      Вот так можно отслеживать более безопасно:
      ExpandedWrap disabled
        type
           TMyWord = class( {TComponent, IUnknown, IDispatch)
           private
             ...
            // последние контролируемые события
            FCurConnection: integer;
            FCurInterface: IUnknown;
            FCurGUID: TGUID;
         
           protected
           ...
           public
            constructor Create( AOwner: TComponent ); override;
            destructor Destroy; override;
         
            // выполнить присоединение Self к обработке событий punk с ID=EvGUID;
            // предыдущее автоматически отсоединяется;
            // если punk = nil -> просто отсоединиться от всего;
            //
            procedure DoConnect( punk: IUnknown; EvGUID: TGUID );
         
            procedure DoDisconnect; // отсоединиться
           end;
         
        ...
         
        // (!) Create привожу чтобы точно показать что вызывается inherited (!)
        //
        constructor TMyWord.Create;
        begin
           inherited;
           InternalRefCount := 1;
        end;
         
        destructor TMyWord.Destroy;
        begin
           DoDisсonnect; // гарантировано отсоединяемся
           inherited;
        end;
         
        procedure TMyWord.DoDisconnect; // отсоединиться
        begin
           DoConnect( nil, FCurGUID );
        end;
         
        procedure TMyWord.DoConnect( punk: IInterface; EvGUID: TGUID);
        begin
           // отсоединиться от прежнего...
           if FCurInterface <> nil then
              ComObj.InterfaceDisconnect( FCurInterface, FCurGUID, FCurConnection );
         
           // присоединиться к новому, если надо
           //
           FCurConnection := 0;
           FCurInterface := punk;
           FCurGUID := EvGUID;
         
           if punk <> nil then
              ComObj.InterfaceConnect( FCurInterface, FCurGUID, Self, FCurConnection );
        end;
        Спасибо огромное, RuSa. Ты меня выручил, теперь все работает. :)
        Вопрос можно считать закрытым
        1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
        0 пользователей:


        Рейтинг@Mail.ru
        [ Script execution time: 0,0297 ]   [ 15 queries used ]   [ Generated: 18.05.24, 17:40 GMT ]