Windows

C线程和MFC线程

C多线程

创建一个多线程的项目

image-20240718153951872

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
// CThread.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include<stdio.h>
#include <windows.h>
#include <conio.h>



DWORD WINAPI ThreadFunc(LPVOID lpParam)
{
for (int i = 0; i < 0x100000; ++i) {
Sleep(10);
char szMsg[80];
wsprintf(szMsg, "Parameter = %d\r\n.", GetCurrentThreadId());
printf(szMsg);
}

return 0;
}

VOID main(VOID)
{
DWORD dwThreadId, dwThrdParam = 1;
HANDLE hThread;
char szMsg[80];
for (int i = 0; i < 4; ++i) {
hThread = CreateThread(
NULL, // no security attributes
0, // use default stack size
ThreadFunc, // thread function
&dwThrdParam, // argument to thread function
0, // use default creation flags
&dwThreadId); // returns the thread identifier

}


if (hThread == NULL)
{
wsprintf(szMsg, "CreateThread failed.");
MessageBox(NULL, szMsg, "main", MB_OK);
}
else
{
_getch();
CloseHandle(hThread);
}
}

img

C语言在开始的时候没有多线程的处理方式而是通过全局变量来实现 就会导致同步问题的产生 现在好像已经解决了 跑起来没什么问题

开始时解决的多线程的问题通过_beginthread_beginthreadex

image-20240718160335455

二者将全局变量移到了线程局部存储空间 - tls

tls - thread local storage

1
2
3
4
5
6
7
TlsAlloc//申请一个线程局部存储空间 分配线程本地存储 (TLS) 索引。 进程的任何线程随后都可以使用此索引来存储和检索线程本地的值,因为每个线程都会收到自己的索引槽。

TlsFree//释放 释放线程本地存储 (TLS) 索引,使其可供重复使用。

TlsSetValue//设置索引值 将值存储在调用线程的线程本地存储 (指定 TLS 索引的 TLS) 槽中。 进程中的每个线程都具有自己的针对每个 TLS 索引的槽。

TlsGetValue//获取索引值 检索调用线程的线程本地存储 (指定 TLS 索引的 TLS) 槽中的值。 进程中的每个线程都具有自己的针对每个 TLS 索引的槽。

img

img

img

img

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
// CThread.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include<stdio.h>
#include <windows.h>
#include <conio.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;
}

VOID main(VOID)
{
DWORD dwThreadId, dwThrdParam = 1;
HANDLE hThread;
char szMsg[80];
for (int i = 0; i < 40; ++i) {
hThread = CreateThread(
NULL, // no security attributes
0, // use default stack size
ThreadFunc, // thread function
&dwThrdParam, // argument to thread function
0, // use default creation flags
&dwThreadId); // returns the thread identifier

}


if (hThread == NULL)
{
wsprintf(szMsg, "CreateThread failed.");
MessageBox(NULL, szMsg, "main", MB_OK);
}
else
{
_getch();
CloseHandle(hThread);
}
}

运行结果:

img

_beginthread_beginthreadex

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 *), //声明为void (*start_address)(void *)形式
unsigned stack_size, //是线程堆栈大小,一般默认为0
void *arglist //向线程传递的参数,一般为结构体
);

unsigned long _beginthreadex( //推荐使用
void *security, //安全属性,NULL表示默认安全性
unsigned stack_size, //是线程堆栈大小,一般默认为0
unsigned(_stdcall *start_address)(void *), //声明为unsigned(*start_address)(void *)形式
void *argilist, //向线程传递的参数,一般为结构体
unsigned initflag, //新线程的初始状态,0表示立即执行,CREATE_SUSPEND表示创建后挂起。
unsigned *thrdaddr //该变量存放线程标识符,它是CreateThread函数中的线程ID。
); //创建成功条件下的将线程句柄转化为unsigned long型返回,创建失败条件下返回0

线程结束:

//释放线程空间、释放线程TLS空间、调用ExiteThread结束线程。
void _endthread(void);
// retval:设定的线程结束码,与ExiteThread函数的参数功能一样,
//其实这个函数释放线程TLS空间,再调用ExiteThread函数,但没有释放线程空间。
void _endthreadex(unsigned retval);
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
// CThread.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#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;
}

img

MT M:多线程 T:静态库
MTd M:多线程 T:静态库 d: debug(调试版)
MD M:多线程 D:动态库
mDd M:多线程 D:动态库 d: debug(调试版)

img

MFC多线程

AfxBeginThread

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CWinThread* AfxBeginThread(
AFX_THREADPROC pfnThreadProc,
LPVOID pParam,
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);

CWinThread* AfxBeginThread(
CRuntimeClass* pThreadClass,
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);

参数4:*dwCreateFlags*
指定一个用于控制线程创建的额外标志。 此标志可以包含两个值之一:

  • CREATE_SUSPENDED 以 1 的挂起计数启动线程。 如果要在线程开始运行之前初始化 CWinThread 对象的任何成员数据(例如 m_bAutoDelete或派生类的任何成员),请使用 CREATE_SUSPENDED。 初始化完成后,使用 CWinThread::ResumeThread启动线程运行。 在调用 CWinThread::ResumeThread 之前,线程不会执行。
  • 0 创建后立即启动线程。
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171

// MFCThreadDlg.cpp: 实现文件
//

#include "pch.h"
#include "framework.h"
#include "MFCThread.h"
#include "MFCThreadDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif

protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持

// 实现
protected:
DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CMFCThreadDlg 对话框



CMFCThreadDlg::CMFCThreadDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_MFCTHREAD_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CMFCThreadDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CMFCThreadDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, &CMFCThreadDlg::OnBnClickedButton1)
END_MESSAGE_MAP()


// CMFCThreadDlg 消息处理程序

BOOL CMFCThreadDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();

// 将“关于...”菜单项添加到系统菜单中。

// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);

CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != nullptr)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}

// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标

// TODO: 在此添加额外的初始化代码

return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}

void CMFCThreadDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}

// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。

void CMFCThreadDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文

SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;

// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CMFCThreadDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}

UINT MyControllingFunction(LPVOID pParam)
{
CString strFmt;
strFmt.Format("hello mfc thread %d", GetCurrentThreadId());
AfxMessageBox(strFmt);
return 0;
}
void CMFCThreadDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
AfxBeginThread(MyControllingFunction,
NULL
);
}

运行结果:
img

添加MFC类

img

继承CWinThread

img

添加类向导

img

线程跑代码的主体

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
25
#pragma once


// CMyThread
#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(WPARAM wParam, LPARAM lParam);
};

CMyThread.cpp:

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
// CMyThread.cpp: 实现文件
//

#include "pch.h"
#include "MFCThread.h"
#include "CMyThread.h"

// CMyThread

IMPLEMENT_DYNCREATE(CMyThread, CWinThread)

CMyThread::CMyThread()
{
}

CMyThread::~CMyThread()
{
}

BOOL CMyThread::InitInstance()
{
// TODO: 在此执行任意逐线程初始化
return TRUE;
}

int CMyThread::ExitInstance()
{
// TODO: 在此执行任意逐线程清理
return CWinThread::ExitInstance();
}

BEGIN_MESSAGE_MAP(CMyThread, CWinThread)
ON_THREAD_MESSAGE(MM_TEST, &CMyThread::OnMmTest)
END_MESSAGE_MAP()


// CMyThread 消息处理程序


int CMyThread::Run()
{
// TODO: 在此添加专用代码和/或调用基类
AfxMessageBox("Run");
return CWinThread::Run();
}

void CMyThread::OnMmTest(WPARAM wParam, LPARAM lParam)
{
AfxMessageBox("OnMnTest");
}

添加两个按钮来跑线程和发消息

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "CMyThread.h"//添加头文件
CWinThread* pThread = NULL;
void CMFCThreadDlg::OnBnClickedButton2()
{
// TODO: 在此添加控件通知处理程序代码
pThread = AfxBeginThread(RUNTIME_CLASS(CMyThread));
}


void CMFCThreadDlg::OnBnClickedButton3()
{
// TODO: 在此添加控件通知处理程序代码
pThread->PostThreadMessage(MM_TEST, NULL, NULL);
}

运行效果:
先点击方法二运行 再点击发消息

img

img

这种方式与createthread类似 但是可以接收消息

ini文件操作

.ini文件是Windows系统配置文件所采用的存储格式,统管Windows的各项配置

ini配置文件由节、键、值组成。

例如:

img

等号左边为参数 右边为值 [。。。]所代表的就是节

在节声明后的所有参数都属于该节。一个节没有明显的结束标识符,一个节的开始就是上一个节的结束或者是文件结束。

ini文件操作相关的API

img

WritePrivateProfileString为例

img

创建一个控制台项目

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Reg.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include<Windows.h>
using namespace std;



int main()
{
DWORD dwRet = WriteProfileString("CR40","年龄","10");
BOOL bRet = WritePrivateProfileString("CR40", "年龄", "10","test.ini");
cout << "Hello World!\n";
}

img

获取自身工作目录:

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Reg.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <windows.h>
#include <iostream>
using namespace std;
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");
if (!bRet) {
cout << "写入失败" << endl;
}
std::cout << "Hello World!\n";
}

img

img

不带Private的操作的是系统配置文件(win.ini),带Private的是操作程序自己的配置文件,注意文件名给全路径

注册表

在运行窗口中输入regedit便可以打开注册表

img

img

img

创建指定的注册表项。 如果注册表项已存在于注册表中,则函数将打开它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Reg.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <windows.h>
#include <iostream>
using namespace std;
int main()
{
HKEY h361 = NULL;
LSTATUS nRet = RegCreateKey(HKEY_CURRENT_USER, "Software\\361", &h361);
if (nRet != ERROR_SUCCESS)
{
return 0;
}
}

image-20240719224016644

img

设置指定注册表项的默认值或未命名值的数据。 数据必须是文本字符串。

参数1:hKey

打开的注册表项的句柄。

参数2:lpSubKey

hKey 参数的子项的名称。 函数设置指定子项的默认值。

参数3:dwType

要存储的信息的类型。

参数4: lpData

要存储的数据。 此参数不能为 NULL

img

添加:

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
// Reg.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <windows.h>
#include <iostream>
using namespace std;
int main()
{
HKEY h361 = NULL;
LSTATUS nRet = RegCreateKey(HKEY_CURRENT_USER, "Software\\361", &h361);
if (nRet != ERROR_SUCCESS)
{
return 0;
}
char szData[] = { "the" };
nRet = RegSetValueEx(h361, "mmm", 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;
}
}

img

删除子键:

img

参数1:当前打开的父键句柄
参数2:将要删除的子键名称

img

361整个都被删除了

删除值:

img

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
// Reg.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <windows.h>
#include <iostream>
using namespace std;
int main()
{
HKEY h361 = NULL;
LSTATUS nRet = RegCreateKey(HKEY_CURRENT_USER, "Software\\361", &h361);
if (nRet != ERROR_SUCCESS)
{
return 0;
}
char szData[] = { "the" };
nRet = RegSetValueEx(h361, "mmm", 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, "mmm");
RegCloseKey(h361);//在这里下断点 运行到这里时便可以看到mmm被删除了
nRet = RegDeleteKey(HKEY_CURRENT_USER, "Software\\361");
if (nRet != ERROR_SUCCESS)
{
return 0;
}
}

image-20240719230451890

img

img

检索与打开的注册表项关联的指定值名称的类型和数据

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
// Reg.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <windows.h>
#include <iostream>
using namespace std;
#define MAX_VALUE_NAME 1024
void QueryKey(HKEY hKey)
{
CHAR achKey[MAX_PATH]; // buffer for subkey name
CHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name
DWORD cchClassName = MAX_PATH; // size of class string
DWORD cSubKeys = 0; // number of subkeys
DWORD cbMaxSubKey = 0; // longest subkey size
DWORD cchMaxClass = 0; // longest class string
DWORD cValues = 0; // number of values for key
DWORD cchMaxValue = 0; // longest value name
DWORD cbMaxValueData = 0; // longest value data
DWORD cbSecurityDescriptor = 0; // size of security descriptor
FILETIME ftLastWriteTime; // last write time

DWORD i = 0, j = 0, retCode = 0, retValue = 0;
CHAR achValue[MAX_VALUE_NAME];
DWORD cchValue = MAX_VALUE_NAME;
CHAR achBuff[80];

// Get the class name and the value count.
RegQueryInfoKey(
hKey, // key handle
achClass, // buffer for class name
&cchClassName, // size of class string
NULL, // reserved
&cSubKeys, // number of subkeys
&cbMaxSubKey, // longest subkey size
&cchMaxClass, // longest class string
&cValues, // number of values for this key
&cchMaxValue, // longest value name
&cbMaxValueData, // longest value data
&cbSecurityDescriptor, // security descriptor
&ftLastWriteTime); // last write time

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);
}
}
}
}
int main()
{
HKEY h361 = NULL;
LSTATUS nRet = RegCreateKey(HKEY_CURRENT_USER, "Software", &h361);
if (nRet != ERROR_SUCCESS)
{
return 0;
}
QueryKey(h361);
}

运行结果:

img