delphi - 使用 C++ Builder 包时如何解决未解析的外部问题?

标签 delphi linker package c++builder

我正在尝试重新配置我的应用程序以大量使用包。我和另一个运行类似实验的开发人员在使用几个不同的包进行链接时都遇到了一些麻烦。我们可能都做错了什么,但天知道是什么:)

情况是这样的:

  • 第一个包 PackageA.bpl 包含 C++ 类 FooA 。该类是用 PACKAGE 指令声明的。
  • 第二个包 PackageB.bpl 包含一个继承自 FooA 的类,称为 FooB 。它包括 FooB.h ,该包是使用运行时包构建的,并通过添加对 PackageA 的引用链接到 PackageA.bpi
  • 构建 PackageB 时,它编译正常,但链接失败,有许多未解析的外部变量,其中前几个是:
  • [ILINK32 Error] Error: Unresolved external '__tpdsc__ FooA' referenced from C:\blah\FooB.OBJ
  • [ILINK32 Error] Error: Unresolved external 'FooA::' referenced from C:\blah\FooB.OBJ
  • [ILINK32 Error] Error: Unresolved external '__fastcall FooA::~FooA()' referenced from blah\FooB.OBJ


  • PackageA.bpl 上运行 TDump 显示:
    Exports from PackageA.bpl
      14 exported name(s), 14 export addresse(s).  Ordinal base is 1.
      Sorted by Name:
        RVA      Ord. Hint Name
        -------- ---- ---- ----
        00002A0C    8 0000 __tpdsc__ FooA
        00002AD8   10 0001 __linkproc__ FooA::Finalize
        00002AC8    9 0002 __linkproc__ FooA::Initialize
        00002E4C   12 0003 __linkproc__ PackageA::Finalize
        00002E3C   11 0004 __linkproc__ PackageA::Initialize
        00006510   14 0007 FooA::
        00002860    5 0008 FooA::FooA(FooA&)
        000027E4    4 0009 FooA::FooA()
        00002770    3 000A __fastcall FooA::~FooA()
        000028DC    6 000B __fastcall FooA::Method1() const
        000028F4    7 000C __fastcall FooA::Method2() const
        00001375    2 000D Finalize
        00001368    1 000E Initialize
        0000610C   13 000F ___CPPdebugHook
    

    因此,该类似乎已导出并可用于链接。我可以看到 ILink32 说它正在寻找但没有找到的特定事物的条目。在 BPI 文件上运行 TDump 会显示类似的条目。

    其他信息

    该类确实从 TObject 派生而来,尽管最初在重构为包之前它是一个普通的 C++ 类。 (下面有更多详细信息。无论如何,在尝试解决像这样的非常像 Delphi 一样的问题时,使用 VCL 样式的类似乎“更安全”。更改这只会将未解析的外部对象的顺序更改为首先找不到 Method1Method2 ,然后是其他.)
    FooA 的声明:
    class PACKAGE FooA: public TObject {
    public:
       FooA();
       virtual __fastcall ~FooA();
       FooA(const FooA&);
       virtual __fastcall long Method1() const;
       virtual __fastcall long Method2() const;
    };
    

    FooB :
    class FooB: public FooA {
    public:
       FooB();
       virtual __fastcall ~FooB();
       ... other methods...
    };
    

    所有方法肯定都在 .cpp 文件中实现,所以它不会因为它们不存在而找不到它们! .cpp 文件在顶部附近还包含 #pragma package(smart_init),在包含之下。

    可能有帮助的问题...
  • 使用 C++ 的包是否可靠,还是只能与 Delphi 代码一起使用?
  • 通过添加对其 BPI 的引用来链接到第一个包是否正确 - 你应该这样做吗?我可以使用 LIB,但它似乎使第二个包更大,我怀疑它静态链接到第一个包的内容中。
  • 我们可以只在 PACKAGE 派生类上使用 TObject 指令吗?在标准 C++ 类上使用它没有编译器警告。
  • 将代码拆分成包是实现隔离代码和通过定义的层/接口(interface)进行通信的目标的最佳方式吗?我一直在研究这条路径,因为它似乎是 C++Builder/Delphi Way,如果它有效,它看起来很有吸引力。但是有更好的选择吗?
  • 我对使用包非常陌生,之前只通过使用组件了解过它们。任何一般的建议都会很棒!

  • 我们使用的是 C++Builder 2010。我在上面的代码示例中编造了类和方法名称,但除此之外,细节正是我们所看到的。

    最佳答案

    Unresolved external

    在您的情况下未解析的外部似乎是因为编译器无法找到包数据的路径。您应该了解是否:

  • 该路径存在于编译器搜索路径列表中。
  • 该包存在于默认包目录中。

  • 如果其中之一为真,则路径不是问题。然而作为 Riho还提到这是导致问题的最可能原因。 Embarcadero documentation wiki关于 Unresolved external 错误的说明如下:

    The named symbol is referenced in the given module but is not defined anywhere in the set of object files and libraries included in the link. Check to make sure the symbol is spelled correctly.

    You will usually see this error from the linker for C or C++ symbols if any of the following occur:

    • You did not properly match a symbol’s declarations of __pascal and __cdecl types in different source files.
    • You have omitted the name of an object file your program needs. You need to manually add all required packages to the Requires list.
    • You did not link in the emulation library.

    If you are linking C++ code with C modules, you might have forgotten to wrap C external declarations in extern “C”.

    You could also have a case mismatch between two symbols.



    来源:Unresolved external 'symbol' referenced from 'module' .

    由于从 - 尽管更改了类名 - 看来,这不是拼写错误的情况。您还声明您已将该包添加到需求列表中,因此我们也排除了这一点。由于您没有链接到 C 模块,我们也可以省略该部分。所以它指向目录的问题。

    关于其他问题

    你的问题都很有趣,很多问题都是我自己在开始为 C++ Builder 开发包和组件时一直在寻找答案的问题。

    使用 C++ 的包是否可靠?

    包是用于 C++ Builder 的一个很好的解决方案,C++ Builder 都是为了支持包和 Pascal 编写的 VCL 框架而构建的。这意味着 C++ Builder 中的某些实现与其他编译器不同。这是保持语言与其 Delphi 兄弟兼容的必要条件。因此,您几乎可以像使用 Delphi 一样轻松地在 C++ Builder 中使用包。

    通过添加对其 BPI 的引用来链接到第一个包是否正确?

    从这里问题的第二部分开始,使用 lib 文件会使您的包更大,因为它使用静态链接 - 所以您的猜测是正确的。现在回到问题的第一部分,通过添加对其 BPI 的引用来链接到包。但是您确实需要确保路径变量已正确设置为 Riho在他的回答中建议。

    就我个人而言,我总是确保将我的包放到用户文件夹中的正确目录中,其位置取决于您的 Delphi 版本和操作系统版本。据我记忆,它位于 Document and Settings\all users\shared documents\Rad studio(version number)\Packages 下,但我可能会误会它。

    我们可以使用PACKAGE吗?指令仅适用于 TObject -派生类?
    PACKAGE宏解析为 __declspec(package) ,你可以把它比作 __declspec(dllexport) .它们之间的区别在于在包中声明时使用包,而在DLL中声明时使用dllexport。官方 embarcadero 论坛上有一个关于此的主题,标题为 __declspec(package) vs __declspec(dllexport) .原帖的作者也问了你关于这个的确切问题,但不幸的是,问题的一部分没有得到解答。

    然而,我有一个理论,我必须强调它只不过是一个理论。 Remy Lebeau 在论坛帖子中回答问题时写道:

    __declspec(dllexport) can be used for plain functions, data variables, and non-VCL classes, and can be used in plain DLLs. __declspec(package) is used for VCL components, and can only be used with packages.



    因此,从阅读他的回复来看,在我看来 package 只是导出类,就像 dllexport 一样。并且由于 dllexport 就我从他的回复中读到的而言是在普通 DLL 中使用的,因此您必须使用该包从包中导出(甚至)非 VCL 类。

    所有这一切的有趣之处在于,据我所知,包本质上是一个 DLL,但我必须承认我无法找到或记住该信息的来源,因此请谨慎对待。

    将代码拆分成包是实现代码隔离目标的最佳方式吗?

    在为 VCL 创建可重用组件时,包具有一些非常突出的优势。显然,使用包限制了用户使用 C++Builder 或 Delphi,但对于为利用 VCL 框架编写的组件来说,这是一个很好的选择。正确编写的包可以简化组件的可重用性,我相信这是为 VCL 分发组件的首选方法。

    但是,如果您的代码没有以任何方式利用 VCL 框架,我会考虑使用一个普通的库,无论是静态的还是动态的,只是为了创建一种对交叉编译器更友好的方法。

    是否有更好的方法来隔离您的代码,实际上取决于您正在从事的项目。我喜欢在包中保留通过使用 VCL 类进行通信的代码,但在常规库中保留不需要使用任何 VCL 类的代码。请记住,虽然您可以轻松地在 DLL 中使用 VCL 类,但如果您选择将带有 VCL 字符串类的函数作为参数或返回值导出,则需要处理特殊情况。

    有什么通用的建议吗?

    我自己并不是最有经验的包开发人员,但我发现禁用运行时链接通常可以解决我的很多问题,虽然修复您自己的代码的任何问题有些微不足道,但您经常会遇到 3rd 方组件那些在处理这个问题上有困难的人。话虽如此,在这种情况下,我不喜欢将我的包与我的应用程序一起分发。但说实话,这是一个品味问题。

    就我个人而言,当我开始创建组件和包时,我发现很难为我的许多问题找到正确的答案。官方帮助文件并不是关于此事的最丰富的信息,但查看 VCL 源代码,通常会为您的问题提供最佳答案。除了有一些其他网站可以提供帮助外,许多网站虽然都针对 Delphi,但您必须习惯这一点。

    Delphi Wikia 有一些关于创建组件的好文章,特别是 Creating ComponentsCreating Packages还有BCB Journal这是为数不多的特定于 C++ Builder 的站点之一,它有一些精美的文章和一个可接受的论坛。 Delphi pages at About.com也是一个很好的信息来源,我在那里找到了很多很好的提示和很高兴知道,特别是:Creating Custom Delphi Components - Inside and Out .

    关于delphi - 使用 C++ Builder 包时如何解决未解析的外部问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2727001/

    相关文章:

    delphi - 使用 TIdFTP Delphi 更改 FTP 中的文件权限

    c++ - 在 C 和 C++ 中包含和链接文件

    c++ - 使用 configure.ac 找不到 boost c++

    go - 错误:目录中的代码…希望导入“…”-我该怎么办?

    Delphi 对 Aero Glass 和 DoubleBuffered 属性的支持 - 发生了什么以及我们如何使用它们?

    delphi - 如何分析 Delphi DLL 文件中的转储文件?

    delphi - Delphi XE2 中的 IntraWeb 12 的数据库、 session 和持久性

    compiler-construction - 编程语言的标准库是如何实现的?

    c++ - 继承 - 符号未定义 Objective-C++

    c++ - 如何在 C/C++ 程序中嵌入 GNU Octave?