Объясните по C++?
1. Никто не пишет на чистом WinAPI. И тем более ни к чему его изучать, нужно просто иметь представление как он работает, а какие-то конкретные решения описаны в документации - когда потребуются тогда и выучишь.
2. Для создания приложений потребуется ознакомиться с каким либо фреймворком, будь то кроссплатформенный Qt или что-то более нативное для Windows.
3. Если не касаться окошек, то чтобы выйти за рамки теоретических програм, нужно начать писать такую не теоретическую программу.
4. Чтобы понимать структутру программ на ассемблере, неплохо бы помимо C++ еще немного изучить сам Ассемблер, принципы архитектуры процессоров, приципы архитектуры приложений под конкретную ОС.
5. Дебаггинг с IDAPro в 21 веке - это экстремальщина. Если ты дебажишь свое приложение на С++  то у тебя есть его исходники, есть куча дебаггеров под конкретные языки которые прекрасно умеют дебажить по исходникам.
Установи MS VisualStudio, напиши код на С++, выдели любую строку своего кода и нажми F9(появится красная точка останова), запусти программу F5 программа остановится на этой точке останова, затем клацни по строке правой кнопкой мыши и выбери (К дизассемблированному коду ) появится ассемблерный код твоей программы.
Вот и изучай.
Ассемблер манипулирует регистрами процессора, по типу зарезервированных имен переменных, определенного размера(их ты найдешь в гугле...))).
VisualStudio X64 Ассемблерные ставки не поддерживает.
Для x86 код следующий:
//==================================================================================================
	namespace kodno {//пространство имен для неуправляемого кода.
//#pragma unmanaged //начало не_управляемого кода.
#pragma managed(push, off)//начало не_управляемого кода.
		//функция с параметрами по ссылке через знак &, позволяет вернуть значения в каждой переменной.
			inline unsigned int func_asm(unsigned int &i1, unsigned int &i2)//inline копирует функцию в место ее вызова, тем самым делает ее локальной.
		{
			unsigned int i3;//переменная для результата.
//---------------------------------------------------------------------------------------------------------------------
//вот так работает дизассемблер:(так можно вычислить как компилятор передает значения по ссылке в переменные):
		//	unsigned int i4 = i1;//для не_управляемого кода всегда нужно ссылочные переменные присваивать обычным.
		//	unsigned int i5 = i2;//₽
			  /*
			    unsigned int i4 = i1;//для не_управляемого кода всегда нужно ссылочные переменные присваивать обычным.
			    00B91477  mov         eax, dword ptr[i1]
				00B9147A  mov         ecx, dword ptr[eax]
				00B9147C  mov         dword ptr[i4], ecx
				unsigned int i5 = i2;//₽
			    00B9147F  mov         edx, dword ptr[i2]
				00B91482  mov         eax, dword ptr[edx]
				00B91484  mov         dword ptr[i5], eax
			  */
//---------------------------------------------------------------------------------------------------------------------
			_asm //начало ассемблерной вставки x86 (32-х разрядные регистры)
			{//перед тем как писать код на ассемблере , нужно сохранить регистры процессора, а перед выходом восстановить их.
				push eax//сохраняю регистр (первый зашел, последним должен выйти, коробочка стека так устроена.)
				push ebx
				push ecx
				//получение значения по ссылке:
				push [i1] //в скобочках [адрес] , значит берется значение по адресу.
                                pop ecx //восстанавливаю значение из push [i1] в регистр ecx.
				push [ecx] //так как ссылка это двойной указатель по типу двойных скобок[адрес[адрес]], то беру еще раз.
				pop eax//восстанавливаю значение из push [ecx] в регистр eax, тем самым получаю реальное значение переданное по ссылке в функцию..
				push [i2]
				pop ecx
				push[ecx]
				pop ebx
				//или так, получение значения по ссылке:
				/*mov ecx, dword ptr[i1]
				mov eax, dword ptr[ecx]
				mov ecx, dword ptr[i2]
				mov ebx, dword ptr[ecx]*/
				add eax, ebx //равносильно EAX=EAX + EBX.
				mov i3, eax //возвращаю значение в переменную.
				//
				pop ecx
				pop ebx
				pop eax//восстанавливаю регистр(первым зашел - последним вышел)
			}
			i1 = i3;//возвращаю значения по ссылкам.
			i2 = i3;
			return i3;//возвращаю значение через функцию.
		}
//#pragma managed //начало управляемого кода /CLR.
#pragma managed(pop)//конец не_управляемого кода.
	}
//-----------------------------------------------------------------------------------------------------------------------------------------
unsigned int b3 = kodno::func_asm(b1, b2);//вызов функции из не_управляемого кода.
//==================================================================================================
/CLR - это машина NET Framework у нее свой машинный код называется УПРАВЛЯЕМЫМ, для нее свой ассемблер, называется IL-Assembler.
А, чистый код выполняет железо на компьютере, этот код называется НЕУПРАВЛЯЕМЫМ.
VisualStudio С++/CLR позволяет в программе писать как НЕ_управляемый так и Управляемый КОД.
Для того чтобы использовать в Visual Studio Ассемблерный код X64 нужно его подвязывать как отдельный модуль, но для начала тренируйся с асм вставками(Есть такая среда Lazarus называется - так вот она принимает как x32 так и x64 ассемблерные вставки, а также разные синтаксисы языка ассемблер, но там Pascal, что в принципе тоже интересно. Можешь сделать так: начать с лазаруса, затем перейти на PascalABC.NET, далее перейти на С#. А, вообще смотри Ютуб на большой скорости - выработаешь молниеносное мышление, там все есть для начинающих, смотри все подряд, чем больше смотришь тем больше ассоциаций, сначало не вникай, наблюдай, понимание придет уже ко второму просмотру второго автора лекций- будет с чем сравнивать. ))) именно так, иначе твоему мозгу нечем будет реагировать.