在宏/模板的帮助下生成任何dll的包装器



例如,我有name1.dll的方法:

  • void func1(int)
  • int func2(bool, char)

name2.dll与方法:

  • std::string func1()
  • bool func2(int, int, int)

为了处理它们,我想分别使用以下语法获得一些生成的类及其对象

DECLARE_WRAPPER(
Wrapper1,
DECLARE_METHOD(func1, void(int))
DECLARE_METHOD(func2, int(bool, char))
)

DECLARE_WRAPPER(
Wrapper2,
DECLARE_METHOD(func1, std::string())
DECLARE_METHOD(func2, bool(int, int, int))
)

,像这样使用:

Wrapper1 w1("name1.dll");
w1 func1(6);
w1.func2(false, 'c');
Wrapper w2("name2.dll");
w2.func1();
w2.func2(6, 5, 7)

我到目前为止的c++代码如下:

#include <iostream>
#include <string>
#include <windows.h>
#define DECLARE_METHOD(name, ret, ...) 
ret name(__VA_ARGS__) { 
FARPROC proc = GetProcAddress(m_hDll, #name); 
if (proc == nullptr) { 
throw std::runtime_error("Function not found"); 
} 
va_list args; 
va_start(args, __VA_ARGS__); 
ret result = reinterpret_cast<ret(__cdecl*)(__VA_ARGS__)>(proc)(__VA_ARGS__); 
va_end(args); 
return result; 
}
#define DECLARE_WRAPPER(wrapperName, ...) 
class wrapperName { 
public: 
wrapperName(const std::string& dllPath) : m_hDll(nullptr) { 
m_hDll = LoadLibraryA(dllPath.c_str()); 
} 
~wrapperName() { 
if (m_hDll) { 
FreeLibrary(m_hDll); 
} 
} 
__VA_ARGS__ 
private: 
HMODULE m_hDll; 
};
DECLARE_WRAPPER(
Wrapper1,
DECLARE_METHOD(func1, void, int)
DECLARE_METHOD(func2, int, bool, char)
);
DECLARE_WRAPPER(
Wrapper2,
DECLARE_METHOD(func1, std::string)
DECLARE_METHOD(func2, bool, int, int, int)
);
int main() {
const std::string dllPath1 = "name1.dll";
const std::string dllPath2 = "name2.dll";
Wrapper1 w1(dllPath1);
w1.func1(6);
int res = w1.func2(false, 'c');
std::cout << "Wrapper1::func2 returned " << res << std::endl;
Wrapper2 w2(dllPath2);
std::string str = w2.func1();
std::cout << "Wrapper2::func1 returned " << str << std::endl;
bool b = w2.func2(6, 5, 7);
std::cout << "Wrapper2::func2 returned " << b << std::endl;
return 0;
}

但是我有错误,我不能使它工作。如何修复这个代码?或者,我是在尝试实现在c++中无法完成的事情吗?我可以使用的当前版本是C++17

我不认为你可以在DECLARE_WRAPPER()的里面使用DECLARE_METHOD()。您可能必须将DECLARE_WRAPPER()分解为单独的宏,以便您可以在它们之间使用DECLARE_METHOD()

然而,DECLARE_METHOD()本身肯定不会像你写的那样工作。它不能同时使用__VA_ARGS__来声明方法参数和调用加载的proc()。而且,它根本不能将__VA_ARGS__传递给va_start()(这并不重要,因为您无论如何都不使用args)。你应该使用c++风格的可变模板,而不是C风格的可变宏。

试试这样写:

#define DECLARE_METHOD(name, returnType) 
template<typename... Args> 
returnType name(Args&&... args) 
{ 
using procType = returnType (__cdecl *)(Args...); 
procType proc = reinterpret_cast<procType>(GetProcAddress(m_hDll, #name)); 
if (proc == nullptr) { 
throw std::runtime_error("Function not found"); 
} 
return proc(std::forward<Args>(args)...); 
}
#define DECLARE_WRAPPER_BEGIN(wrapperName) 
class wrapperName { 
public: 
wrapperName(const std::string& dllPath) : m_hDll(nullptr) { 
m_hDll = LoadLibraryA(dllPath.c_str()); 
if (m_hDll == nullptr) 
throw std::runtime_error("DLL not found"); 
} 
~wrapperName() { 
FreeLibrary(m_hDll); 
}
#define DECLARE_WRAPPER_END() 
private: 
HMODULE m_hDll; 
};
DECLARE_WRAPPER_BEGIN(Wrapper1)
DECLARE_METHOD(func1, void)
DECLARE_METHOD(func2, int)
DECLARE_WRAPPER_END()
DECLARE_WRAPPER_BEGIN(Wrapper2)
DECLARE_METHOD(func1, std::string)
DECLARE_METHOD(func2, bool)
DECLARE_WRAPPER_END()

在线演示

另一方面,Wrapper2::func1()通过DLL边界传递std::string是不安全的。如果DLL函数返回的char*是您想要转换为std::string的,那么您的DECLARE_METHOD()宏必须考虑到这一点,通过使用单独的参数分别指定两种返回类型。例如:

#define DECLARE_METHOD(name, dllReturnType, methReturnType) 
template<typename... Args> 
methReturnType name(Args&&... args) 
{ 
using procType = dllReturnType (__cdecl *)(Args...); 
procType proc = reinterpret_cast<procType>(GetProcAddress(m_hDll, #name)); 
if (proc == nullptr) { 
throw std::runtime_error("Function not found"); 
} 
return proc(std::forward<Args>(args)...); 
}
}
DECLARE_WRAPPER_BEGIN(Wrapper1)
DECLARE_METHOD(func1, void, void)
DECLARE_METHOD(func2, int, int)
DECLARE_WRAPPER_END()
DECLARE_WRAPPER_BEGIN(Wrapper2)
DECLARE_METHOD(func1, char*, std::string)
DECLARE_METHOD(func2, bool, bool)
DECLARE_WRAPPER_END()

也许这对某些人有用,所以我写了我的" final ";的形式。是的,有很多事情要做,但这里是最适合我的框架:

#include <windows.h>
#include <type_traits>
#include <filesystem>

#define DECLARE_METHOD(name, ret, ...)                 
private:                                           
using MethodType_##name = ret(*)(__VA_ARGS__); 
MethodType_##name m_##name{ nullptr };         

#define DEFINE_METHOD(name)                                                                                   
public:                                                                                                   
template<typename... Args>                                                                            
typename std::enable_if<!std::is_void<std::invoke_result_t<decltype(m_##name), Args...>>::value,      
std::invoke_result_t<decltype(m_##name), Args...>>::type                      
name(Args&&... args)                                                                                  
{                                                                                                     
if (!m_##name)                                                                                    
{                                                                                                 
m_##name = reinterpret_cast<MethodType_##name>(GetProcAddress(m_hDll, #name));                
}                                                                                                 
if (!m_##name)                                                                                    
{                                                                                                 
throw std::runtime_error("Function not found");                                               
}                                                                                                 
return m_##name(std::forward<Args>(args)...);                                                     
}                                                                                                     
                                        
template<typename... Args>                                                                            
typename std::enable_if<std::is_void<std::invoke_result_t<decltype(m_##name), Args...>>::value>::type 
name(Args&&... args)                                                                                  
{                                                                                                     
if (!m_##name)                                                                                    
{                                                                                                 
m_##name = reinterpret_cast<MethodType_##name>(GetProcAddress(m_hDll, #name));                
}                                                                                                 
if (!m_##name)                                                                                    
{                                                                                                 
throw std::runtime_error("Function not found");                                               
}                                                                                                 
m_##name(std::forward<Args>(args)...);                                                            
}

#define DECLARE_DLL_WRAPPER(WrapperName, ...)                            
class WrapperName                                                    
{                                                                    
public:                                                              
explicit WrapperName(const std::filesystem::path& fullPathToDll) 
{                                                                
m_hDll = LoadLibraryA(fullPathToDll.u8string().c_str());     
if (!m_hDll)                                                 
{                                                            
throw std::runtime_error("DLL not found");               
}                                                            
}                                                                
~WrapperName()                                                   
{                                                                
FreeLibrary(m_hDll);                                         
}                                                                
__VA_ARGS__                                                      
   
private:                                                             
HMODULE m_hDll{ nullptr };                                       
};

#define DECLARE_AND_DEFINE_METHOD(name, ret, ...)   
DECLARE_METHOD(name, ret, __VA_ARGS__)          
DEFINE_METHOD(name)

// Declaring some dll wrapper type for futher using it
DECLARE_DLL_WRAPPER(Wrapper1,
DECLARE_AND_DEFINE_METHOD(func1, int, bool bParam, int iParam)
DECLARE_AND_DEFINE_METHOD(func2, void, float dParam)
)

int main()
{
Wrapper1 w1("plugin.dll");
const auto ret = w1.func1(false, 6);
w1.func2(2.0f);
return 0;
}

不要严格判断。理想情况下,如果能够突出显示所使用方法的所有参数,那将是非常方便和好的。

最新更新