组件如何在设计时确定项目目录

发布于 2024-08-24 17:44:28 字数 247 浏览 4 评论 0原文

我编写了一个组件,它应该存储一些与项目目录相关的信息。每次更改组件的属性时,它都应该写入一个文件。那么组件如何在设计时确定当前项目目录呢?

提前致谢

编辑:
我想在每次更改组件的属性时生成一个 delphi 源文件,以便在编译代码时始终获得最新版本。将其视为一种代码生成器。

目前,我设置了应存储源的完整路径和文件名,但我更喜欢项目的相对路径(或包含我的组件的表单/数据模块),以便更轻松地在不同的开发人员计算机上复制项目。

I write a component which should store some information relative to the project directory. Every time a property of my component is changed it should write a file. So how can a component determine the current project directory at design time.

Thanks in advance

EDIT:
I want to generate a delphi source file every time a property of my component is changed, so that I always get the latest version when I compile my code. Think of it as a kind of code generator.

At the moment I set whole path and filename where the source should be stored but I prefer a relative path to the project (or the form/datamodule which contains my component) to make it easier to copy the project on different developer machines.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

合久必婚 2024-08-31 17:44:28

感谢您的提示。开放工具 API 是可行的方法,并且可以在设计时从表单上的组件使用开放工具 API。

这是我的解决方案:

我需要两个单元,一个用于组件,另一个用于注册组件和使用开放工具 API 的代码。

这是组件单元:


unit TestLabels;

interface

uses
  SysUtils, Classes, Windows, Controls, StdCtrls;

type
  TTestLabel = class(TLabel)
  private
    FTestProperty: Boolean;
    procedure SetTestProperty(const Value: Boolean);
    procedure Changed;
  published
    property TestProperty: Boolean read FTestProperty write SetTestProperty;
  end;

var
  OnGetUnitPath: TFunc;

implementation

{ TTestLabel }

procedure TTestLabel.Changed;
begin
  if not (csDesigning in ComponentState) then
     Exit; // I only need the path at designtime

  if csLoading in ComponentState then
     Exit; // at this moment you retrieve the unit path which was current before

  if not Assigned(OnGetUnitPath) then
    Exit;

  // only for demonstration
  Caption := OnGetUnitPath;
  MessageBox(0, PChar(ExtractFilePath(OnGetUnitPath)), 'Path of current unit', 0);
end;

procedure TTestLabel.SetTestProperty(const Value: Boolean);
begin
  if FTestProperty  Value then
  begin
    FTestProperty := Value;
    Changed;
  end;
end;

end.

这是注册组件和调用 Open Tools API 的单元:


unit TestLabelsReg;

interface

uses
  SysUtils, Classes, Controls, StdCtrls, TestLabels;

procedure register;

implementation

uses
  ToolsAPI;

function GetCurrentUnitPath: String;
var
  ModuleServices: IOTAModuleServices;
  Module: IOTAModule;
  SourceEditor: IOTASourceEditor;
  idx: integer;

begin
  Result := '';
  SourceEditor := nil;

  if SysUtils.Supports(BorlandIDEServices, IOTAModuleServices,
    ModuleServices) then
  begin
    Module := ModuleServices.CurrentModule;

    if System.Assigned(Module) then
    begin
      idx := Module.GetModuleFileCount - 1;

      // Iterate over modules till we find a source editor or list exhausted
      while (idx >= 0) and not SysUtils.Supports(Module.GetModuleFileEditor(idx), IOTASourceEditor, SourceEditor) do
        System.Dec(idx);

      // Success if list wasn't ehausted.
      if idx >= 0 then
        Result := ExtractFilePath(SourceEditor.FileName);
    end;

  end;

end;

procedure register;
begin
  RegisterComponents('Samples', [TTestLabel]);
  TestLabels.OnGetUnitPath := GetCurrentUnitPath;
end;

end.

Thanks for the hints. Open Tools API is the way to go and using the Open Tools API from a component on a form at designtime is possible.

So here is my solution:

I need two units, one for the component and one for registering the component and the code which use the Open Tools API.

Here comes the component unit:


unit TestLabels;

interface

uses
  SysUtils, Classes, Windows, Controls, StdCtrls;

type
  TTestLabel = class(TLabel)
  private
    FTestProperty: Boolean;
    procedure SetTestProperty(const Value: Boolean);
    procedure Changed;
  published
    property TestProperty: Boolean read FTestProperty write SetTestProperty;
  end;

var
  OnGetUnitPath: TFunc;

implementation

{ TTestLabel }

procedure TTestLabel.Changed;
begin
  if not (csDesigning in ComponentState) then
     Exit; // I only need the path at designtime

  if csLoading in ComponentState then
     Exit; // at this moment you retrieve the unit path which was current before

  if not Assigned(OnGetUnitPath) then
    Exit;

  // only for demonstration
  Caption := OnGetUnitPath;
  MessageBox(0, PChar(ExtractFilePath(OnGetUnitPath)), 'Path of current unit', 0);
end;

procedure TTestLabel.SetTestProperty(const Value: Boolean);
begin
  if FTestProperty  Value then
  begin
    FTestProperty := Value;
    Changed;
  end;
end;

end.

Here is the unit for registering the component and the call to the Open Tools API:


unit TestLabelsReg;

interface

uses
  SysUtils, Classes, Controls, StdCtrls, TestLabels;

procedure register;

implementation

uses
  ToolsAPI;

function GetCurrentUnitPath: String;
var
  ModuleServices: IOTAModuleServices;
  Module: IOTAModule;
  SourceEditor: IOTASourceEditor;
  idx: integer;

begin
  Result := '';
  SourceEditor := nil;

  if SysUtils.Supports(BorlandIDEServices, IOTAModuleServices,
    ModuleServices) then
  begin
    Module := ModuleServices.CurrentModule;

    if System.Assigned(Module) then
    begin
      idx := Module.GetModuleFileCount - 1;

      // Iterate over modules till we find a source editor or list exhausted
      while (idx >= 0) and not SysUtils.Supports(Module.GetModuleFileEditor(idx), IOTASourceEditor, SourceEditor) do
        System.Dec(idx);

      // Success if list wasn't ehausted.
      if idx >= 0 then
        Result := ExtractFilePath(SourceEditor.FileName);
    end;

  end;

end;

procedure register;
begin
  RegisterComponents('Samples', [TTestLabel]);
  TestLabels.OnGetUnitPath := GetCurrentUnitPath;
end;

end.
无名指的心愿 2024-08-31 17:44:28

从delphi 7开始,ToolsAPI单元定义了一个getActiveProject函数,它返回当前项目的IOTAProject接口。

IOTAProject 的 fileName 属性返回项目主源文件(通常是 .dpr 文件)的完整路径。

因此,在许多情况下,可以使用简单的指令,例如:(

if csDesigning in componentState then
    appFolderPath := extractFilePath( getActiveProject.fileName ) 
else
    appFolderPath := extractFilePath( application.exeName );

并且不需要像上面亨氏的示例那样使用两个单位)

Starting with delphi 7, the ToolsAPI unit defines a getActiveProject function which returns the IOTAProject interface of the current project.

The fileName property of IOTAProject returns the full path of the main source file of the project (typically the .dpr file).

So, in many cases, it's possible to use a simple instruction such as:

if csDesigning in componentState then
    appFolderPath := extractFilePath( getActiveProject.fileName ) 
else
    appFolderPath := extractFilePath( application.exeName );

(and there's no need of using two units as in Heinz's example above)

天煞孤星 2024-08-31 17:44:28

组件无法访问您的源路径,因为组件被放置在您的应用程序中并作为应用程序的一部分在 Delphi IDE 之外运行。

如果您想访问项目路径,或者自动化 IDE 内的任何流程;你必须使用 OpenTools API 编写一个 IDE 专家,而不是一个组件。

A component cannot access your source path, because a component is placed in your application and run as a part of your application out of Delphi IDE.

If you want to have access to project path, or automate any process inside IDE; you have to write an IDE expert using OpenTools API, not a component.

<逆流佳人身旁 2024-08-31 17:44:28

我认为不可以。您可以很容易地确定 EXE 运行的目录,但设计时的组件是作为 IDE 的一部分运行的。我怀疑组件是否有办法通过 IDE 访问项目信息。

I don't think it can. You can determine the directory your EXE is running in easily enough, but your component at design-time is running as a part of the IDE. I doubt there's a way for the component to access project information through the IDE.

真心难拥有 2024-08-31 17:44:28

请参阅

http://www.gexperts.org/otafaq.html#project

,然后

www.href.com/pub/sw/ProjectOptions.html

可能有帮助

see

http://www.gexperts.org/otafaq.html#project

and then

www.href.com/pub/sw/ProjectOptions.html

may be it helps

沉睡月亮 2024-08-31 17:44:28

我有一个有点不同的目标:如何知道项目 exe 在设计时的位置,以便应用程序在运行时可以与设计时功能共享一些文件。

因此,我结合了两个最佳答案,并创建了以下要在应用程序中使用的函数:

unit MyUtils;

interface

function GetProjectTargetPath: string;

var

  _GetProjectTargetPath: TFunc<string>;

...
implementation

function GetProjectTargetPath: string;
begin
  Result := '';
  if Assigned(_GetProjectTargetPath) then
    Result := _GetProjectTargetPath;
  if Result <> '' then
    Result := IncludeTrailingPathDelimiter(Result);
end;

然后在组件注册单元中(我将其分开并仅链接到设计时包):

unit MyUtils;

...
implementation

function GetProjectTargetPath: String;
begin
  Result := ExtractFilePath( GetActiveProject.ProjectOptions.TargetName );
end;

procedure Register;
begin
  ...
  MyUtils._GetProjectTargetPath := GetProjectTargetPath;
end;

从而避免实际单元依赖于 ToolsAPI 。

MyUtils.GetProjectTargetPath 在运行时返回一个空字符串,实际上对应于应用程序的 exe 目录。

I had A little bit different objective: how to know where the project exe will be at design time, so that the application at runtime can share some files with the design time functionality.

So, I combined the two best answers and created the following function to be used in applications:

unit MyUtils;

interface

function GetProjectTargetPath: string;

var

  _GetProjectTargetPath: TFunc<string>;

...
implementation

function GetProjectTargetPath: string;
begin
  Result := '';
  if Assigned(_GetProjectTargetPath) then
    Result := _GetProjectTargetPath;
  if Result <> '' then
    Result := IncludeTrailingPathDelimiter(Result);
end;

and then in the component registration unit (which I have separate and linked only to the design time package):

unit MyUtils;

...
implementation

function GetProjectTargetPath: String;
begin
  Result := ExtractFilePath( GetActiveProject.ProjectOptions.TargetName );
end;

procedure Register;
begin
  ...
  MyUtils._GetProjectTargetPath := GetProjectTargetPath;
end;

Thus avoiding the actual unit to depend on ToolsAPI.

MyUtils.GetProjectTargetPath returns an empty string at runtime, which corresponds to the application exe-directory in practice.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文