【问题标题】:Visual C++ how to export x64 DLL functions for DelphiVisual C++ 如何为 Delphi 导出 x64 DLL 函数
【发布时间】:2012-06-25 12:13:15
【问题描述】:

我主要是 Delphi 开发人员,我一直在使用 DLL 包装器,它与 Logitech 的 G15 等 API DLL 接口以与键盘 LCD 屏幕交互。

几年前,有人将这个包装器用于delphi,基本上是一个与Logitech lib 接口并将函数导出到Delphi 的DLL。它一直运行良好,但它只是 32 位的。

我得到了包装源并尝试将其编译为 64 位,但出了点问题。我将平台设置为 64 位,相应地更改了配置等(我认为),并将库路径指向 Logitech 64 位 .lib

导出似乎在那里,但是当我尝试使用 GetProcAddress 加载任何导出时,我收到错误“找不到指定的模块”

怎么了?

// lgLCDWrapper.DLL
//
// A Wrapper-DLL for Delphi and other compilers who cannot include the original
// lcLcd.Lib provided by Logitech. These functions are needed to communicate with
// the graphical LCD of an Logitech G15-Keyboard.
//
// Code by Olaf Stieleke, May 1st, 2007
// Compiler Visual C++ 2005 Express Edition
// Hereby dedicated to Open Source under GPL-License.
//

//Make sure to set additional Include-Path to lglcd.h (found in Logitech-SDK-Folders)
//Make sure to set additional Library-Path to lglcd.lib (found in Logitech-SDK-Folders)

#include "stdafx.h"
#include <lglcd.h>
#include "lgLcdWrapper.h"


BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call,LPVOID lpReserved)
{
    return TRUE;
}

DWORD _cdecl lgLcdInitWrap(void)
{
    return lgLcdInit();
}

DWORD _cdecl lgLcdDeInitWrap(void)
{
    return lgLcdDeInit();
}
DWORD _cdecl lgLcdConnectAWrap(lgLcdConnectContextA *ctx)
{
    return lgLcdConnectA(ctx);
}
DWORD _cdecl lgLcdDisconnectWrap(int connection)
{
    return lgLcdDisconnect(connection);
}
DWORD _cdecl lgLcdEnumerateWrap(int connection, int index, lgLcdDeviceDesc *description)
{
    return lgLcdEnumerate(connection,index,description);
}
DWORD _cdecl lgLcdOpenWrap(lgLcdOpenContext *ctx)
{
    return lgLcdOpen(ctx);
}
DWORD _cdecl lgLcdCloseWrap(int device)
{
    return lgLcdClose(device);
}
DWORD _cdecl lgLcdReadSoftButtonsWrap(int device, DWORD *buttons)
{
    return lgLcdReadSoftButtons(device,buttons);
}
DWORD _cdecl lgLcdUpdateBitmapWrap(int device, lgLcdBitmapHeader *bitmap, DWORD priority)
{
    return lgLcdUpdateBitmap(device,bitmap,priority);
}
DWORD _cdecl lgLcdSetAsLCDForegroundAppWrap(int device, int foregroundYesNoFlag)
{
    return lgLcdSetAsLCDForegroundApp(device,foregroundYesNoFlag);
}

包含文件

extern "C" __declspec(dllexport) DWORD  _cdecl lgLcdInitWrap(void);
extern "C" __declspec(dllexport) DWORD  _cdecl lgLcdDeInitWrap(void);
extern "C" __declspec(dllexport) DWORD  _cdecl lgLcdConnectAWrap(lgLcdConnectContextA *ctx);
extern "C" __declspec(dllexport) DWORD  _cdecl lgLcdDisconnectWrap(int connection);
extern "C" __declspec(dllexport) DWORD  _cdecl lgLcdEnumerateWrap(int connection, int index, lgLcdDeviceDesc *description);
extern "C" __declspec(dllexport) DWORD  _cdecl lgLcdOpenWrap(lgLcdOpenContext *ctx);
extern "C" __declspec(dllexport) DWORD  _cdecl lgLcdCloseWrap(int device);
extern "C" __declspec(dllexport) DWORD  _cdecl lgLcdReadSoftButtonsWrap(int device, DWORD *buttons);
extern "C" __declspec(dllexport) DWORD  _cdecl lgLcdUpdateBitmapWrap(int device, lgLcdBitmapHeader *bitmap, DWORD priority);
extern "C" __declspec(dllexport) DWORD  _cdecl lgLcdSetAsLCDForegroundAppWrap(int device, int foregroundYesNoFlag);

stdafx.cpp

    #include "stdafx.h"
    // stdafx.h : Includedatei für Standardsystem-Includedateien
// oder häufig verwendete projektspezifische Includedateien,
// die nur in unregelmäßigen Abständen geändert werden.
//

#pragma once

// Ändern Sie folgende Definitionen für Plattformen, die älter als die unten angegebenen sind.
// In MSDN finden Sie die neuesten Informationen über die entsprechenden Werte für die unterschiedlichen Plattformen.
#ifndef WINVER              // Lassen Sie die Verwendung spezifischer Features von Windows XP oder später zu.
#define WINVER 0x0501       // Ändern Sie dies in den geeigneten Wert für andere Versionen von Windows.
#endif

#ifndef _WIN32_WINNT        // Lassen Sie die Verwendung spezifischer Features von Windows XP oder später zu.                   
#define _WIN32_WINNT 0x0501 // Ändern Sie dies in den geeigneten Wert für andere Versionen von Windows.
#endif                      

#ifndef _WIN32_WINDOWS      // Lassen Sie die Verwendung spezifischer Features von Windows 98 oder später zu.
#define _WIN32_WINDOWS 0x0410 // Ändern Sie dies in den geeigneten Wert für Windows Me oder höher.
#endif

#ifndef _WIN32_IE           // Lassen Sie die Verwendung spezifischer Features von IE 6.0 oder später zu.
#define _WIN32_IE 0x0600    // Ändern Sie dies in den geeigneten Wert für andere Versionen von IE.
#endif

#define WIN32_LEAN_AND_MEAN     // Selten verwendete Teile der Windows-Header nicht einbinden.
// Windows-Headerdateien:
#include <windows.h>

德尔福代码:

DLLHandle := LoadLibrary('lgLcdWrapperX64.dll');
  if DLLHandle = 0 then Exit(False);

  @lgLcdInit       := GetProcAddress(dllHandle, 'lgLcdInitWrap');
  if (not Assigned(lgLcdInit)) then ShowMessage(SysErrorMessage(GetLastError));

【问题讨论】:

  • 我相信你也应该把extern "C" __declspec(dllexport)放在定义之前。
  • 问题已编辑;导出似乎在那里,但 GetProcAddress 无法加载任何内容(在 32 位下工作正常)
  • 32 位和 64 位进程使用不同的搜索路径和(某些)注册表项。例如,64 位进程使用 system32,而 32 位进程使用 SysWOW64(在 64 位操作系统上)。 32 位进程无法加载 64 位 DLL,反之亦然。这可能是您的问题的根源 - 尝试使用 ProcMon 找出搜索 DLL 的位置。大多数可能是它没有在您期望的地方搜索,或者您混合了 32 位和 64 位可执行文件。
  • 不,应用程序是 64 位的,我将 DLL 放在同一个文件夹中。 LoadLibrary 工作正常,只是 GetProcAddress 返回“找不到指定的模块”,但根据 Dependancy Walker,导出正确
  • 您确定这是您从 GetProcAddress 得到的错误,并且 LoadLibrary 确实成功了吗? GetProcAddress 查找过程,而不是模块。如果它确实是一个未找到的过程,则导出的函数可能会被破坏。是否在 32 位版本中使用了 .def 文件?

标签: c++ visual-studio dll 64-bit


【解决方案1】:

我收到错误“找不到指定的模块

这不是 GetProcAddress 产生的错误,您可以从 LoadLibrary() 中获得该错误。错误代码 126。GetProcAddress 失败并出现错误 127,“找不到指定的过程”。所以检查你的错误处理代码,它可能有一个错误。并三重检查您是否为 DLL 使用了正确的路径名,以及依赖的 DLL 是否已正确部署,例如 CRT dll。

唯一的其他相关花絮是应用于 64 位 DLL 中导出函数的名称修饰。您的客户端代码之前肯定使用过类似“_lgLcdInitWrap”的名称,前导下划线表示 __cdecl 调用约定有效。幸运的是,64 位代码与调用约定无关,只有一个。因此,出口不再被装饰,它只是简单的“lgLcdInitWrap”。使用 dumpbin.exe /exports 或 depends.exe 之类的工具,您肯定会看到。

【讨论】:

  • Erm 不,错误出现在 GetProcAddress 之后:(在最后的主要问题中添加代码)第一步顺利通过,我得到了 DLL 的句柄,然后 GetProceAddress 引发了错误。
  • 我回答了一个 MSVC C++ 问题,无法帮助您解决 Delphi 问题。显然有问题,GetProcAddress 无法生成关于模块的错误,它已经有了模块句柄。你通过了。
  • 一直在使用 Delphi 制作的 64 位 DLL 进行测试,相同的代码在那里工作正常,只是不适用于 C++ DLL。我知道如果 LoadLibrary 失败,我会得到错误代码,但该调用实际上通过了,我得到一个 DLL 句柄并且没有错误,我不知道为什么 GetProcAddress 在这种情况下返回 Module 而不是 Procedure。
【解决方案2】:

发现问题。当我在 VS2010 中添加 x64 平台时,它设置了 2 个导致整个问题的设置:

项目选项 > 链接器 > 高级 > - 随机基地址 - 数据执行保护 (DEP)

禁用这 2 个设置可以修复它。

【讨论】:

  • 禁用这两个选项是一个主要的安全方面的禁忌。
猜你喜欢
  • 2015-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多