解决C++封装WinAPI拖拽功能的非静态引用报错
问题原因
WinAPI要求传入的窗口过程回调必须是静态函数或全局函数,不能是普通非静态类成员函数。非静态成员函数会隐式把this指针作为第一个入参,函数签名和WinAPI要求的LRESULT CALLBACK (HWND, UINT, WPARAM, LPARAM)格式不匹配,所以编译器会报「引用非静态成员」的错误。
解决方法
- 把回调函数声明为类的
static成员,消除隐式this参数,匹配WinAPI的函数签名要求 - 借助窗口的
GWLP_USERDATA存储空间,把当前类实例的指针绑定到对应窗口上,在静态回调里取出这个指针,就能正常访问类的非静态成员(比如存储的原窗口过程地址)
注意:原示例全局代码里存在未定义handle变量的笔误,同时变长数组char fichier[size+1]不属于标准C++特性,统一使用宽字符版本WinAPI函数可以避免非ASCII路径乱码问题,修正后完整可运行代码如下:
#include <windows.h> #include <SFML/Graphics.hpp> #include <vector> #include <iostream> class Dragger { public: Dragger(HWND handle) : m_originalProc(nullptr), m_hwnd(handle) { // 将当前类实例指针存入窗口用户数据区 SetWindowLongPtrW(handle, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this)); DragAcceptFiles(handle, TRUE); // 挂载静态窗口回调 m_originalProc = SetWindowLongPtrW(handle, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(&Dragger::s_WndProc)); } ~Dragger() { // 窗口销毁前还原原窗口过程,避免访问违规 if (m_hwnd && m_originalProc) { SetWindowLongPtrW(m_hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(m_originalProc)); SetWindowLongPtrW(m_hwnd, GWLP_USERDATA, 0); } } private: // 静态回调,完全匹配WinAPI要求的函数签名 static LRESULT CALLBACK s_WndProc(HWND handle, UINT message, WPARAM wParam, LPARAM lParam) { // 取出提前绑定的Dragger实例指针 Dragger* self = reinterpret_cast<Dragger*>(GetWindowLongPtrW(handle, GWLP_USERDATA)); if (message == WM_DROPFILES && self) { HDROP hdrop = reinterpret_cast<HDROP>(wParam); constexpr UINT fileIndex = 0; // 默认读取第一个拖入的文件 POINT dropPos{}; if (DragQueryPoint(hdrop, &dropPos)) { // 先获取文件路径需要的字符长度 UINT pathLen = DragQueryFileW(hdrop, fileIndex, nullptr, 0); if (pathLen > 0) { // 用vector分配缓冲区,替代非标准的变长数组 std::vector<wchar_t> pathBuf(pathLen + 1); DragQueryFileW(hdrop, fileIndex, pathBuf.data(), pathBuf.size()); std::wcout << L"FILE : [" << pathLen << L"] " << pathBuf.data() << std::endl; } } else { std::cout << "Failed to get drop point" << std::endl; } DragFinish(hdrop); } // 调用原窗口过程,保证SFML本身的消息处理逻辑正常运行 return CallWindowProcW(self ? self->m_originalProc : DefWindowProcW, handle, message, wParam, lParam); } HWND m_hwnd; LONG_PTR m_originalProc; }; int main() { const int w = 1000; const int h = 800; sf::RenderWindow window(sf::VideoMode(w, h), "SFML window"); // 通过SFML接口获取原生窗口句柄初始化拖拽功能 Dragger dragger(window.getSystemHandle()); while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) { window.close(); } } window.clear(); window.display(); } return EXIT_SUCCESS; }
内容的提问来源于stack exchange,提问作者Al Tilmidh
相关产品推荐
相关产品推荐

