How to use thread in c++ win 32

Pages: 12
I want to use thread in C ++ like c # then what to do
Here is my c # code

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
Thread[] threadsArray = new Thread[100];

public class Users
{
    public ListView listView1;
    public string sName;
    public string sName2;

    public void Run()
    {
        listView1.Items[0].SubItems[1].Text = sName;
        listView1.Items[0].SubItems[2].Text = sName2;

        int i = 0;
        while (true)
        {
            i += 1;
            listView1.Items[0].SubItems[3].Text = i.ToString();
            Thread.Sleep(200);
        }
    }
}

private void Form1_Load(object sender, EventArgs e)
{
    Users user = new Users();
    user.listView1 = listView1;
    user.sName = "avan";
    user.sName2 = "strong";

    int iThread = 1;
    threadsArray[iThread] = new Thread(user.Run);
    threadsArray[iThread].IsBackground = true;
    threadsArray[iThread].Start();
}
Last edited on
The thread handling is not much different in C++. I would think that this .NET stuff is better and easier done in C#. Why do you want this in c++?

Also note that GUI-elements are not thread safe. To get an idea how it could work see this:

https://stackoverflow.com/questions/661561/how-do-i-update-the-gui-from-another-thread
Hi coder777 !

.net is easy to decompile, so I am learning c ++ win32, I was struggling to figure out how to use multithreaded like c# example but all failed. Can you give me an example for me to learn
An OLD example of a thread control panel using Win32. A bit bloated, but it does work. I just tested it with Visual Studio 2019 and it still works.

ids.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// ids.h

#pragma once

#define  IDM_EXIT       10001
#define  IDM_CONTENTS   10002

#define  IDM_THREAD     10101
#define  IDM_PANEL      10102

#define  IDC_LB1        11001
#define  IDC_LB2        11002
#define  IDB_TERMINATE1 11003
#define  IDB_TERMINATE2 11004
#define  IDB_SUSPEND1   11005
#define  IDB_SUSPEND2   11006
#define  IDB_RESUME1    11007
#define  IDB_RESUME2    11008
#define  IDC_TEXT1      11009
#define  IDC_TEXT2      11010
#define  IDC_TEXT3      11011 

control_panel.rc
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
// resources definitions
#include <windows.h>
#include "ids.h"

TheMenu  MENU
{
   POPUP "&File"
   {
      MENUITEM "E&xit",    IDM_EXIT
   }
   POPUP "&Threads"
   {
      MENUITEM "&Start Threads\tF2",      IDM_THREAD
      MENUITEM "&Control Panel\tF3",      IDM_PANEL
   }
   POPUP "&Help"
   {
      MENUITEM "&Contents",   IDM_CONTENTS
   }
}

TheAccels   ACCELERATORS
{
   VK_F1,   IDM_CONTENTS,  VIRTKEY

   VK_F2,   IDM_THREAD,    VIRTKEY
   VK_F3,   IDM_PANEL,     VIRTKEY
}

TheDB DIALOG 20, 20, 170, 140
CAPTION  "Thread Control Panel"
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
{
   DEFPUSHBUTTON  "Change", IDOK, 80, 105, 33, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP
   PUSHBUTTON  "Done", IDCANCEL, 15, 120, 33, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP

   PUSHBUTTON  "Terminate 1", IDB_TERMINATE1, 10, 10, 42, 12, WS_CHILD | WS_VISIBLE | WS_TABSTOP
   PUSHBUTTON  "Suspend 1", IDB_SUSPEND1, 10, 25, 42, 12, WS_CHILD | WS_VISIBLE | WS_TABSTOP
   PUSHBUTTON  "Resume 1", IDB_RESUME1, 10, 40, 42, 12, WS_CHILD | WS_VISIBLE | WS_TABSTOP

   LISTBOX  IDC_LB1, 65, 11, 63, 42, LBS_NOTIFY | WS_VSCROLL | WS_VISIBLE | WS_BORDER | WS_TABSTOP

   CTEXT "Thread 1", IDC_TEXT1, 135, 22, 30, 18

   PUSHBUTTON  "Terminate 2", IDB_TERMINATE2, 10, 60, 42, 12, WS_CHILD | WS_VISIBLE | WS_TABSTOP
   PUSHBUTTON  "Suspend 2", IDB_SUSPEND2, 10, 75, 42, 12, WS_CHILD | WS_VISIBLE | WS_TABSTOP
   PUSHBUTTON  "Resume 2", IDB_RESUME2, 10, 90, 42, 12, WS_CHILD | WS_VISIBLE | WS_TABSTOP

   LISTBOX  IDC_LB2, 65, 61, 63, 42, LBS_NOTIFY | WS_VSCROLL | WS_VISIBLE | WS_BORDER | WS_TABSTOP

   CTEXT "Thread 2", IDC_TEXT2, 135, 73, 30, 18

   CTEXT "Thread Priority", IDC_TEXT3, 65, 0, 64, 10
}

continued.....
control_panel.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
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
// using a thread control panel

#include	<windows.h>
#include "ids.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);
DWORD WINAPI     TheThread1(LPVOID);
DWORD WINAPI     TheThread2(LPVOID);

const int MAX = 5000000;

HWND hThread1 = NULL;
HWND hThread2 = NULL;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int nWinMode)
{
   const TCHAR szWinName[ ]  = TEXT("TCP");
   const TCHAR szAppTitle[ ] = TEXT("Using A Thread Control Panel");
   HACCEL      hAccel;
   HWND        hwnd;
   MSG         msg;
   WNDCLASS    wc;

   wc.hInstance     = hInstance;
   wc.lpszClassName = szWinName;
   wc.lpfnWndProc   = WndProc;
   wc.style         = 0;
   wc.hIcon         = (HICON) LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
   wc.hCursor       = (HCURSOR) LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
   wc.lpszMenuName  = TEXT("TheMenu");
   wc.cbClsExtra    = 0;
   wc.cbWndExtra    = 0;
   wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);

   if (RegisterClass(&wc) == 0)
   {
      MessageBox(NULL, TEXT("Couldn't Register the Window Class!"), TEXT("ERROR"), MB_OK | MB_ICONERROR);
      return 0;
   }

   hwnd = CreateWindow(szWinName, szAppTitle,
                       WS_OVERLAPPEDWINDOW,
                       CW_USEDEFAULT, CW_USEDEFAULT,
                       CW_USEDEFAULT, CW_USEDEFAULT,
                       NULL, NULL, hInstance, NULL);

   if (hwnd == NULL)
   {
      MessageBox(NULL, TEXT("Couldn't Create the Main Window!"), TEXT("ERROR"), MB_OK | MB_ICONERROR);
      return 0;
   }

   hAccel = LoadAccelerators(hInstance, TEXT("TheAccels"));

   ShowWindow(hwnd, nWinMode);
   UpdateWindow(hwnd);

   while (GetMessage(&msg, NULL, 0, 0))
   {
      if (TranslateAccelerator(hwnd, hAccel, &msg) == 0)
      {
         TranslateMessage(&msg);
         DispatchMessage(&msg);
      }
   }

   return (int) msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   static DWORD threadID1;
   static DWORD threadID2;
   int          response;

   switch (message)
   {
   case WM_COMMAND:
      switch (LOWORD(wParam))
      {
      case IDM_EXIT:
         response = MessageBox(hwnd, TEXT("Quit the Program?"), TEXT("Exit"), MB_YESNO);

         if (response == IDYES)
         {
            PostQuitMessage(0);
         }
         return 0;

      case IDM_CONTENTS:
         MessageBox(hwnd, TEXT("Demonstrates Threads"), TEXT("Help"), MB_OK);
         return 0;

      case IDM_THREAD:
         if (hThread1 == NULL)
         {
            hThread1 = (HWND) CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) TheThread1, (LPVOID) hwnd, 0, &threadID1);
         }

         if (hThread2 == NULL)
         {
            hThread2 = (HWND) CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) TheThread2, (LPVOID) hwnd, 0, &threadID2);
         }
         return 0;

      case IDM_PANEL:
         DialogBox((HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE), TEXT("TheDB"), hwnd, (DLGPROC) DlgProc);
         return 0;
      }
      break;

   case WM_DESTROY:
      PostQuitMessage(0);
      return 0;
   }

   return DefWindowProc(hwnd, message, wParam, lParam);
}

DWORD WINAPI TheThread1(LPVOID param)
{
   TCHAR str[255];
   HDC   hdc;

   hdc = GetDC((HWND) param);

   for (int i = 0; i < MAX; i++)
   {
      wsprintf(str, TEXT("Thread #1: %5d   "), i);
      TextOut(hdc, 4, 1, str, lstrlen(str));
   }

   ReleaseDC((HWND) param, hdc);
   return 0;
}

DWORD WINAPI TheThread2(LPVOID param)
{
   TCHAR str[255];
   HDC   hdc;

   hdc = GetDC((HWND) param);

   for (int i = 0; i < MAX; i++)
   {
      wsprintf(str, TEXT("Thread #2: %5d   "), i);
      TextOut(hdc, 4, 21, str, lstrlen(str));
   }

   ReleaseDC((HWND) param, hdc);
   return 0;
}

control_panel.cpp continued.....
control_panel.cpp continued:
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
LRESULT CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
   const int   NUMPRIORITIES = 5;
   const int   OFFSET = 2;
   static BOOL suspend1 = FALSE;
   static BOOL suspend2 = FALSE;
   static int  threadPriority1;
   static int  threadPriority2;
   DWORD       exitCode1;
   DWORD       exitCode2;
   long        i;
   TCHAR       priorities[NUMPRIORITIES][80] = { TEXT("Lowest"), TEXT("Below Normal"),
                                                 TEXT("Normal"), TEXT("Above Normal"),
                                                 TEXT("Highest") };

   switch (message)
   {
   case WM_INITDIALOG:
      for (i = 0; i < NUMPRIORITIES; i++)
      {
         SendDlgItemMessage(hDlg, IDC_LB1, LB_ADDSTRING, (WPARAM) 0, (LPARAM) priorities[i]);
         SendDlgItemMessage(hDlg, IDC_LB2, LB_ADDSTRING, (WPARAM) 0, (LPARAM) priorities[i]);
      }

      // get current thread priorities
      threadPriority1 = GetThreadPriority(hThread1) + OFFSET;
      threadPriority2 = GetThreadPriority(hThread2) + OFFSET;

      // update the list boxes
      SendDlgItemMessage(hDlg, IDC_LB1, LB_SETCURSEL, (WPARAM) threadPriority1, (LPARAM) 0);
      SendDlgItemMessage(hDlg, IDC_LB2, LB_SETCURSEL, (WPARAM) threadPriority2, (LPARAM) 0);

      // set suspend and resume buttons for the first thread
      if (suspend1 == TRUE)
      {
         EnableWindow(GetDlgItem(hDlg, IDB_SUSPEND1), FALSE);
         EnableWindow(GetDlgItem(hDlg, IDB_RESUME1), TRUE);
      }
      else
      {
         EnableWindow(GetDlgItem(hDlg, IDB_SUSPEND1), TRUE);
         EnableWindow(GetDlgItem(hDlg, IDB_RESUME1), FALSE);
      }

      // set suspend and resume buttons for the first thread
      if (suspend2 == TRUE)
      {
         EnableWindow(GetDlgItem(hDlg, IDB_SUSPEND2), FALSE);
         EnableWindow(GetDlgItem(hDlg, IDB_RESUME2), TRUE);
      }
      else
      {
         EnableWindow(GetDlgItem(hDlg, IDB_SUSPEND2), TRUE);
         EnableWindow(GetDlgItem(hDlg, IDB_RESUME2), FALSE);
      }

      // have both the threads been terminated?
      // Then disable a whole lot of buttons
      GetExitCodeThread(hThread1, &exitCode1);
      GetExitCodeThread(hThread2, &exitCode2);

      if ((exitCode1 != STILL_ACTIVE) && (exitCode2 != STILL_ACTIVE))
      {
         EnableWindow(GetDlgItem(hDlg, IDB_TERMINATE1), FALSE);
         EnableWindow(GetDlgItem(hDlg, IDB_SUSPEND1), FALSE);
         EnableWindow(GetDlgItem(hDlg, IDB_RESUME1), FALSE);
         EnableWindow(GetDlgItem(hDlg, IDC_LB1), FALSE);

         EnableWindow(GetDlgItem(hDlg, IDB_TERMINATE2), FALSE);
         EnableWindow(GetDlgItem(hDlg, IDB_SUSPEND2), FALSE);
         EnableWindow(GetDlgItem(hDlg, IDB_RESUME2), FALSE);
         EnableWindow(GetDlgItem(hDlg, IDC_LB2), FALSE);

         EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
      }

      return 1;

   case WM_COMMAND:
      switch (wParam)
      {
      case IDB_RESUME1:
         ResumeThread(hThread1);
         EnableWindow(GetDlgItem(hDlg, IDB_SUSPEND1), TRUE);
         EnableWindow(GetDlgItem(hDlg, IDB_RESUME1), FALSE);
         suspend1 = FALSE;
         return 1;

      case IDB_SUSPEND1:
         SuspendThread(hThread1);
         EnableWindow(GetDlgItem(hDlg, IDB_SUSPEND1), FALSE);
         EnableWindow(GetDlgItem(hDlg, IDB_RESUME1), TRUE);
         suspend1 = TRUE;
         return 1;

      case IDB_TERMINATE1:
         TerminateThread(hThread1, 0);
         EnableWindow(GetDlgItem(hDlg, IDB_SUSPEND1), FALSE);
         EnableWindow(GetDlgItem(hDlg, IDB_RESUME1), FALSE);
         EnableWindow(GetDlgItem(hDlg, IDC_LB1), FALSE);

         // are both thread dead?  If so, disable the Change button
         GetExitCodeThread(hThread2, &exitCode2);
         if (exitCode2 != STILL_ACTIVE)
         {
            EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
         }
         return 1;

      case IDB_RESUME2:
         ResumeThread(hThread2);
         EnableWindow(GetDlgItem(hDlg, IDB_SUSPEND2), TRUE);
         EnableWindow(GetDlgItem(hDlg, IDB_RESUME2), FALSE);
         suspend2 = FALSE;
         return 1;

      case IDB_SUSPEND2:
         SuspendThread(hThread2);
         EnableWindow(GetDlgItem(hDlg, IDB_SUSPEND2), FALSE);
         EnableWindow(GetDlgItem(hDlg, IDB_RESUME2), TRUE);
         suspend2 = TRUE;
         return 1;

      case IDB_TERMINATE2:
         TerminateThread(hThread2, 0);
         EnableWindow(GetDlgItem(hDlg, IDB_SUSPEND2), FALSE);
         EnableWindow(GetDlgItem(hDlg, IDB_RESUME2), FALSE);
         EnableWindow(GetDlgItem(hDlg, IDC_LB2), FALSE);

         // are both thread dead?  If so, disable the Change button
         GetExitCodeThread(hThread1, &exitCode1);
         if (exitCode1 != STILL_ACTIVE)
         {
            EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
         }
         return 1;

      case IDOK:
         // now, actually change the thread priorities
         threadPriority1 = (int) SendDlgItemMessage(hDlg, IDC_LB1, LB_GETCURSEL, (WPARAM) 0, (LPARAM) 0);
         SetThreadPriority(hThread1, threadPriority1 - OFFSET);

         threadPriority2 = (int) SendDlgItemMessage(hDlg, IDC_LB2, LB_GETCURSEL, (WPARAM) 0, (LPARAM) 0);
         SetThreadPriority(hThread2, threadPriority2 - OFFSET);
         return 1;

      case IDCANCEL:
         EndDialog(hDlg, 0);
         return 1;
      }
   }
   return 0;
}

I cringe when I look at that old code, there are so many things I would change and do differently now.
One word of warning, suspending or terminating a thread from the control panel dialog causes the app to go all loopy. Don't do it! Looks like Win10 changed a few things in the API.

You can change the priority of the threads, though.
I don't exactly follow your C# code Ba Ranh as I don't do C#, but I could post another long winded demo app like Furry Guy's above. The only types of Win32 multi-threaded apps I'm familiar with are ones in which there is a main GUI thread and a non-GUI 'worker thread' which, however, interacts with the GUI thread. I have always noted there is a lot of fear mongering about this topic. The only difficulty I have ever had in testing multi-threaded apps is coming up with realistic test data that actually tests real world situations.
closed account (z05DSL3A)
Just a thought...

Try Microsoft Docs:
Multithreading Support for Older Code (Visual C++)
https://docs.microsoft.com/en-us/cpp/parallel/multithreading-support-for-older-code-visual-cpp?view=msvc-160
This is the C++ code which I tried
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
#include "wx\/wx.h"
#include <wx/listctrl.h>
#include <windows.h> 
#include <stdio.h>

#include<conio.h>

#define HAVE_STRUCT_TIMESPEC
#include <pthread.h>

using std::string;

class cMain : public wxFrame
{ 
public:
	cMain();
	~cMain();

public:
	wxListView *Listview1 = nullptr;
};

class Thread
{
public:

	static wxListView *Listview1;

	static void *Run(void *arg)
	{
		int i = 0;
		while (true)
		{
			i += 1;
			Listview1->SetItem(0, 0, std::to_string(i));

			wxThread::Sleep(200);
		}
		
		pthread_exit(NULL);

		return 0;
	}
};

int main()
{

	pthread_t my_thread;

	int ret;
	Thread t;

	t.Listview1 = cMain::Listview1;
	ret = pthread_create(&my_thread, NULL, t.Run, &t);
	}
}

t.Listview1 = cMain::Listview1;
Error : unresolved external symbol "public: static class wxListView * Thread::Listview1" (?Listview1@Thread@@2PAVwxListView@@A)
First,
#include "wx\/wx.h" is very weird, probably not portable.
Just do: wx/wx.h, which is portable.

Your Thread class has a static variable, so this needs to traditionally be defined outside of the class itself (or with the inline keyword as of C++17).

For example, this code will give a similar linker error:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Example program
#include <iostream>

// Just for demonstration
class wxListView { };

namespace cMain
{
    wxListView* ListView1;
};

class Thread
{
public:
	static wxListView *Listview1;
};


int main()
{
	Thread::Listview1 = cMain::ListView1;
}


To solve it, you can do:
1
2
3
4
5
6
7
class Thread
{
public:
	static wxListView *Listview1;
};

wxListView* Thread::Listview1 = nullptr;


However, the design of your code is a bit odd. Your Thread class doesn't really serve a purpose, because of all its members are static.

You could just have Run be a free function, and pass in cMain::Listview1, and then cast the void* arg back to a wxListView*.
Last edited on
I still try many ways but it doesn't work, can anyone give me an example.
Did you try Furry Guy's example? I could post mine, but its about twice as big as FurryGuy's. If you didn't try his - no sense in posting mine.
closed account (z05DSL3A)
Ba ranh,

It may be worth giving us some idea of your ability with C++ and WinAPI.

Your last post looks like you are try wxWidgets, is that the path you are wanting to go down or was that just some code you grabbed of the net?
Last edited on
@freddie1 : I tried the code of Furry Guy and it doesn't work, I want a brief example that's easy to understand since I just learned C ++ almost 2 months ago.

@Grey Wolf : That's right, I am using the wxWidgets library to create an interface for my program, the code is to write and follow the instructions of wxWidgets by myself.
What compiler are you using? What Win OS version?

As I said I compiled my OLD and outdated example on Win 10 and used Visual Studio 2019 originally. It uses older Win32 thread functions, CreateThread for one. There are newer thread functions in the C Run-time (CRT). _beginthread/_beginthreadex and others.

https://docs.microsoft.com/en-us/cpp/parallel/multithreading-with-c-and-win32?view=msvc-160

I tried my old code sample using Orwell's Dev-C++, a years-out-of-date IDE, and Code::Blocks. Both had the same issues as the VS attempt.

Win10 apparently doesn't like the outdated Win32 CRT thread functions even if the code compiles without a hitch. The code example was from my Win98 days.

You want an easy example? I suggest looking at the C++ <thread> library instead of Win32.
https://en.cppreference.com/w/cpp/thread

I just learned C ++ almost 2 months ago.

Learning threads is certainly not an easy topic for a beginner IMO. The Win32 API doesn't make it any easier. C++ and Win32 are two different programming ecosystems. Console vs. GUI apps.
Sorry Furry Guy

In the past I used C#, everything was very simple, but I found that c # is easy to decompile so I determined to learn C++.
I use <thread> and <pthread.h> libraries but both fail to assign wxListView * Listview1 to cMain class to run another thread

I struggled forever but failed
I use win 10 pro, visual studio 2015
I use win 10 pro, visual studio 2015

Win10 works, VS 2015 I would recommend be replaced by VS 2019. 2015 will never by updated to C++20 or later. IIRC 2015 isn't C++17 compliant as well.

VS 2019 does get frequent updates as MS adds the features to the C++ compiler now that the standard is finalized.

Not that this will be a major issue as you continue learning C++. The language core worked before C++20.

In the past I used C#, everything was very simple

Of course, because C# is a MASSIVE framework that does a lot of the nitty-gritty detail work of dealing with Windows for you.

The Win32 API requires YOU, the programmer, to do a lot the messy work that C# previously did for you. C++ requires a similar attention to detail level of work.

You are just beginning to learn C++. Learning Win32 OR modern C++ can a HUGE undertaking that can take years to learn and master. Trying to learn BOTH at the same time is going to lead to problems. Very frustrating problems that have no easy solutions.

C++ apps are console based apps. No GUI. Current standard C++ requires 3rd party libraries to do GUI.

If you want to learn a lot of the basics of C++ from the ground up an online tutorial can be helpful. Learn C++.
http://www.learncpp.com/

Not everything the C++ standard has is explained at Learn C++. Threads is one of the "not explained" areas.

A good but outdated tutorial for the very basics of Win32 API programming can be found here:
http://www.winprog.org/tutorial/

I am a hobbyist programmer. I've been self-teaching programming before there was a standard C++, C++98. Back when it was Win95/98. When online resources like tutorials weren't available.

My programming skills certainly are not battle tested as would happen to a professional programmer.
A good resource for the WIN API is: http://www.catch22.net/tuts/
Before I get into this with a long post, let me ask:

Have you considered C++ std::thread?

It's part of modern C++ (11 forward), it is portable, and is more likely to be compatible with wxWidgets, itself a portable library not bound to Windows.

Any Windows specific threading is going to be contrary to wxWidgets, whereas that library does attempt to conform to the emerging C++ standards, and most of it portable to all operating systems supporting threads.

Threading isn't simple C++ anyway. 2 months of study in C++ isn't really sufficient to grasp the language, so this is jumping ahead a bit too fast.
Pages: 12