为什么在我 运行 我的项目使用导入的 .lib 时调用 "user breakpoint",而不是在代码内联时调用?
Why is a "user breakpoint" called when I run my project with imported .lib, not when code is inline?
情况
我正在编写一个用于 GPIB 通信的包装器库,用于根据客户规范设置特定仪器以自动化他们的某些过程。我必须在 Windows NT 机器上使用 VC++ 6.0 运行 中的 C++ 和旧的 '98 编译器,以保持与他们经常使用的其他一些设备的兼容性。
我正在尝试制作一个 class 将一些 GPIB 命令组合成更容易记住的功能,同时还保持与仪器直接通信的能力。为此,我将项目的不同部分编译成不同的库和 dll,每个 dll 都是它们可能想要与之通信的不同设备。我还制作了一个通用的 dll 基础 class,所有特定的工具 classes 都从中继承,希望使整个设置尽可能模块化。
问题
所以,综上所述,我在尝试测试不同的 dlls/modules 时遇到了问题。我创建了一个项目来测试通用基础 class,将 .lib 文件添加到项目中,该文件链接到 .dll,并且还 #include
d 该 dll 的头文件。 testGeneric.cpp
看起来像这样:
#include "GENERIC.h"
void main(void) {
GPIBInstrument hp(2); //connects to device at primary address #2
hp.write("*IDN?");
}
超级简单。需要明确的是,我还在 VC++ 6.0 的 "Resource Files" 文件夹中链接了 GENERIC.lib
,并且我可以从路径变量访问 GENERIC.dll
。
继续,GENERIC.h
看起来像这样(select 部分):
#ifndef GENERIC_H
#define GENERIC_H
#include <string>
#include <windows.h>
#include "decl-32.h"
#ifdef GENERIC_EXPORT
#define GENERIC_API __declspec(dllexport)
#else
#define GENERIC_API __declspec(dllimport)
#endif
...(Inline exception classes)...
class GENERIC_API GPIBInstrument {
...
public:
void write(std::string command);
...
};
#endif
只是展示相关的方法。然后 GENERIC.cpp:
#define GENERIC_EXPORT
#include "GENERIC.h"
...
void GPIBInstrument::write(std::string command) {
ibwrt (handle, &command[0u], command.length());
std::cout << command << std::endl;
if (ibsta & TIMO) {
timeoutError();
}
if (ibsta & ERR) {
error("Unable to write command to instrument: " + command);
}
}
所以,看起来不错吧?没有问题。编译正常。我试试 运行 吧,然后 BLAM!我明白了:"User breakpoint called from code at 0x77f7645c"。所以,然后我想,如果我将 GENERIC.h
和 GENERIC.cpp
中的所有代码放入一个文件中,然后 #include
d 将该文件全部作为内联代码,也许它会起作用。所以我试了一下,它编译得很好,运行 很好。
问题(<-啊哈!...但是...)
我做错了什么!?我制作 .dll 的方式有问题吗?还是.lib?还是完全不同?
编辑(为什么!?)
所以,经过一番调试,我发现它与传递字符串文字有关。所以我只是将其修改为:
std::string command = "*IDN?";
hp.write(command);
而且效果很好。我的后续问题是为什么?传递字符串文字与将其分配给变量然后传入之间有什么区别?
使用 std::string 等复杂类型作为 DLL 边界的参数是很棘手的。您必须确保 exe 和 DLL 使用完全相同的库代码实例。这要求您构建它们以使用运行时库的相同版本的 DLL 版本。
情况
我正在编写一个用于 GPIB 通信的包装器库,用于根据客户规范设置特定仪器以自动化他们的某些过程。我必须在 Windows NT 机器上使用 VC++ 6.0 运行 中的 C++ 和旧的 '98 编译器,以保持与他们经常使用的其他一些设备的兼容性。
我正在尝试制作一个 class 将一些 GPIB 命令组合成更容易记住的功能,同时还保持与仪器直接通信的能力。为此,我将项目的不同部分编译成不同的库和 dll,每个 dll 都是它们可能想要与之通信的不同设备。我还制作了一个通用的 dll 基础 class,所有特定的工具 classes 都从中继承,希望使整个设置尽可能模块化。
问题
所以,综上所述,我在尝试测试不同的 dlls/modules 时遇到了问题。我创建了一个项目来测试通用基础 class,将 .lib 文件添加到项目中,该文件链接到 .dll,并且还 #include
d 该 dll 的头文件。 testGeneric.cpp
看起来像这样:
#include "GENERIC.h"
void main(void) {
GPIBInstrument hp(2); //connects to device at primary address #2
hp.write("*IDN?");
}
超级简单。需要明确的是,我还在 VC++ 6.0 的 "Resource Files" 文件夹中链接了 GENERIC.lib
,并且我可以从路径变量访问 GENERIC.dll
。
继续,GENERIC.h
看起来像这样(select 部分):
#ifndef GENERIC_H
#define GENERIC_H
#include <string>
#include <windows.h>
#include "decl-32.h"
#ifdef GENERIC_EXPORT
#define GENERIC_API __declspec(dllexport)
#else
#define GENERIC_API __declspec(dllimport)
#endif
...(Inline exception classes)...
class GENERIC_API GPIBInstrument {
...
public:
void write(std::string command);
...
};
#endif
只是展示相关的方法。然后 GENERIC.cpp:
#define GENERIC_EXPORT
#include "GENERIC.h"
...
void GPIBInstrument::write(std::string command) {
ibwrt (handle, &command[0u], command.length());
std::cout << command << std::endl;
if (ibsta & TIMO) {
timeoutError();
}
if (ibsta & ERR) {
error("Unable to write command to instrument: " + command);
}
}
所以,看起来不错吧?没有问题。编译正常。我试试 运行 吧,然后 BLAM!我明白了:"User breakpoint called from code at 0x77f7645c"。所以,然后我想,如果我将 GENERIC.h
和 GENERIC.cpp
中的所有代码放入一个文件中,然后 #include
d 将该文件全部作为内联代码,也许它会起作用。所以我试了一下,它编译得很好,运行 很好。
问题(<-啊哈!...但是...)
我做错了什么!?我制作 .dll 的方式有问题吗?还是.lib?还是完全不同?
编辑(为什么!?)
所以,经过一番调试,我发现它与传递字符串文字有关。所以我只是将其修改为:
std::string command = "*IDN?";
hp.write(command);
而且效果很好。我的后续问题是为什么?传递字符串文字与将其分配给变量然后传入之间有什么区别?
使用 std::string 等复杂类型作为 DLL 边界的参数是很棘手的。您必须确保 exe 和 DLL 使用完全相同的库代码实例。这要求您构建它们以使用运行时库的相同版本的 DLL 版本。