简体   繁体   English

如何使用函数的参数作为 C++ 中的指针从 DLL 返回值?

[英]How to return value from DLL using parameter of function as a pointer in C++?

I have a simple DLL:我有一个简单的 DLL:

dllmain.cpp: dllmain.cpp:

#define MYDLLDIR        
#include "pch.h"

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    ...
}


void callByPtr(int *i) {
    
    (*i)++;
}

pch.h pch.h

#include "framework.h"

#ifdef MYDLLDIR
#define DLLDIR __declspec(dllexport) __stdcall
#else
#define DLLDIR __declspec(dllimport)
#endif

extern "C" {

    DLLDIR void callByPtr(int *i);
        
};

Client:客户:

typedef void(__stdcall* callByPtr)(int*);

int main()
{
    HINSTANCE hDLL;

    hDLL = LoadLibrary(_T("MyDll.dll"));

    if (NULL != hDLL)
    {

        callByPtr myCall = (callByPtr)GetProcAddress(hDLL, "callByPtr");

        if (!myCall) {
            return EXIT_FAILURE;
        }

        int i = 10;

        int* ptri = &i;

        std::cout << "i " << i << std::endl;
        std::cout << "ptri " << ptri << std::endl;

        myCall(ptri);

        std::cout << "---- After Call ----\n";

        std::cout << "i " << i << std::endl;
        std::cout << "ptri " << ptri << std::endl;    

    }
}

Result:结果:

---- Before Call ---- ---- 致电前----

i = 10我 = 10

ptri = 0025FB40 ptri = 0025FB40

---- After Call ---- ---- 来电后----

i = 11286192我 = 11286192

ptri = 0025FB3C ptri = 0025FB3C

The adress of ptri has changed and value was not 11. ptri 的地址已更改,值不是 11。

How to implement this properly that I can get a value from DLL using method above?如何正确实现这一点,以便我可以使用上述方法从 DLL 中获取值?

Thank you!谢谢!

Your exporting definitions are also not correct.您的导出定义也不正确。 Should be something like:应该是这样的:

#ifdef MYDLL_EXPORT
#define MYDLLDIR  __declspec(dllexport)
#else
#define MYDLLDIR __declspec(dllimport)
#endif

and use the same macro (MYDLLDIR) for both export (dll, #MYDLL_EXPORT defined) and import(clients, #MYDLL_EXPORT NOT defined)并为导出(dll,#MYDLL_EXPORT 定义)和导入(客户端,#MYDLL_EXPORT 未定义)使用相同的宏(MYDLLDIR)

You have to use the same calling convention for callByPtr in all places, in your case __stdcall (the default one is __cstdcall).您必须在所有地方对 callByPtr 使用相同的调用约定,在您的情况下为 __stdcall(默认为 __cstdcall)。

In your pch.h then:在你的 pch.h 然后:

MYDLLDIR void __stdcall callByPtr(int *i);

since you return void in your exported function DLLDIR void callByPtr(int *i);因为您在导出的函数 DLLDIR void callByPtr(int *i); 中返回 void; you should use the default calling convention for C and C++ programs __cdecl.您应该使用 C 和 C++ 程序 __cdecl 的默认调用约定。
After changing:更改后:

  1. in your pch.h file:在您的 pch.h 文件中:
    #define DLLDIR __declspec(dllexport) __stdcall #define DLLDIR __declspec(dllexport) __stdcall
    to
    #define DLLDIR __declspec(dllexport) #define DLLDIR __declspec(dllexport)

  2. in your Client file:在您的客户文件中:
    typedef void(__stdcall* callByPtr)(int*); typedef void(__stdcall* callByPtr)(int*);
    to
    typedef void(__cdecl* callByPtr)(int*); typedef void(__cdecl* callByPtr)(int*);

The rebuild is without errors and warnings and the output is like that:重建没有错误和警告,输出如下:

i 10我 10
ptri 0113FCA4 ptri 0113FCA4
---- After Call ---- ---- 来电后----
i 11我 11
ptri 0113FCA4 ptri 0113FCA4

According to [MS.Docs]: __stdcall根据[MS.Docs]:__stdcall

Syntax句法

return-type __stdcall function-name [( argument-list )]返回类型__stdcall函数名[(参数列表)]

the calling convention specifier comes after the function return type.在调用约定符自带函数的返回类型之后 The way you defined it is before , so (probably) the compiler ignored it ???你定义它的方式是之前,所以(可能)编译器忽略了它??? , ending up in the .dll exporting the function as __cdecl (default), and when the .exe called it as __stdcall , Bang! ,最终在.dll中将函数导出为__cdecl (默认),当.exe将其称为__stdcall 时砰! -> Stack Corruption , and what you think is your pointer is actually something completely different, hence your weird outputs. -> Stack Corruption ,你认为你的指针实际上是完全不同的东西,因此你的输出很奇怪。
The thing that is interesting is that on my end, the compiler ( VS2017 ) spits error C2062: type 'void' unexpected when I try to build the .dll using your form ( #define DLL00_EXPORT_API __declspec(dllexport) __stdcall ).有趣的是,在我的最后,编译器( VS2017 )吐出error C2062: type 'void' unexpected当我尝试使用您的表单#define DLL00_EXPORT_API __declspec(dllexport) __stdcall构建.dll时, error C2062: type 'void' unexpected

Below is a working example (I modified the file names and contents).下面是一个工作示例(我修改了文件名和内容)。

dll00.h : dll00.h :

#pragma once

#if defined(_WIN32)
#  if defined(DLL00_EXPORTS)
#    define DLL00_EXPORT_API __declspec(dllexport)
#  else
#    define DLL00_EXPORT_API __declspec(dllimport)
#  endif
#else
#  define DLL00_EXPORT_API
#endif

#if defined(CALL_CONV_STDCALL)
#  define CALL_CONV __stdcall
#else
#  define CALL_CONV
#endif

#if defined(__cplusplus)
extern "C" {
#endif

DLL00_EXPORT_API void CALL_CONV callByPtr(int *pI);

#if defined(__cplusplus)
}
#endif

dll00.cpp : dll00.cpp :

#define DLL00_EXPORTS
#include "dll00.h"


void CALL_CONV callByPtr(int *pI) {
    if (pI) {
        (*pI)++;
    }
}

main00.cpp : main00.cpp :

#include <iostream>
#include <Windows.h>
#include "dll00.h"

#if defined(CALL_CONV_STDCALL)
#  define FUNC_NAME "_callByPtr@4"
#else
#  define FUNC_NAME "callByPtr"
#endif

using std::cout;
using std::endl;


typedef void(CALL_CONV *CallByPtrFunc)(int*);


int main() {
    HMODULE hDLL;

    hDLL = LoadLibrary("dll00.dll");

    if (!hDLL) {
        std::cout << "LoadLibrary failed" << std::endl;
        return -1;
    }

    CallByPtrFunc callByPtr = (CallByPtrFunc)GetProcAddress(hDLL, FUNC_NAME);

    if (!callByPtr) {
        std::cout << "GetProcAddress failed" << std::endl;
        CloseHandle(hDLL);
        return EXIT_FAILURE;
    }

    int i = 10;

    int *ptri = &i;

    std::cout << "i " << i << std::endl;
    std::cout << "ptri " << ptri << std::endl;

    callByPtr(ptri);

    std::cout << "---- After Call ----\n";

    std::cout << "i " << i << std::endl;
    std::cout << "ptri " << ptri << std::endl;

    CloseHandle(hDLL);

    return 0;
}

Output :输出

 [cfati@CFATI-5510-0:e:\\Work\\Dev\\StackOverflow\\q063951075]> sopr.bat *** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages *** [prompt]> "c:\\Install\\pc032\\Microsoft\\VisualStudioCommunity\\2017\\VC\\Auxiliary\\Build\\vcvarsall.bat" x86 ********************************************************************** ** Visual Studio 2017 Developer Command Prompt v15.9.27 ** Copyright (c) 2017 Microsoft Corporation ********************************************************************** [vcvarsall.bat] Environment initialized for: 'x86' [prompt]> [prompt]> dir /b dll00.cpp dll00.h main00.cpp [prompt]> :: Build the .dll (passing /DCALL_CONV_STDCALL) [prompt]> cl /nologo /MD /DDLL /DCALL_CONV_STDCALL dll00.cpp /link /NOLOGO /DLL /OUT:dll00.dll dll00.cpp Creating library dll00.lib and object dll00.exp [prompt]> [prompt]> :: Build the .exe (also passing /DCALL_CONV_STDCALL) [prompt]> cl /nologo /MD /W0 /EHsc /DCALL_CONV_STDCALL main00.cpp /link /NOLOGO /OUT:main00.exe main00.cpp [prompt]> [prompt]> dir /b dll00.cpp dll00.dll dll00.exp dll00.h dll00.lib dll00.obj main00.cpp main00.exe main00.obj [prompt]> [prompt]> main00.exe i 10 ptri 00F5FCC8 ---- After Call ---- i 11 ptri 00F5FCC8 [prompt]> :: It worked !!! [prompt]> [prompt]> dumpbin /EXPORTS dll00.dll Microsoft (R) COFF/PE Dumper Version 14.16.27043.0 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file dll00.dll File Type: DLL Section contains the following exports for dll00.dll 00000000 characteristics FFFFFFFF time date stamp 0.00 version 1 ordinal base 1 number of functions 1 number of names ordinal hint RVA name 1 0 00001000 _callByPtr@4 Summary 1000 .data 1000 .rdata 1000 .reloc 1000 .text

Notes :注意事项

  • Obviously, the code presented is not the one generating your output :显然,提供的代码不是生成输出的代码

    • It doesn't compile:它不编译:
      • The error I mentioned at the beginning (although that could probably be avoided using some older compiler (or (doubtfully?) a compiler flag))我在开头提到的错误(尽管使用一些较旧的编译器(或(怀疑?)编译器标志)可能可以避免这种情况)
      • Missing #include s and using s in your .exe code缺少#include s 并在.exe代码中使用s
  • "Minor" code problems: “次要”代码问题:

    • NULL pointer test before dereferencing取消引用前的指针测试
    • CloseHandle关闭句柄
    • Differences between DLLDIR definition (as also mentioned by @Petr_Dokoupil): __declspec(dllexport) __stdcall vs. __declspec(dllimport) , but since you're dynamically loading the .dll instead of linking to it, it has nothing to do with the error DLLDIR定义之间的差异@Petr_Dokoupil也提到): __declspec(dllexport) __stdcall__declspec(dllimport) ,但由于您是动态加载.dll而不是链接到它,它与错误无关
  • Why the need to use __stdcall ?为什么需要使用__stdcall (answer provided in comment: " to be compatible with other languages "): (答案在评论中提供:“与其他语言兼容”):

    • It only matters on 32bit (on 64bit it's ignored)仅在32 位上很重要(在64 位上它被忽略)

    • It introduces a lot of additional issues (some of them you didn't even experienced), like the function name mangling (check dumpbin output), which can only be avoided using a .def file它引入了很多额外的问题(其中一些你甚至没有经历过),比如函数名称修改(检查转储箱输出),只能使用.def文件来避免

    • All in all, this seems like an XY Problem .总而言之,这似乎是一个XY 问题 You should use defaults (get rid of __stdcall completely):您应该使用默认值(完全摆脱__stdcall ):

      • With this version of the code, simply don't pass /DCALL_CONV_STDCALL argument to the compiler when building the .dll and .exe使用此版本的代码,在构建.dll.exe时,不要将/DCALL_CONV_STDCALL参数传递给编译器
      • You're less likely to run into (subtle) problems (at least until you get more experience in this area)你不太可能遇到(微妙的)问题(至少在你在这方面获得更多经验之前)
      • Removing all the calling convention related code, would make it much shorter and cleaner删除所有与调用约定相关的代码,将使它更短更干净
    • All the listed languages ( Delphi , Python , C# , ...) have support for __cdecl (after all, I think that most of machine code running out there, is still written in C )所有列出的语言( DelphiPythonC# ,...)都支持__cdecl (毕竟,我认为那里运行的大多数机器代码仍然是用C编写的)

For more details regarding this whole area, you could check (including (recursively) referenced URL s):有关整个区域的更多详细信息,您可以检查(包括(递归)引用的URL s):

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM