最近用QT+大漠插件开发的一个游戏脚本,自己用的不打算开源了,记录一下注意事项。
关于QT的信号和槽
QT的信号参数不要用WIN32风格的,像bool不要用BOOL会报错。如果普通的C++类想要使用QT的信号机制,必须继承QObject,并声明QObject宏。
关于QString转C字符串
QString转wchar_t,不要用QString,C语言原生API都需要TCHAR*类型的参数,如果非要用可以用下面的方法转换一下,注意参数2是一个TCHAR **类型。,计算大小也可以判断是否小于0,因为utf-8的中文二进制补码第一位都是1
UNICODE只是一个字符集,而UTF-8规定了它的储存方式,和TCHAR没关系,CHAR和WCHAR只是储存字符的长度不一样。CHAR也可以储存utf-8
关于编码相关可以参考我的这篇文章:
www.btkrc.com/posts/7e8cdef5/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| #include "tool.h"
BOOL Tool::QString2Wchar(QString buf, TCHAR **szStr) { TCHAR *pTemp = (TCHAR *)reinterpret_cast<const TCHAR *>(buf.utf16()); DWORD length = lstrlen(pTemp); *szStr = new TCHAR[length + 2]; memset(*szStr, 0, length + 2);
wcscpy_s(*szStr, length + 2, pTemp); return TRUE; }
BOOL Tool::QString2Char(QString buf, CHAR **szStr) { std::string str = buf.toLocal8Bit(); const CHAR *pTemp = str.c_str(); DWORD size = strlenUtf8(pTemp); *szStr = new CHAR[size]; strcpy_s(*szStr, size, pTemp); return TRUE; }
DWORD Tool::strlenUtf8(CONST CHAR *szStr) { UCHAR ch; CONST CHAR *p = szStr; DWORD size=0; while (*p) { ch = *p; if (ch > 0 && ch <= 0x7F) { size += 1; p += 1; } else if (ch >= 0x80 && ch <= 0x7FF) { size += 2; p += 2; } else if (ch >= 0x800 && ch <= 0xFFFF) { size += 3; p += 3; } else if (ch >= 0x10000 && ch <= 0x10FFFF) { size += 4; p += 4; } }
return size + 1; }
|
QT中使用多线程
QT中多线程可以使用QThread类,重写父类的run()成员实现,但如果要自己调用API的话要注意了,C++中成员函数有一个隐式的this指针,会导致线程函数参数错误。而且线程函数必须是全局的。如果不想用QThread类的话,可以把线程函数用友元装饰,然后在接口中调用CreateThread()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| HANDLE Control::newThread(PVOID pFunc) { HANDLE hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, this, 0, NULL );
if (!hThread) { MessageBox(hwnd, TEXT("CreateThread Error\n"), NULL, MB_OK | MB_ICONEXCLAMATION); }
return hThread; }
|
如上,在接口中创建线程,线程地址必须是友元,如果直接写全局函数是不能访问类的private成员的。
在QT中处理Windows消息队列
1 2
| bool nativeEvent(const QByteArray &eventType, void *message, long *result)override;
|
关于QT中使用API创建子进程
CreateProcess的第一个参数必须是可写的,否则会报错,或者参数也行,只要保证堆栈存在。并且当前开发环境使用unicode字符集时,CreateProcess的第一个必须指向一个实例字符数组,在unicode版本的CreateProcess中需要修改字符串的内容。所以,这个参数不能为指向只读内存的指针。如果这个参数是一个常量,函数将产生内存访问错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| BOOL Control::createProc(HANDLE *hProc, LPCTSTR szFileName) { PROCESS_INFORMATION pi; STARTUPINFO si; ZeroMemory(&si, sizeof(si)); ZeroMemory(&pi, sizeof(pi)); si.cb = sizeof(si);
if (CreateProcess( szFileName, NULL, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, NULL, NULL, &si, &pi )) { *hProc = pi.hProcess; CloseHandle(pi.hThread); return TRUE; } return FALSE; }
|
如上,UNICODE版本需要设置一个flag,si的cb成员一定要设置正确,否则会报错
互斥体句柄
在程序退出前不要关闭互斥对象句柄,它会在最后一个句柄被关闭时自行销毁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| DWORD preventMultiple(LPCTSTR mutex) { HANDLE hMutex; hMutex = CreateMutex(NULL, FALSE, mutex); DWORD ret = GetLastError();
if (!hMutex) { return MUTEXTOKEN_ERROR; } if (ret == ERROR_ALREADY_EXISTS) { return MUTEXTOKEN_EXISTS; } else { return 0; }
}
|
其他
PROCESS_INFORMATION、STARTUPINFO、PROCESSENTRY32这些结构体的dwSize字段必须被设置正确,否则相关api无法正常执行。
COLORREF转RGB
windows系统是小端储存的,而且COLORREF储存的RGB是反过来的,要转换一下使用,给每个字节调换个位置,,如下:
1 2 3 4 5 6 7 8 9 10 11 12
| pControl->moveToWaitOrGame(hwndHOS, &pControl->params.waitRect, 1); while (TRUE) { COLORREF color = GetPixel(hdc, pControl->params.RGBPointerX, pControl->params.RGBPointerY);
DWORD transColor = (*(pb) << 16) + ((*(pb + 1)) << 8) + (*(pb + 2)); if (transColor == pControl->params.mRGB) { ... }
}
|