【问题标题】:LNK2005 Error in CLR Windows FormCLR Windows 窗体中的 LNK2005 错误
【发布时间】:2016-04-17 09:04:24
【问题描述】:

我正在开发一个 Windows CLR 表单,以便为我作为控制台程序处理的一些代码创建 GUI 交互。

当我在代码的控制台部分包含标头时,我的两个标头可以很好地协同工作,但是当我尝试将它们包含在 form 中时,它们会导致以下结果:

librarytest.obj:错误 LNK2005:_SeqWait 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_KillDLL 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_SetSinFreq2 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_ConnectDirect 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_GetDevice 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_SetSinFreq_Fine2 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_Connect 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_TacOnTimeForTAction 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_SetSinFreq1 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_GetLastEAIError 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_SetGain 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_Disconnect 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_ReadFWVer 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_SetSinFreq_Fine1 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_SetSigSrc 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_ClosePort 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_ShowDebugInfo 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_OpenPort 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_DiscoverDevices 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_TacOnTime 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_PulseOnTime 已在 Gesture_Elicitor.obj 中定义

librarytest.obj:错误 LNK2005:_tactorhandle 已在 Gesture_Elicitor.obj 中定义

....

有趣的问题是,我的一个标题(“wiimote.h”,来自 WiiYourself 项目)如果仅包含一个标题,则可以正常工作。问题在于“tactor_cHeader.h”,它连接到它的 .dll 。有问题的缩写代码如下:

#ifndef TACTOR_H_
#define TACTOR_H_

using namespace std;

#include <windows.h>

...

typedef int (*ConnectDirectPtr)(char*name, int type);
typedef int (*TacOnTimePtr)(int cidx, int board, int tacNum, int durMilli, bool returnifprocessing);
typedef int (*SetFreqPtr)(int cidx, int board, int freq, bool returnifprocessing);
typedef int (*KillDLLptr)();
typedef int (*SeqWaitPtr)(int cidx, int board, int waitTime, bool returnifprocessing);
...

ConnectDirectPtr ConnectDirect;
TacOnTimePtr TacOnTimeForTaction;
SetFreqPtr SetSinFreq1;
SetFreqPtr SetSinFreq2;
KillDLLptr KillDLL;
SeqWaitPtr SeqWait;
...

HINSTANCE tactorhandle = NULL;

inline int InitTactorDLL()
{
    tactorhandle = LoadLibrary("Tactor_DLL.dll");
    if (tactorhandle == 0)
        return -1;
    SeqWait = (SeqWaitPtr)GetProcAddress(tactorhandle, "SeqWait");
    ConnectDirect = (ConnectDirectPtr)GetProcAddress(tactorhandle, "ConnectDirect");
    TacOnTime = (TacOnTimePtr)GetProcAddress(tactorhandle, "TacOnTime");
    SetSinFreq1 = (SetFreqPtr)GetProcAddress(tactorhandle, "SetSinFreq1");
    SetSinFreq2 = (SetFreqPtr)GetProcAddress(tactorhandle, "SetSinFreq2");
    KillDLL = (KillDLLptr)GetProcAddress(tactorhandle, "KillDLL");
}

#endif

那么这个标题与我的表单不匹配的原因是什么?

【问题讨论】:

  • 你能把代码(和文件名)贴在SeqWaitSetSinFreq2KillDLL定义的地方吗?
  • 我编辑了原始文档以符合您的要求 @CristiFati 。这有帮助吗?

标签: visual-studio-2010 visual-c++


【解决方案1】:

抱歉回复晚了。

问题很简单,你的头文件中有变量定义。一般来说,头文件应该只包含声明。检查[SO]: What is the difference between a definition and a declaration? 以查看两者之间的区别。

要修复,您应该移动这些:

ConnectDirectPtr ConnectDirect;
TacOnTimePtr TacOnTimeForTaction;
SetFreqPtr SetSinFreq1;
SetFreqPtr SetSinFreq2;
KillDLLptr KillDLL;
SeqWaitPtr SeqWait;
//...

HINSTANCE tactorhandle = NULL;

放入真正需要它们的 .c 源文件中,或将它们设为 extern ([MS.Docs]: extern (C++))。

背景

C (C++) 代码构建成可移植的可执行代码 有3 个阶段(这里我指的是. exe.dll 文件)。欲了解更多信息,请查看[MS.Docs]: Peering Inside the PE: A Tour of the Win32 Portable Executable File Format

  1. 预处理

    • 预处理器cl.exe[MS.Docs]: Compiler Options Listed Alphabetically)完成,它也是编译器(检查下一阶段);默认情况下这是一个静默阶段(您可以通过指定 /E/EP/P 标志来查看其输出)
    • 对于每个来源(.c.cpp.cxxc++、...)文件,它处理所有预处理指令[MS.Docs]: Preprocessor Directives)(例如:#define#if#include em>, ...);结果仍然是 .c (.cpp, ...) 文件(与原始文件不同,通常要大得多),也称为 compilation翻译单位
    • 当遇到 #include 指令时,包含该指令的行(每行代码只包含一个文件)简单地替换为 (.h 甚至 .c (.cpp, ...)) 文件。请注意,这是递归完成的(如果包含的文件本身包含#include指令,它们也会被扩展,等等)。原始源文件比预处理后的要小很多,这也是预处理器存在的原因之一
  2. 编译

    • 编译器完成(检查前一阶段)
    • 上一阶段生成的每个翻译单元都从C (C++) 代码(人类可读)转换为机器代码( CPU "可读") 或 COFF 格式 ([MS.Docs]: PE Format)。这是 object (.obj) 文件(其内容是乱码 - 至少在 1st 视线中),可以在 VC 项目的中间目录
    • 请注意,对于项目中包含的每个源文件,在此阶段之后都会有一个对应的.obj文件
  3. 链接

    • 链接器完成(link.exe[MS.Docs]: Linker Options
    • 上一阶段的所有object文件都被合并在一起了(还有一堆.lib文件,其内容类似于.obj文件的,但这些只能间接使用 - 在构建 应用程序 时)加上一些额外的操作(例如添加 PE 部分和标题,重新定位一些代码,删除未使用的代码,...)项目的最终工件(exedll

注意:这是 Win 特定的,对于 Nix,阶段(几乎)相同,工具不同。

你的代码会发生什么:

  • 文件tactor.h(我假设这是它的名字,基于开头的包含保护)包含一堆变量定义;我以HINSTANCE tactorhandle 为例
  • librarytest.cGesture_Elicitor.c 文件(从链接器错误中获取名称)均 #include(直接或间接)tractor.h
  • 第 1 阶段tractor.h 将在两个 .c 文件中展开(独立)。因此,两个翻译单元都会有tactorhandle变量
  • 第 2 阶段,上一步中的 2 个翻译单元被编译并转换为目标文件,因为它们的代码在语法上是正确的
  • 第 3 阶段,当组合 2 个 目标文件 时,链接器 发现 tactorhandle 存在于他们两个,然后吐出上面的错误

注意事项

【讨论】:

  • 感谢您提供如此周到、内容丰富的回复。这对更好地理解我的错误的上下文很有帮助。
  • 最重要的是:它帮助您解决问题了吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多