2

我们的 C++ 项目(仍然)使用Treat wchar_t as Built-in: No (/Zc:wchar_t-)在 VS6 上编译时的选项。

这导致wchar_t只是unsigned short( WORD) 的 typedef。

我们想改变这一点,以便将wchar_t其作为适当的内置类型处理——这将大大简化(现代)库的集成。

问题是,我们确实链接到了一些我们无法重新编译的 DLL,这些 DLL 公开了它们的字符串,unsigned short*并且它们的头文件使用wchar_t*. 当 wchar_t 是内置类型时,这将导致链接器错误,因为显然导出符号不同。

更改标头需要添加一种转换层——我当然不想将转换添加到调用这些标头中的类的所有代码中。

是否可以修复 DLL,以便它们的导出符号“假装”导出内置wchar_t而不是WORD? 毕竟这两种类型在 VC++ 中是 100% 二进制兼容的。

还有其他想法吗?

4

2 回答 2

1

很好,它们是二进制兼容的。

您可能遇到的唯一一堵墙是导出函数的名称修改。如果您无法更改的 DLL 使用其修饰的 C++ 名称导出这些函数,那么当客户端找不到导出的名称时,您将在运行时获得失败鲸鱼。当程序员不使用extern "C"或不使用 .def 文件重命名导出时,就会发生这种情况。否则使用 Dumpbin.exe /exports 很容易看到。解决这个问题很痛苦,您需要修改 DLL 标头以将 wchar_t 更改为 WORD,宏可以做到这一点,并编写采用 wchar_t 并通过强制转换调用 WORD 函数的小适配器函数。

如果这些是导出的 C++ 类,那么你有一个更大的问题。您需要为这些 DLL 创建一个新的导入库。从您从 Dumpbin.exe /exports 获得的输出开始,为您提供原始名称。并从中创建一个 .def 文件,使用其 foo = bar 选项重命名符号。使用 lib.exe /def 选项创建导入库。创建一个小测试 DLL 以确定您应该如何重命名损坏的名称。

于 2013-04-01T12:12:03.253 回答
0

感谢@HansPassant 的回答,我能够做到这一点。

完整步骤在这里供参考:

  1. 为 DLL 生成导出符号:dumpbin /EXPORTS ddao35u.dll
  2. 用 wchar_t 替换所有无符号短指针类型:这意味着将PAG(for unsigned short*) 和PBG(for unsigned short const*) 替换为 wchar_t string-manglePA_WPB_W
  3. 编写一个包含所有符号的新 def 文件,其中带有字符串的函数被替换名称。示例是:

    LIBRARY ddao35u
    EXPORTS
      ??0CdbBSTR@@QAE@PA_W@Z=??0CdbBSTR@@QAE@PAG@Z  @1   ... replaced
                      ^^^^  =                ^^^
      ??0CdbBookmark@@QAE@ABV0@@Z  @2  ... no strings, no replacement needed
    ...
    
  4. 从 def 文件生成导入库:lib /DEF:ddao35u.def
  5. 从您那里引用这个新ddao35u.lib的 wchar_t-native VS 项目链接器选项,而不是原始的 lib 文件。

我将在这里转储我用来执行此操作的整个 perl 脚本函数:

sub fixModuleLib {
  my $basename = shift;
  my $dllpath = shift;
  # my $basename = "ddao35u";
  print "\n\n";
  my $cmdExport = "dumpbin /EXPORTS $dllpath";
  print "Running >> $cmdExport << for export symbols ...\n";
  die "Input file $dllpath not found!" unless -f "$dllpath"; 
  my @exports  = qx/$cmdExport/;

  print "Open $basename.def for writing ...\n";
  open(my $defh, ">", "$basename.def") or die "Unable to open basename.def for writing: $!";
  print $defh "LIBRARY $basename"."\n";
  print $defh "EXPORTS"."\n";

  my @expEntries;
  my $usCount = 0;
  foreach (@exports) {
    #           1    0 00002050 ??0CdbBSTR@@QAE@PAG@Z
    my $ws = '\s+';
    my $hnum = '[ABCDEFabcfed0123456789]+';
    if (m/$ws(\d+)$ws$hnum$ws$hnum$ws(\?\S+)/) {
      my $ord = $1;
      my $mangle = $2;
      my $defline;
      # PAG = unsigned short*
      # PBG = unsigned short const*
      # at least two @ signs before parameter list (at least in our case)
      if ($mangle =~ m/(.+@.*@.*)(P[AB]G)(.*)/) { # case sensitive
        my $pre = $1;
        my $uptr = $2;
        my $post = $3;
        # print "$ord\t$pre>>>$uptr<<<$post\n";
        my $wptr = $uptr;
        $wptr =~ s/G/_W/;
        my $wchartMangle = "$pre$wptr$post";
        $defline = "  $wchartMangle=$mangle  \@$ord";
        print "CHANGED: $defline\n";
        $usCount += 1;                 
      } else {
        # line doesn't contain unsigned short ptr
        $defline = "  $mangle  \@$ord";
      }
      push @expEntries, $defline;
    }
  }
  print "There are ".scalar(@expEntries)." export lines and $usCount of them contain an unsigned-short-pointer.\n";
  die "No entries found!" unless $usCount>0;
  print "Writing entries to $basename.def ...\n";
  foreach my $entry (@expEntries) {
    print $defh $entry."\n"; 
  }
  close($defh);

  my $cmdLib = "lib /DEF:$basename.def";
  print "Generate Export Lib from DEF-File: >> $cmdLib << ...\n";
  system($cmdLib); 
}
于 2013-04-08T10:08:34.747 回答