例如,我有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;
}
不要严格判断。理想情况下,如果能够突出显示所使用方法的所有参数,那将是非常方便和好的。