MinGW .dll .lib 库的编译与实现

本文详细介绍如何使用MinGW编译DLL,包括符号表、头文件及库函数的创建过程,同时介绍了两种链接方式:显式链接和隐式链接。
Mingw32  dll库到编译,导出导入

这里有 hello.def(符号表) hello.h  hello.c(编译成库) main.c(主程序调用库)

   符号表 hello.def

  LIBRARY libhello                                    //要加载到dll名
  DESCRIPTION "DLL is used by hello"                  //可加可不加,主要是描诉dll的作用
  EXPORTS                                             //导入函数
          PrintHello@0                                //@0 @4  有问题???  看下面的编译def    
          GetString@4


   头文件 hello.h

#ifndef _HELLO_H_
#define _HELLO_H_
#include <stdio.h>
#include <windows.h>

#if defined DLL_EXPORT                                     //宏定义
#define DECLDIR __declspec(dllexport)
#else
#define DECLDIR __declspec(dllimport)
#endif

//DECLDIR void __stdcall PrintHello(void);                    //导出PringHello(),GetString()函数     __stdcall 关键字 根据参数个数从右到左依次入栈
//DECLDIR void* __stdcall GetString(const char *src);
DECLDIR void PrintHello(void);                    
DECLDIR void* GetString(const char *src);
**********************************************************************
http://hi.baidu.com/luosiyong/item/88a97b0e3bd0ee8802ce1b0b

在开发dll的时候,一般不让编译器改变函数名,所以通常是以C方式编译,即加入了extern "C"说明。但是看上面的组合测试结果,__stdcall和__fastcall编译出来的函数名还是和原始的函数名不同。就拿__stdcall来说,它以C编译导出的时候,会在函数前面加入下划线,并在函数后面加入@和参数总大小的字节数。
注意:他需要写的名称是 "_add@8",而不是简单的"add",否则就会出现函数未定义的链接错误。
**********************************************************************

#endif

   库函数hello.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#define NUMBER 100

#define DLL_EXPORT                            //注:宏定义后  代表头文件到函数是导出的
#include "hello.h"

DECLDIR
//void __stdcall PrintHello(void)
void PrintHello(void)
{
  char ptr[] = "Hello world";
  printf("%s\n",ptr);

  return;
}

DECLDIR
//void* __stdcall GetString(const char *src)
void* GetString(const char *src)
{
 char *dest = (char*)malloc(NUMBER);
 memset(dest,0,NUMBER);
// bzero(dest,NUMBER);
 char *temp = dest;

 while(*src != '\0')
    *dest++ = *src++;

 printf("%s\n",temp);

 free(temp);

 return;
}

    主程序main.c
**************************************************
隐式链接
链接到你.lib
文件并将.dll文件置入你的新项目的路径中去

显示链接
难点的加载DLL的方法是有稍微有点复杂的。你将需要函数指针和一些Windows函数。但是,通过
这种载入DLLs的方法,你不需要DLL的.lib或头文件
**************************************************
#include <stdio.h>
#include <windows.h>
#include "hello.h"

//#pragma comment(lib, "libhello.lib")                          //*************隐式链接?????????生成lib库
//__declspec(dllimport)
//extern void __stdcall PrintHello(void);
//__declspec(dllimport)
//extern void* __stdcall GetString(const char *src);

typedef void (*MYHELLO)(void);                          //定义函数指针
typedef void* (*MYSTRING)(const char*);

int main(int argc,char **argv)
{
  const char *string = "This is a test for dll";

#if 1 

  void *hmodule;                                                //*************显式链接 直接加载dll

  MYHELLO pHello;
  MYSTRING gString;
 // const char hdll[] = "libhello.dll";

  hmodule = LoadLibrary("libhello.dll");                        //加载dll模块,返回模块句柄hmodule
  if(hmodule != NULL)
  {
    printf("%s\n","success");
    pHello = (MYHELLO)GetProcAddress(hmodule,"PrintHello");     //“ PrintHello@0 ”  把PrintHello@0()的地址映射到pHello,同理;  注:@0 @4 这是根据.def符号表导出,这里还有问题???如何去掉@  形成标准C
    gString = (MYSTRING)GetProcAddress(hmodule,"GetString@4");  //"GetString@4";

    if(pHello != NULL && gString != NULL)
    {
      pHello();                                                   //函数到调用
      gString(string);
    }

    FreeLibrary(hmodule);                                         
  }
#endif

//  PrintHello();
//  GetString(string);

  return 0;
}

一。显式链接dll的步骤
 1.先讨论以下符号表,生成符号表有两种方法
   a.如上面:自己编写到一个,但是没有加@(这里不明白),应该可以不加的,但是若是不加,后面符号表的导出就会出问题,注意main.c里的映射函数到函数名
   b.自动导出 #i686-pc-mingw32-gcc -c hello.c
            #i686-pc-mingw32-dlltool -z hello.def --export-all-symbols hello.o
    
; /opt/mxe/usr/bin/i686-pc-mingw32-dlltool -z hello.def --export-all-symbols hello.o
EXPORTS
        GetString@4 @ 1
        PrintHello@0 @ 2

   可以看出自动导出到文件它加上来“@”,所以自己编写到@ 后面到数字是根据这里加到,若数字有误,则编译会报错
 
 2.生成dll文件,libhello.dll
   #i686-pc-mingw32-gcc -shared -g -o libhello.dll hello.o hello.def

 3.编译主程序main.c生成可执行 main.exe  (PE32)
   #i686-pc-mingw32-gcc -o main.exe main.c libhello.dll
 
 4.执行 #wine main.exe

注:extern "C"
  {
    DECLDIR void __stdcall PrintHello(void);
    DECLDIR void* __stdcall GetString(const char *src);
  }

  编译 ERROR  
  root@localhost:/home/xiaot/workspace/Test/Hello/windows# i686-pc-mingw32-gcc -c hello.c
  In file included from hello.c:8:0:
  hello.h:12:8: error: expected identifier or '(' before string constant
所以不能使用extern "C" ???????

二。隐式链接加载.lib库到步骤    main.c红色代码的实现
 1.由于__stdcall关键字到原因,导致符号表生成有“@”符号,去掉之后
   采取自动导出符号  
  #i686-pc-mingw32-dlltool -z hello.def --export-all-symbols hello.o
  “hello.def”

  ; i686-pc-mingw32-dlltool -z hello.def --export-all-symbols hello.o
  EXPORTS
        GetString @ 1
        PrintHello @ 2

 2.从动态库导出.lib文件 libhello.lib
  #i686-pc-mingw32-dlltool -k --dllname libhello.dll --output-lib libhello.lib --def hello.def

 3.编译 生成main.exe
    #i686-pc-mingw32-gcc -o main.exe main.c libhello.lib

 4.执行 #wine main.exe


参看设置选项的意思      #i686-pc-mingw32-dlltool --help


### CAA编译未生成.dll.lib文件的解决方案 在CAA开发中,编译过程中未能正确生成`.dll`和`.lib`文件通常项目配置、依赖路径以及链接器设置相关。以下是针对此问题的详细分析和解决方法: #### 1. 检查项目配置 确保项目的CMake或Makefile配置文件中正确设置了输出目标。如果使用CMake,需要确认以下内容是否正确: - `add_library`命令是否用于定义静态或动态[^1]。 - `set_target_properties`是否正确设置了的输出路径和名称。 ```cmake # 定义动态 add_library(my_caa SHARED src/main.cpp) # 设置输出路径 set_target_properties(my_caa PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set_target_properties(my_caa PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) ``` #### 2. 验证依赖路径 CAA项目通常依赖于CATIA提供的基础。如果这些的路径未正确配置,可能导致链接失败或无法生成目标文件。验证以下内容: - 确保`Additional Library Directories`包含所有必要的路径。 - 在Visual Studio中,检查`Linker -> Input -> Additional Dependencies`是否包含所有依赖的`.lib`文件[^2]。 #### 3. 检查编译器和链接器选项 CAA项目可能需要特定的编译器和链接器选项以确保正确生成`.dll`和`.lib`文件。以下是一些关键设置: - 确保`/MD`或`/MDd`选项被启用,以便使用多线程运行时[^1]。 - 如果使用GCC或MinGW,确保`-shared`选项被传递给链接器以生成动态。 ```makefile # 使用GCC生成动态 g++ -shared -o libcaa.dll src/main.o -L/path/to/libs -lcatia_base ``` #### 4. 分析Make错误日志 如果`make`命令执行失败,检查错误日志以定位具体问题。常见的错误包括: - 缺少头文件或文件。 - 链接器找不到符号定义。 通过以下命令查看详细的编译和链接日志: ```bash make VERBOSE=1 ``` #### 5. 验证目标平台架构 确保编译的目标平台架构CATIA运行环境一致。例如,如果CATIA运行在64位Windows上,则需要编译64位版本的DLLLIB文件[^2]。 ```python import platform print(platform.architecture()) # 检查当前Python环境的架构 ``` #### 6. 调试动态链接生成问题 如果动态链接生成失败,可以尝试以下方法: - 使用工具如Dependency Walker分析生成的DLL文件,检查是否存在未解析的依赖项。 - 确保CATIA运行时环境中存在所有必要的运行,例如`msvcp71.dll`。 --- ### 示例代码:修复Makefile中的链接器问题 以下是一个示例Makefile,展示了如何正确配置链接器以生成动态和静态: ```makefile # 定义编译器和链接器 CC = g++ AR = ar # 定义源文件和输出文件 SRCS = src/main.cpp src/utils.cpp OBJS = $(SRCS:.cpp=.o) DLL = libcaa.dll LIB = libcaa.a # 编译规则 $(DLL): $(OBJS) $(CC) -shared -o $@ $^ -L/path/to/libs -lcatia_base $(LIB): $(OBJS) $(AR) rcs $@ $^ %.o: %.cpp $(CC) -c $< -o $@ -I/path/to/headers ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值