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

Dav*_*vid 8 delphi linker c++builder package

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

情况是这样的:

  • 第一个包,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
Run Code Online (Sandbox Code Playgroud)

所以这个类肯定似乎已导出并可用于链接.我可以看到ILink32说它正在寻找而不是寻找的特定事物的条目.在BPI文件上运行TDump显示类似的条目.

其他信息

该类确实来自TObject,虽然最初在重构为包之前它是一个普通的C++类.(下面的更多细节,似乎"更安全"试图无论如何解决这样一个非常德尔福十岁上下的事情的问题在使用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;
};
Run Code Online (Sandbox Code Playgroud)

并且FooB:

class FooB: public FooA {
public:
   FooB();
   virtual __fastcall ~FooB();
   ... other methods...
};
Run Code Online (Sandbox Code Playgroud)

所有方法肯定都是在.cpp文件中实现的,因此它们并不是因为它们不存在而找不到它们!.cpp文件还包含#pragma package(smart_init)在include附近的顶部附近.

可能有帮助的问题......

  • 包使用C++是可靠的,还是只能用于Delphi代码?
  • 通过添加对其BPI的引用正确链接到第一个包 - 您应该如何做到这一点?我可以使用LIB,但它似乎使第二个包更大,我怀疑它是第一个内容的静态链接.
  • 我们可以PACKAGE仅在TObject-derived类上使用该指令吗?在标准C++类上没有使用它的编译器警告.
  • 将代码拆分成包是实现隔离代码和通过定义的层/接口进行通信的最佳方法吗?我一直在调查这条路径,因为它似乎是C++ Builder/Delphi方式,如果它起作用,它看起来很有吸引力.但是有更好的选择吗?
  • 我很擅长使用软件包,并且之前只使用过组件才知道它们.任何一般性的建议都会很棒!

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

Tom*_*sen 9

未解决的外部问题

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

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

如果其中一个是真的那么路径不是问题.然而,Riho也提到这是问题的最可能原因.该Embarcadero的文档维基状态下面对无法解析的外部错误:

命名符号在给定模块中引用,但未在链接中包含的目标文件和库集合中的任何位置定义.检查以确保符号拼写正确.

如果发生以下任何一种情况,您通常会在链接器中看到C或C++符号的错误:

  • 您没有正确匹配符号在不同源文件中的声明__pascal__cdecl类型.
  • 您已省略了程序所需的目标文件的名称.您需要手动将所有必需的包添加到"需求"列表中.
  • 您没有在仿真库中链接.

如果要将C++代码与C模块链接,则可能忘记在外部"C"中包装C外部声明.

您也可能在两个符号之间出现大小写不匹配.

来源:未解决的"模块"引用的外部"符号".

因为它似乎来自 - 虽然改变了类名 - 但它不是拼写错误的情况.您还声明已将包添加到需求列表中,因此我们也排除了这一点.由于您没有链接到C模块,我们也可以省略该部分.所以它指出了目录的问题.

关于其他问题

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

包使用C++可靠吗?

包是一个很好的解决方案,可用于C++ Builder,C++ Builder都支持包和Pascal编写的VCL框架.这意味着某些实现在C++ Builder中与其他编译器不同.这是保持语言与其Delphi兄弟兼容的必要条件.出于这个原因,你可以在C++ Builder中使用的包几乎一样容易,如果采用Delphi.

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

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

我个人总是确保我的软件包到你的用户文件夹中的正确目录,这个位置取决于你的Delphi版本和操作系统版本.据我所知,它是在Document and Settings \所有用户\共享文件\ Rad工作室(版本号)\包下,但我可能会弄错.

我们可以PACKAGE仅在TObject-derived类上使用该指令吗?

PACKAGE宏解析为__declspec(package),您可以将其与之进行比较__declspec(dllexport).它们之间的区别在于,在中声明时使用包,并且在DLL中声明时使用dllexport.在官方的embarcadero论坛上有一个名为__declspec(package)vs __declspec(dllexport)的话题.原帖的作者也问你关于这个问题的确切问题,但不幸的是,部分问题没有答案.

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

__declspec(dllexport)可用于普通函数,数据变量和非VCL类,并且可以在普通DLL中使用.__declspec(包)用于VCL组件,并且只能与包一起使用.

因此,通过阅读他的回答,在我看来,包只是导出类,就像dllexport一样.而且,因为我可以从他的响应中读取的dllexport将在普通的DLL中使用,所以只需要使用该包从包中导出(甚至)非VCL类.

有趣的是,就我记忆而言,一个软件包本质上是一个DLL,但我必须承认,我无法找到或记住这些信息的来源,所以需要一点点精力.

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

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

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

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

有任何一般性的建议吗?

我自己并不是最有经验的软件包开发人员,但我发现禁用运行时链接通常可以解决我的很多问题,而修复自己代码的任何问题都有些微不足道,你经常遇到第三方组件处理这个问题很困难.话虽如此,我不喜欢在这种情况下按照我的应用程序分发我的软件包.但说实话,这是一个品味问题.

就个人而言,当我开始创建组件和包时,我发现很难找到我的许多问题的正确答案.官方帮助文件在这方面的信息量最大,但通过VCL源代码查看,通常会为您提供最佳答案.此外还有一些其他网站可以提供帮助,但很多网站都针对Delphi,但是你必须习惯这个.

Delphi Wikia有一些关于创建组件的好文章,特别是创建组件创建包还有BCB Journal,它是为数不多的C++ Builder特定站点之一,它有一些很好的文章和一个可接受的论坛.About.com上Delphi页面也是一个很好的信息来源,我发现了许多好的提示并且很高兴知道,特别是:创建自定义Delphi组件 - 内部和外部.