C线程和mfc线程 C多线程 tls-thread local storage
1 2 3 4 TlsAlloc TlsFree TlsSetValue TlsGetValue
TlsAlloc
1 2 3 4 5 函数原型:DWORD TlsAlloc () ; 作用:为调用该函数的线程分配一个线程局部存储序号,供其它三个函数使用 注意:一个进程最多分配TLS_MINIMUM_AVAILABLE个索引,目前定义为64 ,一个进程中所有线程分配的索引不会重复 使用: int index=TlsAlloc()
DWORD TlsAlloc(void); // 返回一个TLS索引
每个线程创建时系统给它分配一个LPVOID指针的数组(叫做TLS数组,长度为TLS_MINIMUM_AVAILABLE),TlsAlloc()函数的作用是返回访问当前线程的TLS数组的一个下标(索引),不同线程得到不同的索引变量值。
当调用TlsAlloc的时候,系统会挨个检查这个数组中成员的值,直到找到一个值为FREE的成员。把找到的成员的值由FREE改为INUSE后,TlsAlloc函数返回该成员的索引。如果不能找到一个值为FREE的成员,TlsAlloc函数就返回TLS_OUT_OF_INDEXES(在WinBase.h文件中定义为-1)
TlsSetValue
1 2 3 4 函数原型:BOOL TlsSetValue (DWORD dwTlsIndex,LPVOID lpTlsValue) ; 作用:在指定的序号内存储数据,如TlsAlloc分配成功返回的序号 使用: BOOL ret=TlsSetValue(index,(LPVOID)0x123456 )
BOOL TlsSetValue(
DWORD dwTlsIndex, // TLS 索引
LPVOID lpTlsValue // 要设置的值
);
将参数lpTlsValue指定的值放入索引为dwTlsIndex的线程数组成员中,调用成功,会返回TRUE。
TlsGetValue
1 2 3 4 函数原型:LPVOID TlsGetValue (DWORD dwTlsIndex) ; 作用:取得指定序号内存储的数据 使用: LPVOID lp=TlsGetValue(index);
LPVOID TlsGetValue(DWORD dwTlsIndex ); // TLS索引
先检查TLS 索引值合法与否,如果合法,取得线程数组中索引为dwTlsIndex的成员的值。
TlsFree
1 2 3 4 函数原型:BOOL TlsFree (DWORD dwTlsIndex) ; 作用:释放指定序号 使用: TlsFree(index);
释放局部存储索引。
新建控制台应用
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 50 #include <windows.h> #include <conio.h> #include <stdio.h> DWORD WINAPI ThreadFunc (LPVOID lpParam) { DWORD dwIdx = TlsAlloc(); TlsSetValue(dwIdx, (LPVOID)GetCurrentThreadId()); for (int i = 0 ; i < 0x100000 ; ++i) { Sleep(10 ); DWORD dwVal = (DWORD)TlsGetValue(dwIdx); char szMsg[80 ]; wsprintf(szMsg, "Parameter=%d\tidx:%d\tval:%d\r\n" , GetCurrentThreadId(),dwIdx,dwVal); printf (szMsg); } TlsFree(dwIdx); return 0 ; } int main () { DWORD dwThreadId, dwThrdParam = 1 ; HANDLE hThread; char szMsg[80 ]; for (int i = 0 ; i < 80 ; ++i) { hThread = CreateThread( NULL , 0 , ThreadFunc, &dwThrdParam, 0 , &dwThreadId ); } if (hThread == NULL ) { wsprintf(szMsg, "CreateThread failed." ); MessageBox(NULL , szMsg, "main" , MB_OK); } else { _getch(); CloseHandle(hThread); } return 0 ; }
MT
多线程(M),静态库(T)
MTD
多线程(M),静态库(T),调试版(d)
MD
多线程(M),动态库(D)
MDd
多线程(M),动态库(D),调试版(d)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 unsigned long _beginthread( void (_cdecl *start_address)(void *), unsigned stack_size, void *arglist ); unsigned long _beginthreadex( void *security, unsigned stack_size, unsigned (_stdcall *start_address)(void *), void *argilist, unsigned initflag, unsigned *thrdaddr ); void _endthread(void ); void _endthreadex(unsigned retval);
两组函数都是用来创建和结束线程的。这两对函数的不同点如下: (1)从形式上开,_beginthreadex()更像CreateThread()。_beginthreadex()比_beginthread()多3个参数:intiflag,security和threadaddr。 (2)两种创建方式的线程函数不同。_beginthreadex()的线程函数必须调用_stdcall调用方式,而且必须返回一个unsigned int型的退出码。 (3)_beginthreadex()在创建线程失败时返回0,而_beginthread()在创建线程失败时返回-1。这一点是在检查返回结果是必须注意的。 (4)如果是调用_beginthread()创建线程,并相应地调用_endthread()结束线程时,系统自动关闭线程句柄;而调用_beginthreadx()创建线程,并相应地调用_endthreadx()结束线程时,系统不能自动关闭线程句柄。因此调用_beginthreadx()创建线程还需程序员自己关闭线程句柄,以清除线程的地址空间。
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 50 51 #include <windows.h> #include <conio.h> #include <stdio.h> #include <process.h> unsigned int WINAPI ThreadFunc (LPVOID lpParam) { DWORD dwIdx = TlsAlloc(); TlsSetValue(dwIdx, (LPVOID)GetCurrentThreadId()); for (int i = 0 ; i < 0x100000 ; ++i) { Sleep(10 ); DWORD dwVal = (DWORD)TlsGetValue(dwIdx); char szMsg[80 ]; wsprintf(szMsg, "Parameter=%d\tidx:%d\tval:%d\r\n" , GetCurrentThreadId(),dwIdx,dwVal); printf (szMsg); } TlsFree(dwIdx); return 0 ; } int main () { unsigned int dwThreadId, dwThrdParam = 1 ; HANDLE hThread; char szMsg[80 ]; for (int i = 0 ; i < 80 ; ++i) { hThread = (HANDLE)_beginthreadex( NULL , 0 , ThreadFunc, &dwThrdParam, 0 , &dwThreadId ); } if (hThread == NULL ) { wsprintf(szMsg, "CreateThread failed." ); MessageBox(NULL , szMsg, "main" , MB_OK); } else { _getch(); CloseHandle(hThread); } return 0 ; }
运行结果
mfc多线程 新建MFC应用程序
AfxBeginThread
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 CWinThread* AfxBeginThread (AFX_THREADPROC pfnThreadProc, LPVOID lParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0 , DWORD dwCreateFlags = 0 , LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL ) ;
1 2 3 4 5 6 7 8 9 10 11 12 UINT MyControllingFunction (LPVOID pParam) { CString strFmt; strFmt.Format("hello mfc thread %d" , GetCurrentThreadId); AfxMessageBox(strFmt); return 0 ; } void CMFCThreadDlg::OnBnClickedButton1 () { AfxBeginThread(MyControllingFunction, NULL ); }
运行结果
新建MFC类
新建类向导
新建Run虚函数
CMyThread.cpp
1 2 3 4 5 6 7 8 9 10 11 int CMyThread::Run () { AfxMessageBox("Run" ); return CWinThread::Run(); } void CMyThread::OnMmTest (UINT wParam, LONG lParam) { AfxMessageBox("OnMnTest" ); }
CMyThread.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #pragma once #define MM_TEST WM_USER+1 class CMyThread : public CWinThread{ DECLARE_DYNCREATE(CMyThread) protected: CMyThread(); virtual ~CMyThread(); public: virtual BOOL InitInstance () ; virtual int ExitInstance () ; protected: DECLARE_MESSAGE_MAP() public: virtual int Run () ; afx_msg void OnMmTest (UINT wParam, LONG lParam) ; };
1 2 3 4 5 6 #include "CMyThread.h" CWinThread* pThread = NULL ; void CMFCThreadDlg::OnBnClickedButton2 () { pThread = AfxBeginThread(RUNTIME_CLASS(CMyThread)); }
1 2 3 4 void CMFCThreadDlg::OnBnClickedButton3 () { pThread->PostThreadMessageA(MM_TEST, NULL , NULL ); }
运行后先点击方法二运行起来,然后点击发消息来发送消息
ini文件操作 不带Private的操作的是系统配置文件(win.ini),带Private的是操作程序自己的配置文件,注意文件名给全路径
WriteProfileString
把信息写入系统的win.ini文件
1 2 3 4 5 BOOL WriteProfileString ( LPCTSTR lpAppName, LPCTSTR lpKeyName, LPCTSTR lpString )
WritePrivateProfileString
函数功能:
将lpString(CString型)变量存入lpFileName(Cfg.ini)文件里面,按照lpAppName字段进行分类索引。
各个参数详解:
BOOL WritePrivateProfileString(
LPCTSTRlpAppName,
LPCTSTRlpKeyName,
LPCTSTRlpString,
LPCTSTRlpFileName
);
//其中各参数的意义:
LPCTSTR lpAppName; //是INI文件中的一个字段名.
LPCTSTR lpKeyName;//是lpAppName下的一个键名,通俗讲就是变量名.
LPCTSTR lpString; //是键值, 也就是变量的值,不过必须为LPCTSTR型或CString型的.
LPCTSTR lpFileName;//是完整的INI文件名.
调用:
WritePrivateProfileString(“StudentInfo1”,”身份证”,”44022520070001”,”.\ConfigFile\ConfigInit.ini”);
GetCurrentDirectory
函数功能 获取当前进程的当前目录。参数说明 nBufferLength 缓冲区的长度lpBuffer 指定一个预定义字串,用于装载当前目录返回值 调用成功 返回装载到lpBuffer的字节数。 使用GetLastError函数可获得错误信息。
新建控制台应用(如果权限不够则以管理员身份运行)
1 2 3 4 5 6 7 8 9 10 #include <windows.h> #include <iostream> int main () { DWORD dwRet = WriteProfileString("CR40" , "年龄" , "10" ); std ::cout << "Hello World!\n" ; }
有值说明运行成功(若为0则可能是没有权限,以管理员身份运行)
找到win.ini
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <windows.h> #include <iostream> int main () { char szPath[MAX_PATH] = {}; GetCurrentDirectory(MAX_PATH, szPath); DWORD dwRet = WriteProfileString("CR40" , "年龄" , "10" ); char szIniPath[MAX_PATH] = {}; wsprintf(szIniPath, "%s\\test.ini" , szPath); BOOL bRet = WritePrivateProfileString("CR40" , "年龄" , "12" , "test.ini" ); std ::cout << "Hello World!\n" ; }
注册表 RegCreatekey
该函数用于打开指定的键,如果键不存在,则新建一个键或子键 LONG RegCreatekey(HKEY hKey,LPCTSTR lpSubKey,PHKEY phkResult);hKey :打开键的句柄lpSubKey :函数打开或创建的键名phkResult :函数返回的打开或创建键的句柄指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <windows.h> #include <iostream> int main () { HKEY h361 = NULL ; LSTATUS nRet = RegCreateKey(HKEY_CURRENT_USER, "Software\\361" , &h361); if (nRet != ERROR_SUCCESS) { return 0 ; } }
创建成功
RegSetValue
设置一个注册表键的缺省数据值,不能用此函数设置除了缺省值以外的任何值。
参数 :
hKey –键句柄
lpSubKey –子键名称或路径
dwType –数据类型,只能接受 REG_SZ
lpData –所设置的字符串数据
cbData –lpData字符串的长度,须包含Chr(0)字符
返回值: =0 成功 ≠0 失败
RegSetValueEx
RegSetValueEx函数可以设置注册表中键的值 各个参数及返回值的含义如下:hKey 的含义同RegCreateKeyEx函数中的hKey参数。lpValueName 为一个指向包含值名的字符串指针。Reserved 保留,通常必须设置为0。dwType 确定了设置的值的类型同RegQueryValueKeyEx的lyType参数。lpData 为一个指向包含数据的缓冲区的指针。cbData 以字节为单位,指定数据的长度。 返回值同RegCreateKeyEx函数的返回值。
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 #include <windows.h> #include <iostream> int main () { HKEY h361 = NULL ; LSTATUS nRet = RegCreateKey(HKEY_CURRENT_USER, "Software\\361" , &h361); if (nRet != ERROR_SUCCESS) { return 0 ; } char szData[] = { "hello" }; nRet = RegSetValueEx(h361, "world" , 0 , REG_SZ, (LPBYTE)szData, sizeof (szData)); if (nRet != ERROR_SUCCESS) { return 0 ; } DWORD dwValue = 1 ; nRet = RegSetValueEx(h361, "no" , 0 , REG_DWORD, (LPBYTE)&dwValue, sizeof (dwValue)); if (nRet != ERROR_SUCCESS) { return 0 ; } }
RegDeleteKey
该函数用于从注册表中删除某个子键 LONG RegDeleteKey(HKEY hkey,LPCWSTR lpSubKey);hKey :当前打开的父键句柄lpSubKey :将要删除的子键名称
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 #include <windows.h> #include <iostream> int main () { HKEY h361 = NULL ; LSTATUS nRet = RegCreateKey(HKEY_CURRENT_USER, "Software\\361" , &h361); if (nRet != ERROR_SUCCESS) { return 0 ; } char szData[] = { "hello" }; nRet = RegSetValueEx(h361, "world" , 0 , REG_SZ, (LPBYTE)szData, sizeof (szData)); if (nRet != ERROR_SUCCESS) { return 0 ; } DWORD dwValue = 1 ; nRet = RegSetValueEx(h361, "no" , 0 , REG_DWORD, (LPBYTE)&dwValue, sizeof (dwValue)); if (nRet != ERROR_SUCCESS) { return 0 ; } RegCloseKey(h361); nRet = RegDeleteKey(HKEY_CURRENT_USER,"Software\\361" ); if (nRet != ERROR_SUCCESS) { return 0 ; } }
运行后原来的361被删除
RegDeleteValue
参数说明:
hKey 同RegCreateKeyEx函数中的hKey参数。
lpSubKey 同RegCreateKeyEx函数中的lpSubKey参数。
返回值 (同上)
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 #include <windows.h> #include <iostream> int main () { HKEY h361 = NULL ; LSTATUS nRet = RegCreateKey(HKEY_CURRENT_USER, "Software\\361" , &h361); if (nRet != ERROR_SUCCESS) { return 0 ; } char szData[] = { "hello" }; nRet = RegSetValueEx(h361, "world" , 0 , REG_SZ, (LPBYTE)szData, sizeof (szData)); if (nRet != ERROR_SUCCESS) { return 0 ; } DWORD dwValue = 1 ; nRet = RegSetValueEx(h361, "no" , 0 , REG_DWORD, (LPBYTE)&dwValue, sizeof (dwValue)); if (nRet != ERROR_SUCCESS) { return 0 ; } nRet = RegDeleteValue(h361, "world" ); RegCloseKey(h361); nRet = RegDeleteKey(HKEY_CURRENT_USER,"Software\\361" ); if (nRet != ERROR_SUCCESS) { return 0 ; } }
下断点后运行
删除前已经完成了361的创建
删除后world被成功删除
运行结束后整个361都被删除
RegQueryValue
RegQueryValueEx找回一个打开的注册表键值相关联的给定的变量数据或者变量。
1 2 3 4 5 6 7 8 LONG RegQueryValueEx ( HKEY hKey, LPCTSTR lpValueName, LPD [word](https: LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData );
示例
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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 #include <windows.h> #include <iostream> void QueryKey (HKEY hKey) ;int main () { #if 0 char szPath[MAX_PATH] = {}; GetCurrentDirectory(MAX_PATH, szPath); DWORD dwRet = WriteProfileString("CR40" , "年龄" , "10" ); char szIniPath[MAX_PATH] = {}; wsprintf(szIniPath, "%s\\test.ini" , szPath); BOOL bRet = WritePrivateProfileString("CR40" , "年龄" , "12" , szIniPath); std ::cout << "Hello World!\n" ; #endif #if 0 HKEY h361 = NULL ; LSTATUS nRet = RegCreateKey(HKEY_CURRENT_USER, "Software\\361" , &h361); if (nRet != ERROR_SUCCESS) { return 0 ; } char szData[] = { "hello" }; nRet = RegSetValueEx(h361, "world" , 0 , REG_SZ, (LPBYTE)szData, sizeof (szData)); if (nRet != ERROR_SUCCESS) { return 0 ; } DWORD dwValue = 1 ; nRet = RegSetValueEx(h361, "no" , 0 , REG_DWORD, (LPBYTE)&dwValue, sizeof (dwValue)); if (nRet != ERROR_SUCCESS) { return 0 ; } nRet = RegDeleteValue(h361, "world" ); RegCloseKey(h361); nRet = RegDeleteKey(HKEY_CURRENT_USER, "Software\\361" ); if (nRet != ERROR_SUCCESS) { return 0 ; } #endif HKEY h361 = NULL ; LSTATUS nRet = RegCreateKey(HKEY_CURRENT_USER, "Software" , &h361); if (nRet != ERROR_SUCCESS) { return 0 ; } QueryKey(h361); } #define MAX_VALUE_NAME 1024 void QueryKey (HKEY hKey) { CHAR achKey[MAX_PATH]; CHAR achClass[MAX_PATH] = TEXT("" ); DWORD cchClassName = MAX_PATH; DWORD cSubKeys = 0 ; DWORD cbMaxSubKey = 0 ; DWORD cchMaxClass = 0 ; DWORD cValues = 0 ; DWORD cchMaxValue = 0 ; DWORD cbMaxValueData = 0 ; DWORD cbSecurityDescriptor = 0 ; FILETIME ftLastWriteTime; DWORD i = 0 , j = 0 , retCode = 0 , retValue = 0 ; CHAR achValue[MAX_VALUE_NAME]; DWORD cchValue = MAX_VALUE_NAME; CHAR achBuff[80 ]; RegQueryInfoKey( hKey, achClass, &cchClassName, NULL , &cSubKeys, &cbMaxSubKey, &cchMaxClass, &cValues, &cchMaxValue, &cbMaxValueData, &cbSecurityDescriptor, &ftLastWriteTime); for (i = 0 , retCode = ERROR_SUCCESS; retCode == ERROR_SUCCESS; i++) { DWORD dwNamelen = MAX_PATH; retCode = ::RegEnumKeyEx(hKey, i, achKey, &dwNamelen, NULL , NULL , NULL , &ftLastWriteTime); if (retCode == (DWORD)ERROR_SUCCESS) { printf ("Key:%s\r\n" , achKey); } } if (cValues) { for (j = 0 , retValue = ERROR_SUCCESS; j < cValues; j++) { cchValue = MAX_VALUE_NAME; achValue[0 ] = '\0' ; retCode = ::RegEnumValue(hKey, j, achValue, &cchValue, NULL , NULL , NULL , NULL ); if (retValue != (DWORD)ERROR_SUCCESS && retValue != ERROR_INSUFFICIENT_BUFFER) { wsprintf(achBuff, "Line:%d 0 based index = %d,retValue = %d," "ValueLen = %d" , __LINE__, j, retValue, cchValue); printf (achBuff); } } } }
运行结果