CLR Windows窗体中的LNK2005错误

时间:2016-01-12 16:30:47

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

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

当我在代码的控制台部分中包含标题时,我的两个标题一起播放得很好,但是当我尝试将它们包含在表单中时,它们会产生以下结果:

  

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:已在Gesture_Elicitor.obj中定义_DiscoverDevices

     

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

那么这个标题对于我的表单来说不是很好吗?

1 个答案:

答案 0 :(得分: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[MSDN]: Using extern to Specify Linkage)。

<强> Backgorund

C 代码构建到可移植可执行代码中有三个阶段(这里我指的是 .exe 。 dll 文件)。有关详情,请查看[MSDN]: Peering Inside the PE: A Tour of the Win32 Portable Executable File Format

  1. <强>预处理

    • 预处理器 cl.exe [MSDN]: Compiler Options Listed Alphabetically)完成,它也是编译器(检查下一阶段);这默认为静默阶段(您可以通过指定/E/EP/P标志来查看其输出)
    • 对于每个源( .c )文件,它处理所有预处理指令[MSDN]: Preprocessor Directives)(例如:#define,{{ 1}},#if,...);结果仍然是 .c 文件(不同于原始文件,通常是非常大),也称为编译翻译单元
    • 当遇到#include指令时,包含指令的行(每行代码只包含一个文件)只是被( .h 或甚至包括 .c )文件。请注意,这是递归(如果包含的文件本身包含#include指令,它们也会被扩展,依此类推)。原始源文件比预处理源文件小得多,这是预处理器存在的原因之一
  2. <强>编译

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

    • 链接器完成 link.exe [MSDN]: Linker Options
    • 上一阶段的所有目标文件被合并在一起(带有一堆 .lib 文件,其内容类似于 .obj 文件,但那些只能间接使用 - 在构建应用程序时)加上一些额外的操作(例如添加PE部分和标题,重新定位一些代码,删除未使用的代码,......)到项目的最终工件( exe dll
  4. 注意:这是 Win 特定的,对于 Ux ,阶段(几乎)相同,工具不同。

    您的代码中会发生什么:

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

    备注