Как открыть.pas-файл из другого приложения в уже открытой среде IDE Delphi и положение в строке#

предполагая, что у меня открыта IDE Delphi, как я могу открыть .pas файл, выбранный в другом приложении и открыть его в Delphi IDE, а также позиционирование его на определенный номер строки?

Я видел, как некоторые инструменты редактирования делают это.

Я не уверен, что это просто вариант обычного открытого файла (например., используя ассоциацию файлов по умолчанию), или параметр командной строки, или вам нужен DDE или COM или что-то совсем другое.

обратите внимание, что я не хочу, чтобы закрыть проект и открыть новый или поддельный проект.

кроме того, я не хочу, чтобы файл добавлен в проект. Я просто хочу его открыть.

например, когда вы <ctrl>-click в переменной или типе среда IDE откроет файл, содержащий этот символ, и перейдет к строке, в которой этот символ объявлен. Это все, что я хочу сделать, но из внешнего приложения. (Я не ищу символ, просто линию.)

На данный момент я использую Delphi XE5, поэтому меня интересует новый Delphi версии, а не pre-XE2 или около того.

(часть вопроса заключается в том, как я могу гарантировать, что если IDE уже открыта, файл открывается на новой вкладке внутри текущей IDE, а не в другом экземпляре IDE?)

1 ответов


код ниже (для D7) показывает, как это можно сделать с помощью надстройки IDE .Составлено Dpk в Bpl. Это началось как просто "доказательство концепции", но на самом деле это работает.

Он содержит приложение "отправитель", которое использует WM_COPYDATA для отправки имени файла, строки и столбца получателю, размещенному в .Файл Bpl.

отправитель отправляет получателю строку типа

Filename=d:\aaad7\ota\dskfilesu.pas
Line=8
Col=12
Comment=(* some comment or other*)

строка комментария не является обязательным.

в рамках .Bpl, приемник использует Службы OTA, чтобы открыть запрошенный файл и расположить курсор редактора, затем вставляет комментарий, если таковой имеется.

самое сложное было выяснить, как справиться с одним конкретным осложнением, случай, когда именованный файл, который нужно открыть, является одним с связанной формой. Если да, то в D7 (и, я полагаю, в других версиях IDE с включенной опцией плавающего конструктора), когда IDE открывает .Pas-файл, он также открывает .Dfm, и оставил на свои собственные устройства, что бы оставить редактор форм в перед редактором кода. Вызов IOTASourceEditor.Шоу для .Pas-файл, по крайней мере, помещает редактор кода IDE перед ним .Форма Dfm, но это не удовлетворило меня, потому что к настоящему времени мое любопытство было задето - как вы получаете форму, которую IDE отображает с экрана?

Я провел много времени, исследуя различные тупики, потому что службы OTA + NTA, похоже, не предоставляют никакого способа явно закрыть IOTAEditor или любого из его потомков. В конце концов оказалось, что что нужно сделать, это просто получить ссылку на форму и просто отправить ее WM_CLOSE (!) - см. комментарии в коде.

чистки рядов, будучи новичком в ОТА, поначалу (пока я не узнала, как IOTAModules работать) я обнаружил, что наиболее сложная часть это было открытие, как заиметь IEditView интерфейс нужно установить редактор каре установки, но как обычно с этими interfacey вещи, как только вы получите "волшебное заклинание" совершенно верно, все это работает.

удачи! И спасибо за увлекательный проблема!

unit Receiveru;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, ToolsAPI;

type
  TOTAEditPosnForm = class(TForm)
    Memo1: TMemo;
  private
    FEdLine: Integer;
    FEdCol: Integer;
    FEditorFileName: String;
    FEditorInsert: String;
    procedure WMCopyData(var Msg : TWMCopyData); message WM_COPYDATA;
    procedure HandleCopyDataString(CopyDataStruct : PCopyDataStruct);
    procedure OpenInIDEEditor;
    property EditorFileName : String read FEditorFileName write FEditorFileName;
    property EdLine : Integer read FEdLine write FEdLine;
    property EdCol : Integer read FEdCol write FEdCol;
    property EditorInsert : String read FEditorInsert write FEditorInsert;
  end;

var
  OTAEditPosnForm: TOTAEditPosnForm;

procedure Register;

implementation

{$R *.dfm}

procedure MonitorFiles;
begin
  OTAEditPosnForm := TOTAEditPosnForm.Create(Nil);
  OTAEditPosnForm.Show;
end;

procedure Register;
begin
  MonitorFiles;
end;

procedure TOTAEditPosnForm.OpenInIDEEditor;
var
  IServices : IOTAServices;
  IActionServices : IOTAActionServices;
  IModuleServices : IOTAModuleServices;
  IEditorServices : IOTAEditorServices60;
  IModule : IOTAModule;
  i : Integer;
  IEditor : IOTAEditor;
  ISourceEditor : IOTASourceEditor;
  IFormEditor : IOTAFormEditor;
  IComponent : IOTAComponent;
  INTAComp : INTAComponent;
  AForm : TForm;
  IEditView : IOTAEditView;
  CursorPos : TOTAEditPos;
  IEditWriter : IOTAEditWriter;
  CharPos : TOTACharPos;
  InsertPos : Longint;
  FileName : String;
begin
  IServices := BorlandIDEServices as IOTAServices;
  Assert(Assigned(IServices), 'IOTAServices not available');

  IServices.QueryInterface(IOTAACtionServices, IActionServices);
  if IActionServices <> Nil then begin

    IServices.QueryInterface(IOTAModuleServices, IModuleServices);
    Assert(IModuleServices <> Nil);

    //  Close all files open in the IDE
    IModuleServices.CloseAll;

    if IActionServices.OpenFile(EditorFileName) then begin

      //  At this point, if the named file has an associated .DFM and
      //  we stopped here, the form designer would be in front of the
      //  code editor.

      IModule := IModuleServices.Modules[0];
      //  IModule is the one holding our .Pas file and its .Dfm, if any
      //  So, iterate the IModule's editors until we find the one
      //  for the .Pas file and then call .Show on it.  This will
      //  bring the code editor in front of the form editor.

      ISourceEditor := Nil;

      for i := 0 to IModule.ModuleFileCount - 1 do begin
        IEditor := IModule.ModuleFileEditors[i];
        FileName := IEditor.FileName;
        Memo1.Lines.Add(Format('%d %s', [i, FileName]));
        if CompareText(ExtractFileExt(IEditor.FileName), '.Pas') = 0 then begin
          if ISourceEditor = Nil then begin
            IEditor.QueryInterface(IOTASourceEditor, ISourceEditor);
            IEditor.Show;
          end
        end
        else begin
          // Maybe the editor is a Form Editor.  If it is
          // close the form (the counterpart to the .Pas, that is}
          IEditor.QueryInterface(IOTAFormEditor, IFormEditor);
          if IFormEditor <> Nil then begin
            IComponent := IFormEditor.GetRootComponent;
            IComponent.QueryInterface(INTAComponent, INTAComp);
            AForm := TForm(INTAComp.GetComponent);
            //AForm.Close; < this does NOT close the on-screen form
            // IActionServices.CloseFile(IEditor.FileName); <- neither does this
            SendMessage(AForm.Handle, WM_Close, 0, 0);  // But this does !
          end;
        end;
      end;

      //  Next, place the editor caret where we want it ...
      IServices.QueryInterface(IOTAEditorServices, IEditorServices);
      Assert(IEditorServices <> Nil);

      IEditView := IEditorServices.TopView;
      Assert(IEditView <> Nil);
      CursorPos.Line := edLine;
      CursorPos.Col := edCol;
      IEditView.SetCursorPos(CursorPos);
      //  and scroll the IEditView to the caret
      IEditView.MoveViewToCursor;

      //  Finally, insert the comment, if any
      if EditorInsert <> '' then begin
        Assert(ISourceEditor <> Nil);
        IEditView.ConvertPos(True, CursorPos, CharPos);
        InsertPos := IEditView.CharPosToPos(CharPos);
        IEditWriter := ISourceEditor.CreateUndoableWriter;
        Assert(IEditWriter <> Nil, 'IEditWriter');
        IEditWriter.CopyTo(InsertPos);
        IEditWriter.Insert(PChar(EditorInsert));
        IEditWriter := Nil;
      end;
    end;
  end;
end;

procedure TOTAEditPosnForm.HandleCopyDataString(
  CopyDataStruct: PCopyDataStruct);
begin
  Memo1.Lines.Text := PChar(CopyDataStruct.lpData);

  EditorFileName := Memo1.Lines.Values['FileName'];
  edLine := StrToInt(Memo1.Lines.Values['Line']);
  edCol := StrToInt(Memo1.Lines.Values['Col']);
  EditorInsert := Trim(Memo1.Lines.Values['Comment']);
  if EditorFileName <> '' then
    OpenInIDEEditor;
end;

procedure TOTAEditPosnForm.WMCopyData(var Msg: TWMCopyData);
begin
  HandleCopyDataString(Msg.CopyDataStruct);
  msg.Result := Length(Memo1.Lines.Text);
end;

initialization

finalization
  if Assigned(OTAEditPosnForm) then begin
    OTAEditPosnForm.Close;
    FreeAndNil(OTAEditPosnForm);
  end;
end.

код отправителя:

procedure TSenderMainForm.btnSendClick(Sender: TObject);
begin
  SendMemo;
end;

procedure TSenderMainForm.SendData(
  CopyDataStruct: TCopyDataStruct);
var
  HReceiver : THandle;
  Res : integer;
begin
  HReceiver := FindWindow(PChar('TOTAEditPosnForm'),PChar('OTAEditPosnForm'));
  if HReceiver = 0 then begin
    Caption := 'CopyData Receiver NOT found!';
  end
  else begin
    Res := SendMessage(HReceiver, WM_COPYDATA, Integer(Handle), Integer(@CopyDataStruct));
    if Res > 0 then
      Caption := Format('Received %d characters', [Res]);
  end;
end;

procedure TSenderMainForm.SendMemo;
var
  MS : TMemoryStream;
  CopyDataStruct : TCopyDataStruct;
  S : String;
begin
  MS := TMemoryStream.Create;
  try
    S := Memo1.Lines.Text + #0;
    MS.Write(S[1], Length(S));
    CopyDataStruct.dwData := 1;
    CopyDataStruct.cbData := MS.Size;
    CopyDataStruct.lpData := MS.Memory;
    SendData(CopyDataStruct);
  finally
    MS.Free;
  end;
end;