如何调用外部DLL中未在头文件声明的结构体函数?
我在Visual Studio 2022的解决方案里实现一个独立组件,需要访问外部DLL的内容、修改变量并调用其他组件的函数。由于部分函数/类没在原头文件声明,我自己写了“伪头文件”。用这个伪头文件能成功编译,也能访问修改原实例的变量,但调用结构体内部函数时会触发LNK2019链接错误。
公共代码部分
CommonClass和CommonContainer是解决方案共享类,CommonContainer通过GetReference()返回内部引用。
Common.h(系统包装类)
#pragma once class CommonClass { }; template<class T> class CommonContainer { private: T* reference; public: CommonContainer(T* newReference) : reference(newReference) { } T* GetReference() const { return reference; } CommonContainer& operator=(const CommonContainer& other) { reference = other.GetReference(); return *this; } template<class OtherClass> CommonContainer& operator=(const CommonContainer<OtherClass>& other) { reference = other.GetReference(); return *this; } CommonContainer& operator=(T* newReference) { reference = newReference; return *this; } };
SharedComponent.h(跨组件共享存储类)
#pragma once #include "Common.h" #include <map> #include <string> template<class T> class SharedComponent { private: std::map<std::string, T*> componentsMap = {}; public: T* Get(std::string name) { return componentsMap[name]; } T* Set(std::string name, T* component) { componentsMap[name] = component; return component; } };
外部DLL部分
Handler.h(独立DLL中的Handler组件)
#pragma once namespace outside { class HandlerComponent : public CommonClass { public: HandlerComponent(); private: void GetData(std::vector<std::string>* data); void Initialize(SharedComponent<CommonClass>* shared); CommonContainer<CommonClass> implementation; }; }
Handler.cpp(实现代码)
#include "Common.h" #include "SharedComponent.h" #include <vector> #include <string> #include "Handler.h" namespace outside { struct HandlerComponentImplementation : CommonClass { void Initialize(); std::vector<std::string> GetJsonData(); struct InternalData { std::vector<std::string> requests; std::vector<std::string> responses; int counter; InternalData() : counter(0) { Update(); } void Update() { // Update internal data } }; InternalData internal; }; void HandlerComponentImplementation::Initialize() { // Some init code internal = InternalData(); } std::vector<std::string> HandlerComponentImplementation::GetJsonData() { internal.Update(); return internal.responses; } void HandlerComponent::GetData(std::vector<std::string>* data) { auto componentImplementation = static_cast<HandlerComponentImplementation*>(implementation.GetReference()); *data = componentImplementation->GetJsonData(); } void HandlerComponent::Initialize(SharedComponent<CommonClass>* shared) { CommonContainer<HandlerComponentImplementation> componentImplementation = new HandlerComponentImplementation(); implementation = componentImplementation; shared->Set("Handler", this); } }
我的组件部分
HandlerFake.h(伪头文件,将Handler类成员设为公有)
#pragma once #include "Common.h" #include "SharedComponent.h" #include <vector> #include <string> namespace outside { struct HandlerComponentImplementation : CommonClass { public: std::vector<std::string> GetJsonData(); struct InternalData { public: std::vector<std::string> requests; std::vector<std::string> responses; int counter; InternalData(); void Update(); }; InternalData* internal; }; class HandlerComponent : public CommonClass { public: HandlerComponent(); void GetData(std::vector<std::string>* data); void Initialize(); CommonContainer<CommonClass> implementation; }; // Component to manipulate Handler instance class ManipulatorComponent : public CommonClass { void Initialize(SharedComponent<CommonClass>* shared); }; }
HandlerFake.cpp(获取Handler实例并操作的代码)
#include "Common.h" #include "SharedComponent.h" #include <vector> #include <string> #include "HandlerFake.h" namespace outside { void ManipulatorComponent::Initialize(SharedComponent<CommonClass>* shared) { auto handler = shared->Get("Handler"); auto handlerComponent = (HandlerComponent*) handler; // Can access whatever from handlerComponent auto implementation = handlerComponent->implementation; auto handlerComponentImplementation = static_cast<HandlerComponentImplementation*>(implementation.GetReference()); auto handlerComponentImplementationInternal = handlerComponentImplementation->internal; // Get error if use this (LNK2019) handlerComponentImplementationInternal->Update(); } }
核心原因
LNK2019错误是因为链接器找不到InternalData::Update()的符号定义。外部DLL里的InternalData是嵌套在HandlerComponentImplementation内部的私有结构,它的成员函数Update()只在DLL内部实现,没有被导出到外部可见的符号表中。你写的伪头文件虽然声明了这个函数,但链接器无法在DLL的导出符号里找到对应的实现。
另外,伪头文件里把HandlerComponentImplementation::internal声明成了指针InternalData*,但实际DLL里它是值类型InternalData internal——这会导致内存布局不匹配,即使编译过了,运行时也会出问题。
解决步骤
修正内存布局不匹配问题:
在伪头文件HandlerFake.h里,把HandlerComponentImplementation的internal成员改成和DLL里一致的类型:// 原伪头文件里的错误声明 // InternalData* internal; // 改成和DLL一致的: InternalData internal;通过函数指针调用私有成员函数:
由于InternalData::Update()没有被导出,直接调用会触发链接错误,需要用函数指针的方式绕开链接器检查。具体步骤:- 找到
InternalData::Update()的内存地址(可以通过逆向工程工具,比如x64dbg,或者根据类的内存布局计算偏移)。 - 在代码里把地址转换成函数指针,然后调用。
示例代码修改:
void ManipulatorComponent::Initialize(SharedComponent<CommonClass>* shared) { auto handler = shared->Get("Handler"); auto handlerComponent = (HandlerComponent*) handler; auto implementation = handlerComponent->implementation; auto handlerComponentImplementation = static_cast<HandlerComponentImplementation*>(implementation.GetReference()); // 修正后的internal访问(值类型而非指针) auto& internalData = handlerComponentImplementation->internal; // 假设通过逆向得到Update()的函数地址是0x12345678(实际要根据DLL的导出表或内存计算) using UpdateFunc = void(__thiscall*)(InternalData*); UpdateFunc update = reinterpret_cast<UpdateFunc>(0x12345678); // 调用函数,传入this指针 update(&internalData); }- 找到
替代方案:利用现有导出函数间接调用:
如果能找到DLL里已经导出的函数间接触发Update()(比如HandlerComponent::GetData()内部会调用internal.Update()),可以通过调用这些公开函数来达到目的,避免直接调用私有函数。
- 内存布局匹配优先:伪头文件的类/结构必须和DLL内部的内存布局完全一致(成员顺序、类型、继承关系都不能错),否则会导致内存访问错误。
- 函数指针跳转:对于未导出的成员函数,通过逆向工程获取函数地址,转换成对应签名的函数指针调用。注意
__thiscall调用约定(成员函数默认调用约定)需要传入this指针作为第一个参数。 - 利用RTTI或虚函数表:如果类有虚函数,可以通过虚函数表的偏移找到对应的函数地址。比如
CommonClass如果有虚函数,HandlerComponentImplementation的虚表会包含它的虚函数入口。 - 内存直接修改:如果只是需要修改变量,直接通过内存偏移访问即可,不需要函数调用,这也是你之前能成功修改变量的原因。
- DLL注入与挂钩:如果需要长期调用私有函数,可以把自己的组件注入到目标进程空间,通过挂钩或直接内存访问的方式操作,但这种方式复杂度较高。
内容的提问来源于stack exchange,提问作者YYY

