Нечто Страшное 1 год назад
Родитель
Сommit
ebf95f66c5
1 измененных файлов с 1570 добавлено и 0 удалено
  1. 1570 0
      Системное программирование 41 П.docx

+ 1570 - 0
Системное программирование 41 П.docx

@@ -0,0 +1,1570 @@
+
   Введение в системное программирование 
+Системой называется отношение двух множеств.  множество элементов и множество связей между этими элементами.
+Программы называется набор инструкций,  понятных для конечного исполнителя.
+Программный код -  это это зашифрованная версия программы; То есть набор инструкций, написанный на языке,  понятном постановщику задачи.
+Транслятор называется средство,  которое переводит инструкции с языка постановщика на язык исполнителя.
+Компилятор переводит все инструкции сразу, А интерпретатор осуществляет построчный перевод.
+Программирование - Это процесс написания программного кода.
+Особенности работы программиста заключаются в специфическом стиле мышления, Который необходим для написания программ. 
+Системные программисты пишут программные коды для функционирования системы 
+Операционная система Выступает посредником между пользователем и аппаратной частью компьютера.
+Следовательно свойства операционной системы можно разделить на две группы:
+  oo   Машинно-зависимые свойства. Критичны по отношению к архитектуре 
+     oo      Управление памятью
+     oo       управление аппаратными прерываниями
+     oo       управление вводом выводом
+     oo       выделение ресурсов процессам
+     oo      и т.п.
+  oo   Машинно-независимые свойства 
+     oo      Управление потоками данных
+     oo       защита данных
+     oo       планирование заданий 
+     oo      и т.п.
+Качественная операционная система должна также обладать следующим набором свойств:
+  oo   Надежность. Минимальное количество сбоев, А также наличие механизмов диагностирования ошибок.
+  oo   Защищённость. Система должна защищать процессы от негативного влияния друг на друга (а также от процессов извне).
+  oo   Предсказуемость. Поведение системы можно спрогнозировать. Выполнение типовых операций должно приводить к типовому результату
+  oo   Удобство.  Выполнение типовых операций должно затрачивать оптимальное количество ресурсов пользователя
+  oo   Эффективность. Выполнение типовых операций должно затрачивать оптимальное количество ресурсов системы 
+  oo   Оптимальное количество сервисов. Возможности системы должны покрывать базовые потребности среднестатистического пользователя. 
+  oo   Гибкость. Параметры системы могут быть адаптированы под потребности конкретного пользователя.
+  oo   Расширяемость. Наращивание функционала системы мы без ущерба для базового функционала 
+  oo   Ясность. Механизмы работы системы должны быть доступны для восприятия пользователем. 
+        Основные типы операционных систем
+  1.   Системы пакетной обработки данных 
+     oo      Пакет это последовательность команд,  Объединённых в одну логическую структуру.
+     oo      Оператор загружает пакет данных в систему,  система обрабатывает этот пакет данных И выдает новый пакет с результатом
+     oo      Недостатком такой системы является невозможность пользователя взаимодействовать с ней во время вычислений. Поэтому невозможно различить ситуацию зависания программы и работы программы (Отследить Прогресс выполнения).
+     oo      Другим недостатком является невозможность распараллеливания операций 
+  2.   Системы реального времени. Создают имитацию параллельности выполнения процессов.
+     oo      Здесь на выполнение каждого процесса отводится определенный квант времени.Затем инициируется аппаратное прерывание и и начинается отсчет нового Кванта 
+  3.   Многопроцессорные системы. Сочетают в себе несколько логических или физических процессов.
+     oo      Каждый процессор может работать как в режиме реального времени,  так и в режиме пакетной обработки.
+  4.   Система мульти программирования. Позволяет оптимизировать затраты системных ресурсов даже в 1 поточном режим 
+  5.   Сетевые ос используют удалённый сервер для обработки данных.
+
+Общие принципы взаимодействия пользователя с операционной системой
+Взаимодействие осуществляется через интерфейс пользователя (это часть операционной системы).
+Например выделяют графический или консольный интерфейс.
+Интерфейс Может видоизменяться в зависимости от категории пользователей. 
+
+Назначение и использование современных ПК 
+Назначение компьютера -  это автоматизация процессов,  которые раньше выполняли с человеком.
+Использование компьютера -  Это работа с программным обеспечением 
+
+Программное обеспечение делится на три категории:
+  oo   Прикладное. Используется для решения прикладных задач. 
+  oo   Системное. Используется для решения системных задач.
+  oo   Инструментальная. Используется для разработки программного обеспечения.
+
+С этой позицией операционная система выступает совокупностью прикладных и системных программ.
+
+В целом операционная система представляет из себя множество процессов. Каждый процесс это отдельно взятая запущенная программа.
+Программа представляет из себя набор инструкций. Инструкция разделяется на две части: Область данных и область управления.
+В ходе работы процессы потребляют ресурсы. Или, другими словами, ресурс -  это то,  что Необходимо процессу для работы.
+
+Ресурсы бывают делимые и неделимые.
+Неделимые могут быть использованы только одним процессом. Делимые используется несколькими процессами одновременно или параллельно.
+Основные ресурсы,  необходимые для работы процессов можно посмотреть в Диспетчере задач.
+Потребление процессора.  
+Метрикой,  который отвечает за потребление процессора является процессор на и время. Процессорное время -  это количество тактов, которые необходимо для завершения процесса.
+Однако на практике данные характеристика не является самой существенной. Гораздо важнее отслеживать процент загрузки процессора (Это-то Какое количество процессорного времени потребляет каждый процесс от максимально возможного)
+Потребление оперативной памяти.
+Данный показатель измеряется в мегабайтах. С практической точки зрения важно именно количественное соотношение (Это связано с подходами к динамическому выделению памяти).
+
+Также интерес Могут представлять потребление сетевых ресурсов,  частота обращения к внешнему накопителю и др.
+
+Основная задача оптимизации программного кода сводятся непосредственно к к уменьшению процессорного времени и количеству потребляемой памяти при сохранении функционала.
+Парадигма мультипрограммирования Способствует оптимизации потребление ресурсов,  что сокращает время выполнения программ.
+В однопрограммном режиме все процессы выполняются последовательно друг за другом.  таким образом суммарное время на выполнение данных процессов увеличивается.
+ в мультипрограммном режиме используется имитация параллельности работы (Пока один процесс загружает данные,  другой выполняется. И пока к первый процесс выполняется, другой загружает данные).
+Мультипрограммный подход сокращает совокупное время выполнения процессов,  Однако каждый процесс По отдельности выполняется дольше. 
+
+Отсюда можно понять,  что механизм выделения ресурсов процессам зависит от ситуации. За распределение ресурсов отвечает специальный компонент операционной системы.
+
+Ресурс может быть выделен в следующих случаях: 
+  oo   Если он свободен. И отсутствуют запросы к нему от процессов с более высоким приоритетом.
+  oo   Если этот ресурс допускает совместное использование
+  oo   Если он занят процессом с низшим приоритетом
+   
+Ресурсы бывают не только аппаратного,  но и программного характера.
+Например программные модули.
+Программные модули могут быть также делимыми, неделимыми. Но их ограничение на неделимость достигается программным путём (Программные ресурсы априори делимые).
+Поэтому их Обычно классифицируют по возможности повторного доступа. Они делятся на привилегированные,  не привилегированные и реентерабельные.
+
+Привилегированные модули выполняется без прерываний. 
+Непривилегированные модули могут быть прерваны в любой момент.
+Рентабельные модули имеют определенные точки прерывания. Они занимаются в промежуточную позицию между привилегированными и непривилегированными.
+ 
+
+        Оптимизация потребления ресурсов
+Ресурсы компьютера всегда конечные. Поэтому всегда стоит задача оптимизации их количества (Чтобы 1этаж задача выполнялась с использованием меньшего количества ресурсов). 
+Процессорное время может быть сэкономлено путем оптимизации алгоритмов.
+Экономия памяти производится за счет уменьшения количества переменных. Однако в данном случае приходится увеличивать количество операций присваивания,  что увеличивает процессорное время.
+Поэтому в рамках одного процесса экономия процессорного времени и памяти являются взаимно обратными операциями.  
+Однако в рамках системы мы можно сэкономить общее количество потребляемой памяти. Это достигается за счет динамического выделения памяти.
+Объём где я ими памяти под переменную зависит от её типа.  статические переменные занимает оперативную память пока процесс не завершится.
+
+                Указатель как тип данных 
+
+Указатель представляет из себя переменную,  значением которой является адрес другой переменной.
+Под указатель тоже выделяется место в оперативной памяти. Количество байт = разрядности программы.
+Переменные однозначно идентифицируется двумя параметрами: Это её адрес (Номер 1 ячейки памяти) и Количество занимаемых ячеек.
+   int a = 10;
+	int* p = &a;//в качестве значения указателя выступает адрес переменной
+
+К указателям применима операции сложения и вычитания. Они интерпретируются как сдвиги на определённое количество ячеек памяти. Шаг равен количество байтов,  на которые ссылается указатель.
+
+                     Массивы и указатели 
+Фактически массив является указателем на последовательность элементов определенного типа. Имя массива является указателем на первый элемент. А объём памяти равен суммарному объему Всех элементов с учетом их типа.
+Пример:
+#define N 10
+int a[N];
+int main(void)
+{
+	system("chcp 1251>nul");
+	for (int i = 0; i < N; i++)
+	{
+		*(a+i) = i * i;
+	}
+	for (int i = 0; i < N; i++)
+	{
+		printf("%d ", *(a+i));
+	}
+	return 0;
+}
+
+           Динамическое выделение памяти 
+За динамическое выделение памяти отвечает функция malloc();
+В качестве аргумента данная функция принимает количество байт,  которые нужно выделить.
+За освобождение памяти отвечает функция free().  в качестве аргумента функция принимает указатель:
+   int* p = malloc(4);
+	*p = 10;
+	printf("%d", *p);
+	free(p);
+
+Функция sizeof()  принимает в качестве аргументов идентификатор типа,  А возвращается количество байт, которое он занимает.
+
+Пример создания динамического массива:
+int n;
+	printf("введите размерность массива\n");
+	scanf("%d", &n);
+	int* a=malloc(n*sizeof(int));
+	for (int i = 0; i < n; i++)
+	{
+		a[i] = i * i;
+	}
+	for (int i = 0; i < n+5; i++)
+	{
+		printf("%d ",a[i]);
+	}
+	free(a);
+                                       
+                     Указатели и функции 
+Указатели можно передавать как аргументы функции.  также Функция может возвращать указатель. пример:
+void f1(int* a)
+{
+	*a = (*a) + 10;
+}
+int main(void)
+{
+	system("chcp 1251>nul");
+	int n = 10;
+	printf("%d\n", n);
+	f1(&n);
+	printf("%d\n", n);	
+	return 0;
+}
+                   Обобщенные указатели 
+Обобщенные указатели могут ссылаться на любой тип данных. Однако для обращения указателю его необходимо в явном виде привезти к тому типу,  на который он ссылается. пример:
+   int i = 100500;
+	char c = 'W';
+	float f = 123.456;
+	void* p;
+	p = &i;
+	printf("%d\n", *(int*)p);
+	p = &c;
+	printf("%c\n", *(char*)p);
+	p = &f;
+	printf("%f\n", *(float*)p);
+                                       
+                  Указатель на указатель 
+Поскольку показатель также является переменной,  то она тоже имеет адрес.  соответственно этот адрес можно сохранить в значении другого указателя.
+указатель на указатель объявляется с двумя звездочками.
+
+Пример массива указателей:	int i = 100500;
+	int n = 123456;
+	int* p[2] = {&i,&n};	
+	printf(" %d %d", *p[0], *p[1]);
+
+           Оператор переименования типов
+
+typedef int* p_int;
+typedef p_int* pp_int;
+int main(void)
+{
+	system("chcp 1251>nul");
+	int i = 100500;	
+	p_int  p = &i;
+	pp_int pp = &p;	
+	printf("%d %d %d", i,*p,**pp);
+	return 0;
+}
+
+           Указатели и двумерные массивы 
+Двумерный массив определяется как линейный массив из указателей,  каждый из которых также является линейным массивом.
+int main(void)
+{
+	system("chcp 1251>nul");
+	printf("введите размерность массива ");
+	int n;
+	scanf("%d", &n);//ввели размерность массива
+	int** pp = malloc(n * sizeof(int));//выделилил память под линейный массив указателей
+	int** pp1 = pp;//сохраняем "голову" в двумерном массиве (запомнили, где начало массива)
+	int* p;
+	//алгоритм заполнения динамического двумерного массива
+	for (int i = 0; i < n; i++) 
+	{
+		*pp = malloc((i + 1) * sizeof(int));
+		p = *pp;//запомнили положение головы во вложенном массиве
+		for (int j = 0; j <= i; j++)
+		{
+			**pp = j;
+			(*pp)++;
+		}
+		*pp = p;//вернули положение головы обратно
+		pp++;
+	}
+	pp = pp1;//вернули "голову" на место
+	//алгоритм вывода динамического двумерного массива
+	for (int i = 0; i < n; i++)
+	{		
+		p = *pp;//запомнили положение головы во вложенном массиве
+		for (int j = 0; j <= i; j++)
+		{
+			printf("%d ", **pp);
+			(*pp)++;
+		}
+		printf("\n");
+		*pp = p;//вернули положение головы обратно
+		pp++;
+	}
+	pp = pp1;//вернули "голову" на место
+	//очищаем выделенную под массив память
+	for (int i = 0; i < n; i++)
+	{		
+		free(pp[i]);//	очистили внутренние массивы
+	}	
+	free(pp);//очистили внешний массив
+	return 0;
+}
+тот же код, только через индексаторы:
+int main(void)
+{
+	system("chcp 1251>nul");
+	printf("введите размерность массива ");
+	int n;
+	scanf("%d", &n);//ввели размерность массива
+	int** pp = malloc(n * sizeof(int));//выделилил память под линейный массив указателей
+	//алгоритм заполнения динамического двумерного массива
+	for (int i = 0; i < n; i++) 
+	{
+		pp[i] = malloc((i + 1) * sizeof(int));		
+		for (int j = 0; j <= i; j++)
+		{
+			pp[i][j] = j;			
+		}		
+	}	
+	//алгоритм вывода динамического двумерного массива
+	for (int i = 0; i < n; i++)
+	{				
+		for (int j = 0; j <= i; j++)
+		{
+			printf("%d ", pp[i][j]);			
+		}
+		printf("\n");		
+	}	
+	//очищаем выделенную под массив память
+	for (int i = 0; i < n; i++)
+	{		
+		free(pp[i]);//	очистили внутренние массивы
+	}	
+	free(pp);//очистили внешний массив
+	return 0;
+}
+
+                    Указатель на функцию 
+Под функцию, ю.в. также как и под переменную  ю.в. выделяется память.  соответственно можно создать указатель на ту область памяти,  в которой находится эта функция.
+По аналогии с массивом, имя функции является указателем на первую ячейку памяти, в которой она находится:
+int summ(int a, int b)
+{
+	return a + b;
+}
+int razn(int a, int b)
+{
+	return a - b;
+}
+int main(void)
+{
+	system("chcp 1251>nul");
+	int (*f)(int, int);//указатель на функцию с двумя аргументами типа int и возвращающую int
+	f = razn;
+	printf("%d", f(2, 3));
+	return 0;
+}
+Передача указателя на функцию в качестве аргумента другой функции: 
+int kv(int a)
+{
+	return a*a;
+}
+int kub(int a)
+{
+	return a*a*a;
+}
+int chto_to(int (*f)(int), int n)
+{
+	return f(n);
+}
+int main(void)
+{
+	system("chcp 1251>nul");
+	printf("1 - квадрат, 2 - куб ");	
+	int (*f)(int);
+	int i=0, n=0;
+	scanf("%d %d", &i, &n);
+	switch (i)
+	{
+	case 1:
+		f = kv;
+		break;
+	case 2:
+		f = kub;
+		break;
+	default:
+		f = kub;
+		break;
+	}
+	printf("\n%d", chto_to(f,n));
+	return 0;
+}
+
+Указатель как аргумент функции:
+void kv(int* a)
+{
+	*a*=*a;
+}
+void kub(int* a)
+{
+	*a*=*a**a;
+}
+
+int main(void)
+{
+	system("chcp 1251>nul");
+	int a = 2;
+	kub(&a);
+	printf("%d", a);
+	return 0;
+}
+ Функция которая принимает и возвращает указатели:
+char* concat(char* c1, char* c2)//функция конкатенации строк
+{
+	int l1 = 0, l2 = 0;
+	while (c1[l1]!='\0')//определяем длину первой строки
+	{
+		l1++;
+	}
+	while (c2[l2] != '\0')//определяем длину второй строки
+	{
+		l2++;
+	}
+	char* c = malloc(l1+l2);//выделяем память под результирующую строку
+	for (int i = 0; i < l1; i++)//заносим посимвольно первую строку
+	{
+		c[i] = c1[i];
+	}
+	for (int i = 0; i < l2; i++)//заносим посимвольно вторую строку
+	{
+		c[i+l1] = c2[i];
+	}
+	c[l1 + l2] = '\0';//добавляем символ окончания строки
+	return c;
+}
+
+int main(void)
+{
+	system("chcp 1251>nul");
+	char c1[] = "Hello ";
+	char c2[] = { 'w','o','r','l','d','\0' };
+	char* c = concat(c1, c2);
+	printf("%s", c);
+	return 0;
+}
+                  Указатели на структуры 
+Структура также статический располагается в памяти,  поэтому на неё тоже можно создать указатель.
+Обращение к полю структуры через указатель осуществляется с помощью оператора Стрелка ->
+struct MyStruct
+{
+	int a;
+	char c;
+};
+int main(void)
+{
+	system("chcp 1251>nul");
+	struct MyStruct m = { 10,'n' };
+	struct MyStruct* p = &m;
+	printf("%d %c     %d %c", m.a, m.c, (*p).a, p->c);
+	return 0;
+}
+
+
+           Динамические структуры данных 
+Большинство данных в компьютере Можно представить в виде списка из определенных структур.
+Причём хранение осуществляется именно в виде списков,  а не в виде массивов,  поскольку массивы имеют ряд недостатков:
+  oo   Необходимо выделять большое количество последовательно идущих секторов памяти.
+  oo   Это большое количество операций переприсваивания при при работе с элементами в середине массива.
+Для того чтобы избежать эти недостатки  и используются динамические структуры данных
+             Линейный односвязный список 
+Это самая простая из существующих динамических структур.
+По сути Каждый элемент списка является структурой,  где часть полей отвечает за данные,  а часть полей за адреса таких же элементов списка.
+В односвязном списке поле с адресом только одно. 
+
+пример использования односвязных списков:
+#include <stdio.h>
+struct MyStruct
+{
+	int a;
+	struct MyStruct* next;
+};
+typedef struct MyStruct s;
+s* create(int);
+void show(s*);
+void delete(s*);
+s* insert(s*, s, int);
+int main(void)
+{
+	system("chcp 1251>nul");
+	
+	s* list1 = create(10);
+	show(list1);
+	s item = { 25,NULL };
+	list1 = insert(list1, item, 4);
+	show(list1);
+	delete(list1);	
+	return 0;
+}
+s* create(int n)
+{
+	s* start = malloc(sizeof(s));//создаем первый элемент
+	start->a = 1;
+	start->next = NULL;
+	s* p, * q;//указатели на предыдущий и следующий элемент ЛОС
+	p = start;
+	for (size_t i = 0; i < n-1; i++)//создаем в цикле все остальные элементы
+	{
+		q = malloc(sizeof(s));//инициализируем следующий элемент
+		q->a = p->a + 1;
+		p->next = q;//поле с указателем предыдущего элемента содержит адрес следующего
+		p = q;//предыдущий элемент стал следующим
+	}
+	p->next = NULL;
+	return start;
+}
+void show(s* list)
+{
+	while (list)//пока list != NULL
+	{
+		printf("%d ", list->a);
+		list = list->next;
+	}
+	printf("\n");
+}
+void delete(s* list)
+{
+	s* p = list;
+	while (p)
+	{
+		p = list->next;//запомнили следующий
+		free(list);//удалили предыдущий
+		list = p;//следующий стал первым
+	}
+}
+s* insert(s* list, s item, int k)
+{
+	s* el = malloc(sizeof(s));//выделяем память под новый элемент списка
+	el->a = item.a;//помещаем в него поле из структуры
+	if (k == 1)//если мы меняем первый элемент
+	{
+		el->next = list;
+		list = el;
+	}
+	else // если меняем не первый элемент
+	{
+		s* start = list;//запоминаем голову списка
+		for (size_t i = 0; i < k - 1; i++)
+		{
+			list = list->next;//сдвигаем на k позиций
+			if (!(list->next))
+			{
+				printf("вы ввели индекс, превышающий размеры списка. МЫ вставим элемент в конец списка\n");
+				break;
+			}
+		}
+		void* temp = list->next;//обмен адресами
+		list->next = el;
+		el->next = temp;
+		list = start;//возвращаем голову на место
+	}
+	return list;
+}
+                  Многофайловые проекты 
+Серьёзный программные продукты имеют достаточно большое количество строчек кода. Если весь программный код помещается в одном файле,  то его восприятие становится затруднительным.
+
+Поэтому логически завершенные фрагменты программного кода рекомендуется помещать в отдельные файлы. 
+В целом из фрагментов программы помещается целесообразно объявления глобальных переменных и отдельно взятой функции.
+
+Для функций,  описанных в другом файле,  перед вызовом желательно указать их прототип  (В том файле,  откуда осуществляется вызов).
+
+Для использования глобальной переменной из другого файла она также должна быть описана на этом файле,  откуда осуществляется вызов. Для выстраивания связей между переменными в исходном файле Объявленная переменная должна быть помечена Зарезервирован словом extern.
+
+
+Но в любом случае в каждом файле должны быть указаны прототипы функций,  объявление структур и описание глобальных переменных. А также операторы переопределения типов.
+
+При описании необходимо учитывать следующее:
+  oo   В глобальной области памяти допускается повторное определение, но не допускается повторная инициализация
+  oo   Внутри функций допускается повторная инициализация,  но не допускается повторное определение 
+В связи с этим имеет смысл вынести все повторно используемые фрагменты кода да в отдельный файл и подключать его по мере необходимости.
+Такие файлы называются файлами заголовков.
+
+Файлы заголовков имеет расширение .h  И добавляется в проекте стандартным способом (с помощью #include).
+Пользовательские файлы заголовков указывается в двойных кавычках.
+
+Файлы заголовков рекомендуется использовать для объявления объектов  и не рекомендуется для их инициализации.
+
+                Директивы препроцессора 
+Препроцессор -  это специальная программа,  которая осуществляет алгоритмические действия перед компиляцией основного кода.
+Команды для препроцессора называются директивами. 
+Директивы начинаются с символа #, В конце строки директивой ";" можно не ставить.
+
+Список основных директив: 
+  oo   #include. Вставляет содержимое из текстового файла в то место, где она написана 
+  oo   #define. Имеет три основных применения:
+     oo      Инициализация параметров (Задание флагов). Используется преимущественно для условной компиляции 
+     oo      Задание констант. Используется в качестве альтернативы глобальным переменным
+     oo      Задание макроопределений (макросов)
+  oo   #undef. Отменяют задание параметра
+  oo   #ifdef - Условие компиляции,  если определённый параметр задан 
+  oo   #ifndef - Условие компиляции,  если определённый параметр не задан
+  oo   #if - Инициализация условной компиляции.  далее необходимо ввести условие,  используя другие Директивы препроцессора.
+  oo   #elif - Директивы для создания вложенных условий.
+  oo   #else - Ветка при ложности всех условий. Её нельзя ставить выше чем #elif
+  oo   #error - Внесение искусственной ошибки для компиляции 
+
+       Разработка программ в системе Windows 
+Операционная система Windows может предоставлять интерфейс для выполнения определённых системных задач сторонними программами. Такой интерфейс называется WinAPI.
+WinAPI Представляет из себя набор функций,  структур и различных параметров.
+Для начала необходимо ознакомиться с типами данных,  которые используется системой. 
+
+                       Типы данных в windows
+  oo   Тип BYTE обозначает 8-разрядное беззнаковое символьное значение.
+  oo   Тип WORD  --  16-разрядное беззнаковое короткое целое.
+  oo   Тип DWORD  --  беззнаковое длинное целое.
+  oo   Тип UINT  --  беззнаковое 32-разрядное целое.
+  oo   Тип LONG эквивалентен типу long.
+  oo   Тип BOOL обозначает целое и используется, когда значение может быть либо истинным, либо ложным.
+  oo   Тип LPSTR определяет указатель на строку.
+  oo   Тип LPCSTR определяет константный (const) указатель на строку.
+  oo   Тип HANDLE обозначает 32-разрядное целое, используемое в качестве дескриптора. 
+
+Дескриптор выступает в качестве идентификатора определенного ресурса 
+
+          Функция запуска приложений Windows 
+Функция называется WinMail. Принимает 4 параметра.  они описаны ниже 
+int WINAPI WinMain(HINSTANCE hlnstance. // дескриптор, присваиваемый запущенному приложению
+HINSTANCE hPrevInstance, // для совместимости с winl6. в Win32 не используется
+LPSTR lpCmdLine. // указатель на командною строку, если приложение так запущено
+int nCmdShow); // значение, которое может быть передано в функцию Show Window ()
+
+Перед запуском приложений Windows необходимо особым образом настроить проект
+
+Также при работе с API системы Windows необходимо подключать заголовочный файл windows.h
+
+В данном случае создается системный процесс,  который не имеет интерфейса. 
+Для ввода информации обычно используются файлы или другие способы (буфер обмена, именованный канал и т.п.)
+Вывод информации также осуществляется в файл или другой источник данных.
+Также вывод может осуществляться в диалоговое окно вывода.
+
+За это отвечает функция MessageBox. Она принимает 4 параметра:
+  1.   Дескриптор окна,  который вызывает данный messagebox. Поскольку мы не работаем с оконными приложениями здесь ставим NULL
+  2.   Непосредственно текст сообщения. Если для вывода используется текст в формате юникода,  да то перед строкой ставятся префикс L 
+  3.   Заголовок окна messagebox . Это тоже может быть строка в формате ascii или Unicode 
+  4.   Набор параметров. Набор кнопок. Например кнопки OK отмена И другие. Также в качестве параметра можно вставить иконку 
+
+Файловый ввод и вывод информации в языке си 
+Файловый ввод-вывод Находится также в заголовочном файле <stdio.h>
+Типовыми функциями для работы с файлами являются следующие:
+  oo   Создание указателя на файл (Открытие файла)
+  oo    закрытие файла
+  oo    вывод информации из файла (чтение)
+  oo   Запись информации в файл
+
+Для открытия файла в системе C необходимо объявить переменную типа файл (Точнее указатель на файловый поток)
+Зам открытии файла отвечает функция fopen.
+Данная функция Принимает два параметра:
+  1.   Имя файла (путь к нему)
+  2.   Режим доступа к файлу (имеется в виду чтение или запись)
+
+Список режимов:
+
+--------------------------------------------------------------------------------
+r
+--------------------------------------------------------------------------------
+Чтение. Файл должен существовать.
+--------------------------------------------------------------------------------
+w
+--------------------------------------------------------------------------------
+Запись нового файла. Если файл с таким именем уже существует, то его содержимое будет потеряно.
+--------------------------------------------------------------------------------
+a
+--------------------------------------------------------------------------------
+Запись в конец файла. Операции позиционирования (fseek, fsetpos, frewind) игнорируются. Файл создаётся, если не существовал.
+--------------------------------------------------------------------------------
+r+
+--------------------------------------------------------------------------------
+Чтение и обновление. Можно как читать, так и писать. Файл должен существовать.
+--------------------------------------------------------------------------------
+w+
+--------------------------------------------------------------------------------
+Запись и обновление. Создаётся новый файл. Если файл с таким именем уже существует, то его содержимое будет потеряно. Можно как писать, так и читать.
+--------------------------------------------------------------------------------
+a+
+--------------------------------------------------------------------------------
+Запись в конец и обновление. Операции позиционирования работают только для чтения, для записи игнорируются. Если файл не существовал, то будет создан новый.
+Если необходимо открыть файл в бинарном режиме, то в конец строки добавляется буква b, например "rb", "wb", "ab", или, для смешанного режима "ab+", "wb+", "ab+". Вместо b можно добавлять букву t, тогда файл будет открываться в текстовом режиме. Это зависит от реализации. В новом стандарте си (2011) буква x означает, что функция fopen должна завершиться с ошибкой, если файл уже существует. Дополним нашу старую программу: заново откроем файл и считаем, что мы туда записали.
+               Работа с файлами через WinAPI 
+За создание дескриптора файла отвечает функция Createfile 
+HANDLE CreateFile(
+LPCTSTR lpFileName, // Указатель на имя файла (устройства)
+DWORD dwDesiredAccess, //Параметры доступа
+DWORD dwShareMode, //Разделяемый доступ
+LPSECURITY_ATTRIBUTES lpSecurityAttributes, //безопасность
+DWORD dwCreationDistribution,// Описание
+DWORD dwFlagsAndAttributes, // Атрибуты файла
+HANDLE hTemplateFile // Файл шаблона
+);
+
+
+
+конкретный пример открытия файла:
+//создаем текстовый файл
+    HANDLE hFile = CreateFile(PATH,//путь к файлу
+        GENERIC_READ | GENERIC_WRITE,//флаги на открытие как на чтение, так и на запись
+        FILE_SHARE_READ,//совместный доступ только на чтение
+        NULL,//структура безопасности по умолчанию
+        OPEN_ALWAYS,//режим создания файла (открыть, перезаписать и т.п.)
+        FILE_ATTRIBUTE_NORMAL,//атрибуты файла по умолчанию
+        NULL);//шаблон файла отсутствует
+
+
+Запись данных в файл:
+
+BOOL WriteFile(HANDLE hFile, //собственно указатель на файл
+LPVOID lpBuffer, // указатель на буфер - откуда записываем данные в файл
+DWORD nNumberOfBytesToWrite, //объем записываемых данных
+LPDWORD lpNumberOfBytesWrite, //фактический размер записанных данных
+LPOVERLAPPED lpOverlapped // флаг режима доступа к файлу: асинхронный(FILE_FLAG_OVERLAPPED)
+//или синхронный(NULL)
+конкретный пример записи в файл:
+ LPCSTR MyString = "Hello world";//буфер для записи (что записываем)
+ DWORD d = 0;
+//функция записи в файл
+    WriteFile(hFile,//дескриптор открытого файла
+        MyString,//указываем буфер для записи
+        strlen(MyString), //указываем, сколько байт мы хотим записать
+        &d,//передаем указатель на DWORD
+        NULL//синхронный режим записи
+    );
+Функция для чтения данных из файла:
+За это отвечает функция ReadFile. Аргументы у неё такие же как его функции WriteFile,
+Но при чтении есть некоторые особенности:
+  oo   Надо указывать заведомо большой буфер для чтения (Так как мы не знаем Сколько информации находится в файле) 
+  oo   После прочтения строку нужно закрыть (Дописать к ней символ \0)
+
+пример кода:
+ DWORD d = 0;//сколько фактически байт было прочитано
+DWORD sizeBuffer = 521;//объем буфера
+    LPSTR str = malloc(sizeBuffer+1);//куда считывать
+    ReadFile(hFile, str, sizeBuffer, &d, NULL);
+    str[d] = '\0';
+Пример со структурой Overlapped:
+ OVERLAPPED olf = { 0 }; //Структура, в которой задана позиция в файле 
+    DWORD sizeBuffer = 512;//объем буфера
+    LPSTR str = malloc(sizeBuffer+1);//куда считывать
+    ReadFile(hFile, str, sizeBuffer, &d, &olf);
+    free(str);
+    olf.Offset = 0;//задаем смещение (позицию в файле)
+    LPSTR str1 = malloc(d + 1);
+    ReadFile(hFile, str1, d, &d1, &olf);
+    str1[d1] = '\0';
+
+
+Использование динамических загружаемых библиотек 
+По мере усложнения проекта количество используемых функций возрастает.
+И не всегда использование Многофайловых проектов решает эту проблему.
+Помимо всего прочего использование множества функций в одном проекте влечет за собой следующие проблемы:
+  oo   Не все функции,  описанные в проекте единовременно могут быть использованы,  но они занимают пространство оперативной памяти и это сказывается на объёме исполняемого файла.
+  oo   Если одни и те же функции необходимо использовать в разных проектах,  то код приходится дублировать. Причём не всегда копирование кода решает эту проблему. 
+Данные проблемы Как раз-таки решается за счет подключения динамических библиотек.
+Есть и статические библиотеки (lib),  но на сегодняшний момент их стараются не использовать.
+Динамические библиотеки подключаются к проекту и отключаются в тот момент времени,  когда это надо. Таким образом достигается экономия оперативной памяти.
+
+             Создание библиотеки DLL в winapi 
+Для создания библиотеки DLL нужно добавить соответствующий проект в решение И настроить его во на запуск как динамическую библиотеку.
+Проект DLL имеет некоторые особенности.
+Он настраивается также,  как и проект winapi, Ну помимо этого ещё необходимо указать В настройках проекта на вкладке общие Тип конфигурации динамическая библиотека.
+Точка входа у библиотеки DLL тоже своя особенная:
+BOOL WINAPI DllMain(HINSTANCE hlnstDll, DWORD dwReason, LPVOID IpReserved)
+{
+	BOOL bAllWentWell = TRUE;
+	switch (dwReason)
+	{
+	case DLL_PROCESS_ATTACH:
+		break;
+	case DLL_THREAD_ATTACH:
+		break;
+	case DLL_THREAD_DETACH:
+		break;
+	case DLL_PROCESS_DETACH:
+		break;
+	}
+	if (bAllWentWell)
+		return TRUE;
+	else
+		return FALSE;
+}
+
+
+ Всем экспортируемые из DLL функции. Должны иметь специальные соглашения о вызовах _cdecl. (Дело в том что приложение winapi по умолчанию имеет соглашения вызовах __stdcall).
+Также же функции необходимо пометить с помощью специального оператора:
+__declspec(dllimport)  - для Импортируемых функций 
+
+__declspec(dllexport)  - для экспортируемых функций.
+И это всё описывается в портативе функции. Без прототипа мы её импортировать или экспортировать не сможем (Но просто писать функции в dll мы можем).
+Пример экспортируемой функции:
+__declspec(dllexport) int Hello(LPWSTR str);
+int Hello(LPWSTR str)
+{
+	MessageBox(NULL, str, L"Проверка связи", MB_OK);
+	return 0;
+}
+
+ Импорт функции из DLL в основную программу 
+Для этого используются три основных шага:
+  1.   Подключается DLL. (Создается дескриптор данной библиотеки) 
+  2.   Импортируется функция из этой библиотеки 
+  3.   Освобождается память под дескриптор (Отключается библиотека)
+
+Для подключения библиотеки используется функция LoadLibrary. В качестве аргумента этой функции передается путь к DLL. Возвращает она дескриптор HINSTANCE
+Однако если эту библиотеку загрузить не удалось, то функция возвращает NULL
+
+За Отключение библиотеки отвечает функция FreeLibrary. В качестве аргумента она получает дескриптор библиотеки.
+
+Для импорта функции необходимо сначала создать указатель на функцию с сигнатурой, Которую мы хотим вызвать.
+Также мы должны указать соглашение о вызовах.
+typedef int(__cdecl* MyFunction)(LPWSTR);
+Оператор typedef в данном случае объявляет пользовательский тип, который позволяет создать указатель на эту функцию.
+
+За инициализацию функции Из DLL Отвечает функция GetProcAddress. Она принимает два параметра.  это дескриптор DLL и строку, содержащую имя импортируемой функции. Возвращает она естественно указатель на эту функцию.
+пример вызова функции из DLL:
+#include <windows.h>
+#define PATH L"CodeDll.dll"
+typedef int(_cdecl* MyFunction)(LPWSTR);
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+    LPSTR lpCmdLine, int nCmdShow)
+{
+    HINSTANCE MyDLL;
+    if (!(MyDLL = LoadLibrary(PATH))) return 1;//подключение DLL
+    MyFunction MyF1; //создали переменную типа указатель на вызываемую функцию
+    MyF1 = (MyFunction)GetProcAddress(MyDLL, "Hello");//инициализация указателя на функцию
+    MyF1(L"Привет, мир");
+    FreeLibrary(MyDLL);//отключение DLL
+    return 0;
+}
+ Не забывайте,  что при любом изменении в исходном коде DLL,  проект нужно пересобрать (иначе сама библиотека не перекомпилируется);
+
+              Использование DLL в языке си#
+Языках высокого уровня также используется DLL.
+ DLL библиотеки Также можно разрабатывать Средствами языка си#.
+Алгоритм примерно тот же самый:
+  oo   В уже существующий проект на си# Надо добавить ещё один проект dll
+  oo   Поставить ссылку на проект с DLL
+  oo   Там где, мы хотим использовать функционал из dll Подключить соответствующее пространство имён.
+  oo   Теперь можно использовать классы и методы из dll
+Это работает Как для статических классов,  также для не статических классов и для вложенных пространств имён.
+пример кода:
+код DLL:
+namespace ClassLibrary1
+{
+    public class Class1
+    {
+        public int summ(int a,int b)
+            { return a + b; }
+    }
+    namespace SubClassLibrary1
+    {
+        public static class SubClass1
+        {
+            public static int razn(int a, int b)
+            { return a - b; }
+        }
+    }
+}
+Код основной программы:
+using ClassLibrary1;
+using ClassLibrary1.SubClassLibrary1;
+namespace ConsoleApp4
+{
+    internal class Program
+    {
+        static void Main(string[] args)
+        {
+            Class1 class1 = new Class1();//нестатический класс из DLL
+            Console.WriteLine(class1.summ(3, 6));
+ теперь            Console.WriteLine(SubClass1.razn(3, 6));//статический класс из DLL
+            Console.ReadKey();               
+        }        
+    }
+}
+
+ Подключение к проекту c# Библиотеки от winapi 
+Иногда выгоднее подключать низкоуровневые функции к работе вашей программы.
+Преимущества этого решения -  скорость выполнения самой программы.
+минус данного решения В том,  что кот становится неуправляемым. То есть встроенные средства языка си# Не смогут проанализировать этот код Ну и например предотвратить утечку памяти,  если она допускается функциями из библиотеки.
+
+Алгоритм подключения системной библиотеки примерно такой же, Как и на языке си,  но только используя синтаксис си#.
+  oo   Объявить прототип функции. Для этого нужно:
+     oo       подключить пространство имён: using System.Runtime.InteropServices;
+     oo      Использовать атрибут [DllImport]. У этого атрибута есть несколько параметров, Они передаются внутри скобок ( как у метода)
+     oo      Нужно Правильно указать соглашение о вызовах в атрибуте [DllImport] импорт.  Дело в том что по умолчанию считается __stdcall, А у нас в библиотеке __cdecl
+     oo      Далее описать сигнатуру вызываемой функции dll. Только нужно заменить типы данных ( те,  те которые используются в Windows, на те, как которые Используются в языке си #)
+пример кода.
+для DLL (WinAPI):
+#include <Windows.h>
+BOOL WINAPI DllMain(HINSTANCE hlnstDll, DWORD dwReason, LPVOID IpReserved)
+{
+	BOOL bAllWentWell = TRUE;
+	switch (dwReason)
+	{
+	case DLL_PROCESS_ATTACH:
+		break;
+	case DLL_THREAD_ATTACH:
+		break;
+	case DLL_THREAD_DETACH:
+		break;
+	case DLL_PROCESS_DETACH:
+		break;
+	}
+	if (bAllWentWell)
+		return TRUE;
+	else
+		return FALSE;
+}
+ __declspec(dllexport) int MyFunc(LPWSTR str);
+int MyFunc(LPWSTR str)
+{
+	MessageBox(NULL, str, L"Я сделяль", MB_OK);
+	return 0;
+}
+ __declspec(dllexport) int Summ(int a, int b);
+
+int Summ(int a, int b)
+{
+	return a + b;
+}
+
+для c#:
+using System.Runtime.InteropServices;//пространство имен для импорта функций из DLL
+namespace ConsoleApp4
+{
+    internal class Program
+    {
+        [DllImport(@"D:\VSProject\UsingDLLWinAPI\Debug\DLLCode.dll",CallingConvention=CallingConvention.Cdecl)]//CallingConvention - это соглашение о вызовах
+        public static extern int MyFunc(byte[] str);//подключил функцию messagebox из DLL
+        [DllImport(@"D:\VSProject\UsingDLLWinAPI\Debug\DLLCode.dll", CallingConvention = CallingConvention.Cdecl)]
+        public static extern int Summ(int a, int b);//подключил функцию суммы из DLL
+        static void Main(string[] args)
+        {
+            byte[] b = Encoding.Unicode.GetBytes("Мой текст");
+            MyFunc(b);
+            int s = Summ(2, 6);            
+            Console.ReadKey();               
+        }        
+    }
+}
+
+               Работа с многозадачностью 
+В основе парадигмы программирования лежит понятие процесс. 
+Процесс можно понимать как некий объект для исполняемого файла.  один и тот же исполняемый файл может быть запущен несколько раз (при этом создаётся несколько процессов).
+Вся информация о процессе хранится в оперативной памяти.
+Процесс характеризуется неким набором информации. За работу процессов отвечают потоки. Поток рассматривается как последовательный набор инструкций, Который выполняется на процессоре.Каждый процесс имеет хотя бы один поток. Данный поток называется основным 
+Процессы могут находиться в одном из следующих состояний:
+  oo   Выполняется. Основному потоку выделено процессорное время. Количество выполняемых процессов одновременно и не может быть больше,  чем Потоков,  который поддерживает процессор 
+  oo   Состояние готовности к выполнению. Процессу Предоставлены все ресурсы, кроме процессорного времени.
+  oo   Состояние ожидания. Процессу предоставлены не все ресурсы (Например идёт ввод или вывод данных). 
+За время своего существования один и тот же процесс может многократно менять свои состояния.
+Место процесса в очереди определяется его приоритетом.
+Приоритет процесса может поменять приоритет (в том числе и создаться) в одном из следующих случаев:
+  oo   По команде пользователя. 
+  oo   При выборе из очереди планировщиком операционной системы 
+  oo   По таймеру системному 
+  oo   По инициативе другого процесса 
+При создании процесса в него могут быть переданы аргументы.
+Для этого необходимо правильным образом описать функцию запуска 
+int main(int argc, char* argv[])
+{
+    setlocale(LC_ALL, "RUS");
+    for (size_t i = 0; i < argc; i++)
+    {
+        std::cout << argv[i] << "\n";
+    }  
+}
+Аргументы представляет из себя массив строковых значений. В примере показано,  как их вывести на экран.
+Первым аргументом ( с индексом 0)  является имя исполняемого файла.
+
+При создании процесса создаётся также его дескриптор,  который является структурой,  содержащий всю необходимую информацию о процессе.
+
+Основной поток в процессе может также порождать и вспомогательные потоки.
+Они нужны для параллельного выполнения операций. Желательно,  чтобы данные,  которые используют различные потоки не взаимодействовали между собой.
+
+Для создания потоков в winapi используется функция 
+CreateThread (NULL, //указатель на структуру безопасности (NULL по умолчанию)
+0, // размер стека (0 по умолчанию)
+func, //указатель на функцию, которая будет выполняться в потоке
+NULL, // указатель на аргумент функции потока (NULL - пустой указатель, без аргументов)
+0, // флаги создания потока. 0 - по умолчанию. 
+0)//ID потока (0 - автоматически).
+
+Функция потока должна иметь следующую сигнатуру:
+DWORD WINAPI func(LPVOID param);
+Она должна возвращать значение dword, Иметь в качестве аргумента указатель общего типа.
+   Примеры задач с использованием потоков 
+Задача: Посчитать в одном потоке факториал числа,  а в другом последовательность Фибоначчи.
+В основном потоке мы создаем 2 вспомогательных С использованием функции CreateThread.
+пример:
+HANDLE hF[2];
+    hF[0] = CreateThread(NULL, 0, TreadFactr, NULL, 0, 0);
+    hF[1] = CreateThread(NULL, 0, TreadFib, NULL, 0, 0);
+Данная функция возвращает дескриптор.  мы используем Один массив для всех дескрипторов создаваемых потоков (Это нужно для применения функции WaitForMultipleObjects, Который в качестве аргумента принимают указатель на дескрипторы).
+Структура функции WaitForMultipleObjects
+(count, //Количество потоков,  завершения которых необходимо ждать 
+hF,//Указатель на дескрипторы (Массив дескрипторов)
+TRUE, //Флаг ожидания. Истина -  будет ждать завершения всех потоков из count, Ложь -  ожидание Завершения одного любого потока.
+INFINITE)//Время ожидания завершения потока.
+
+коды функций потока:
+DWORD WINAPI TreadFactr(LPVOID param)
+{
+    int f = 0;
+    for (int i = 0; i <= n; i++)
+    {
+        if (i == 0)
+        {
+            f = 1;
+        }
+        else
+        {
+            f *= i;
+        }
+        printf("factorial %d raven %d \n", i, f);
+    }
+    ExitThread(0);
+}
+DWORD WINAPI TreadFib(LPVOID param)
+{
+    int f = 0;
+    int f1 = 1;
+    int f2 = 1;
+    for (int i = 0; i <= n; i++)
+    {
+        if (i > 1)
+        {
+            f = f1;
+            f1 = f2;
+            f2 += f;
+        }
+        printf("%d element Fibonachi raven %d \n", i, f2);
+    }
+    ExitThread(0);
+}
+Задача про управление одним потоком из другого потока:
+HANDLE hF[2];
+int work = 0;
+VOID Upravlenie(VOID)
+{
+    system("chcp 1251");
+    
+    hF[0] = CreateThread(NULL, 0, TreadWorker, NULL, 0, 0);
+    hF[1] = CreateThread(NULL, 0, TreadManager, NULL, 0, 0);
+    WaitForMultipleObjects(2, hF, TRUE, INFINITE);
+}
+
+DWORD WINAPI TreadWorker(LPVOID param)
+{//просто увеличивает значение счетчика
+    while (TRUE)
+    {
+        Sleep(100);
+        work++;        
+    }
+}
+DWORD WINAPI TreadManager(LPVOID param)
+{
+    int i;
+    while (TRUE)
+    {
+        printf("Выберите действие: \n 1-посмотреть значение счетчика \n 2-поставить рабочий поток на паузу \n 3-снять рабочий поток с паузы \n 4 - завершить все потоки \n ");
+        scanf("%d", &i);
+        switch (i)
+        {
+        case 1:
+            printf("Значение счетчика равно %d\n", work);
+            break;
+        case 2: 
+            printf("Рабочий поток поставлен на паузу\n");
+            SuspendThread(hF[0]);
+            break;
+        case 3:
+            printf("Рабочий поток снят с паузы\n");
+            ResumeThread(hF[0]);
+            break;
+        case 4:
+            printf("Все потоки завершили работу\n");
+            TerminateThread(hF[0], 0);
+            ExitThread(0);
+            break;
+        default:
+            printf("Ничего не изменилось\n");
+            break;
+        }
+    }
+}
+
+Поток имеет свой счётчик прерываний (То есть если его поставить на паузу два раза,  То есть снять с паузы надо тоже два раза). Пример:
+int i = 0;
+HANDLE hF[2];
+VOID FactrSinh(VOID)
+{
+   
+    hF[0] = CreateThread(NULL, 0, ThreadF, NULL, 0, 0);
+    hF[1] = CreateThread(NULL, 0, ThreadVivod, NULL, 0, 0);
+    WaitForMultipleObjects(2, hF, TRUE, INFINITE);
+}
+
+DWORD WINAPI ThreadF(LPVOID param)
+{ 
+    for (i = 0; i < 50; i++)
+    {
+        Sleep(500);
+        if (i >10 && i<=20) { SuspendThread(hF[1]); }
+        if (i>20) { ResumeThread(hF[1]); }
+        if (i == 49) { TerminateThread(hF[1],0); }
+    }
+    ResumeThread(hF[1]);
+    ExitThread(0);
+}
+DWORD WINAPI ThreadVivod(LPVOID param)
+{
+    while (TRUE)
+    {
+        Sleep(100);
+        printf("index raven %d \n", i);
+    }    
+    ExitThread(0);
+}
+
+                  Синхронизация потоков 
+В случае когда два или более потоков пытаются получить одновременный доступ к какому-либо общему ресурсу (Например К данным в оперативной памяти), Поведение программа может быть неверным. Значение может быть записано раньше времени или прочитано раньше времени.
+Существует несколько способов синхронизации потоков,  однако все они в той или иной мере способствуют запрещению параллельного доступа к общему ресурсу.
+Рассмотрим принцип действия критической секции.
+Критическая секция -  это участок кода,  да который фиксируется для выполнения только одним потоком (Набор инструкций,  который может выполняться только одним потоком одновременно).
+Критическая секция объявляется следующим образом. 
+  oo   Создаётся Переменная соответствующего типа 
+   CRITICAL_SECTION section = { 0 };
+   Создавать её желательно в глобальной области видимости.
+  oo   До начала выполнения потока необходимо инициализировать критическую секцию с помощью соответствующей функции:
+   InitializeCriticalSection(&section);
+  oo   Участок кода,  да который контролируется критической секцией должен находиться между инструкциями EnterCriticalSection(&section); и LeaveCriticalSection(&section);
+  oo   После окончание работы потоков можно освободить критическую секцию с помощью функции DeleteCriticalSection(&section);
+Пример кода:
+#define _CRT_SECURE_NO_WARNINGS
+#include <Windows.h>
+#include <stdio.h>
+#include <malloc.h>
+#define I_WILL_WAIT 10
+
+
+CRITICAL_SECTION section = { 0 }; //Критическая секция
+
+VOID Crit(VOID);
+DWORD WINAPI Thread1(DWORD param);
+DWORD WINAPI Thread2(DWORD param);
+
+int count = 0;
+HANDLE h[3];
+
+VOID Crit(VOID)
+{
+	DWORD tmp1 = 1;
+	DWORD tmp2 = 2;
+	DWORD tmp3 = 3;
+	InitializeCriticalSection(&section);
+	h[0] = CreateThread(NULL, 0, Thread2, tmp1, 0, 0);	
+	h[1] = CreateThread(NULL, 0, Thread2, tmp2, 0, 0);	
+	h[2] = CreateThread(NULL, 0, Thread2, tmp3, 0, 0);
+	WaitForMultipleObjects(3, h, TRUE, INFINITE);
+	DeleteCriticalSection(&section);
+	printf("count = %d\n", count);
+}
+DWORD WINAPI Thread1(DWORD param)
+{
+	for (int i=0;i<10;i++)
+	{
+		Sleep(I_WILL_WAIT);
+		count++;	
+		printf("count = %d, potok = %d\n", count, param);
+	}
+	ExitThread(0);
+}
+DWORD WINAPI Thread2(DWORD param)
+{
+	EnterCriticalSection(&section);
+	for (int i = 0; i < 10; i++)
+	{
+		Sleep(I_WILL_WAIT);			
+		count++;				
+		printf("count = %d, potok = %d\n", count, param);
+	}
+	LeaveCriticalSection(&section);
+	ExitThread(0);
+}
+
+Технологии обмена данными между процессами 
+На данный момент нам известны следующие способы:
+  oo   Использовать аргументы командной строки. Однако этот способ работает  только для создания новых процессов.  в уже запущенную программу таким способом данные мы передать не сможем 
+  oo   Использовать файлы. Однако этот способ также не лишён недостатков.  основным недостатком является скорость работы ( из-за долгого обращения к файловой системе)
+В качестве решения проблемы можно рассматривать файл,  который создан в оперативной памяти ( другими словами выделенный объем оперативной памяти,  который может использоваться совместно разными процессами).
+
+                  Каналы передачи данных 
+Канал (англ. Pipe) - Область виртуального пространства,  которое может быть использовано для совместного доступа различными процессами.
+Однако в силу этого канал не может храниться ze1 дельно от какого-то процесса (По факту дескриптор канала является глобальным указателем). Поэтому информация о нём будет очищена если завершить процесс, который его создал.
+Будем называть процесс, Который создаёт канал с сервером,  а процессы,  которые подключаются к каналу -  клиентами.
+
+Каналы имеют несколько разновидностей:
+  oo   Симплексные или дуплексные 
+     oo      Симплексный это однонаправленные. Например сервер только записывает данные,  а Клиенты только читают их. Или клиенты только пишут,  а сервер только читает.
+     oo      Дуплексные это когда и клиент и сервер может и читать и писать 
+  oo   Бинарные или текстовые. По аналогии с бинарными или текстовыми файлами 
+  oo   С общим или разделяемым доступом к содержимому
+  oo   Именованные или анонимные 
+                                       
+                       Анонимные каналы.
+В качестве их идентификатора используется дескрипторы на чтение или на запись.
+Пример функции для создания анонимного канала 
+BOOL CreatePipe (PHANDLE pliRead. // переменная для дескриптора чтения (входной канал) 
+PHANDLE phWrite. // переменная для дескриптора записи (выходной канал) 
+LPSECURITY_ATTRIBUTES Ipsa. // привилегии доступа 
+DWORD dwPipeSize); // размер буфера канала (0 по умолчанию)
+Из недостатков такого способа следует отметить,  что дескриптор канала сервер должен передать клиенту по какому-то другому пути.
+
+       Использование именованных каналов 
+Именованные каналы поем дескриптора имеют также имя в виде строки. Причём Если дескриптор каждый раз разный, то имя является константой.
+Имя является сетевым.
+Сервер создаёт именованный канал с помощью функции CreateNamedPipe():
+Она имеет следующую сигнатуру:
+HANDLE CreateNamedPipe (
+LPTSTR IpszPipeName, // строка с именем нового канала (сетевое)
+DWORD fdwOpenMode, // доступ (симплексный или дуплексный)
+DWORD fdwPipeMode, // тип, режимы чтения и ожидания 
+DWORD dwMaxInstances, // максимальное число клиентов
+DWORD dwOutBuf, // размер выходного буфера, байты
+DWORD dwInBuf, / размер входного буфера/байты
+DWORD dwTimeout, // время паузы, миллисекунды (ожидание подключения)
+LPSECURITY ATTRIBUTES Ipsa);     // структура безопасности
+
+Проверить статус подключения клиента к серверу можно с помощью функции ConnectNamedPipe();
+В качестве аргументов она принимает дескриптор канала и Структуру OVERLAPPED (для асинхронного доступа). Для синхронного доступа можно поставить NULL
+Возвращает она логическое значение.
+ Для того чтобы установить подключение,  на стороне клиента Должна быть вызвана функция SetNamedPipeHandleState();
+Она имеет следующие аргументы:
+  oo   Дескриптор канала
+  oo    режим подключения
+  oo    максимальное количество пользователей
+  oo    Время ожидания
+Пример:
+BOOL isSuccess = SetNamedPipeHandleState(hNamedPipe,&dwMode,NULL,NULL);
+Если клиент подключился к серверу,  toobi функция возвращает значение True;
+Если Клиент не подключен,  то функция ConnectNamedPipe возвращает false;
+ если сервер не отвечает,  то функция SetNamedPipeHandleState возвращает FALSE.
+Далее вся логика функционирования имя нового канала настраивается исходя из контекста задачи. 
+В приведённом ниже случае клиент пишет сообщение серверу первым,  затем ждёт ответного сообщения от сервера.  и после получения этот процесс повторяется.
+Полный код программы выглядят следующим образом:
+ для сервера:
+int main()
+{
+    system("chcp 1251");
+    HANDLE hNamedPipe;//объявление дескриптора калала
+    LPWSTR  lpszPipeName = "\\\\.\\pipe\\MyPipe";//переменная, содержащая имя канала
+    DWORD size_buffer = SIZE_BUFFER;//размер буфера для чтения
+    LPSTR buffer = (CHAR*)calloc(size_buffer, sizeof(CHAR));//строковая переменная, в которую будут считаны данные
+    char message[SIZE_BUFFER];
+    BOOL Connected;
+    DWORD actual_readen; //сколько на самом деле было прочитано байт
+    BOOL SuccessRead;
+    DWORD d = 0;//переменная, в которой будет храниться значение числа, передаваемого от клиента
+    while (TRUE)
+    {
+        hNamedPipe = CreateNamedPipeA( //создание канала
+            lpszPipeName, //имя канала
+            PIPE_ACCESS_DUPLEX, //режим доступа к каналу (односторонний/двусторонний)
+            PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, //режимы работы канала: передавать сообщения|читать сообщения|ждать
+            PIPE_UNLIMITED_INSTANCES, //количество водящих соединений к каналу. в данном случае неограничено
+            SIZE_BUFFER, // объем буфера на чтение (байт)
+            SIZE_BUFFER, // объем буфера на запись (байт)
+            INFINITE, // максимальное время ожидания сообщения
+            NULL); //указатель на структуру безопасности
+         Connected = ConnectNamedPipe(hNamedPipe, NULL); //установка соединения клиента с каналом
+        if (Connected) //если клиент подключился
+        {
+           // printf("\nКлиент успешно подключился \n");          
+            SuccessRead = ReadFile(hNamedPipe, buffer, size_buffer, &actual_readen, NULL);
+            if (SuccessRead)
+            {   
+                printf("\nКлиент пишет: ");
+                printf(buffer);
+                 printf("\n");
+                //отвечаем клиенту             
+                 printf("\nвведите сообщение для клиента:\n");
+                 gets(message);
+                 buffer = &message;//строковая переменная, значение которой записывается в канал
+                 WriteFile(hNamedPipe, buffer, size_buffer, &actual_readen, NULL);
+                } 
+        }
+        else
+        {
+            printf("\nКлиент отключился от сервера\n");            
+        }
+        CloseHandle(hNamedPipe);//закрываем канал
+    }
+}
+для клиента:
+int main()
+{
+	system("chcp 1251");
+	LPSTR  lpszPipeName = "\\\\.\\pipe\\MyPipe";//имя канала (такое же, как и на сервере)	 
+	
+	BOOL flag_otvet = TRUE;
+	char message[SIZE_BUFFER];
+	DWORD size_buffer = SIZE_BUFFER;//размер буфера для записи
+	DWORD actual_written; //сколько на самом деле было записано байт
+	LPSTR buffer;
+	DWORD actual_readen;
+	BOOL SuccessRead;
+	while (TRUE)
+	{
+		
+		char message[SIZE_BUFFER];
+		HANDLE hNamedPipe = CreateFileA(//открываем канал. по аналогии с открытием файла
+			lpszPipeName, GENERIC_READ | GENERIC_WRITE,
+			0, NULL, OPEN_EXISTING, 0, NULL);
+		DWORD dwMode = PIPE_READMODE_MESSAGE;
+		BOOL isSuccess = SetNamedPipeHandleState(hNamedPipe,&dwMode,NULL,NULL);
+		if (!isSuccess)
+		{
+			printf("сервер не отвечает\n");
+			flag_otvet = TRUE;
+		}
+		else
+		{
+		//	printf("соединение установлено\n");			
+				if (flag_otvet)
+				{
+					printf("введите сообщение для сервера:\n");
+					gets(message);
+					buffer = &message;//строковая переменная, значение которой записывается в канал
+					WriteFile(hNamedPipe, buffer, size_buffer, &actual_written, NULL);
+					flag_otvet = FALSE;
+				}	
+				buffer = (CHAR*)calloc(size_buffer, sizeof(CHAR));
+				SuccessRead = ReadFile(hNamedPipe, buffer, SIZE_BUFFER, &actual_readen, NULL);
+				if (SuccessRead)
+				{
+					printf("\nСервер пишет: ");
+					printf(buffer);
+					printf("\n");
+					flag_otvet = TRUE;					
+				}				
+		}		
+		Sleep(100);
+		CloseHandle(hNamedPipe);//закрываем подключение к каналу
+	}
+}
+
+  Использование системного буфера обмена 
+Несмотря на все удобства,  именованные каналы имеют ряд недостатков:
+  oo   Канал не может функционировать без сервера (Если программа сервер завершится,  то канал закроется);
+  oo   Доступ к каналу нужно специально организовывать из программного кода.
+Одним из решений данной проблемы является использование системного буфер обмена. 
+По сути буфер обмена можно рассматривать как канал или глобальный указатель,  к которому имеют доступ все прикладные программы. 
+Однако при работе с буфером следует учитывать и его недостатки:
+  oo   Буфер обмена только один в системе.  и любое приложение может его перезаписать. Поэтому нет гарантии целостности получения данных 
+  oo   Информация не может быть приватной (Во сколько буфер обмена доступен всем)
+
+Буфер обмена на позволяет хранить данные различных типов.  Однако при работе с ним программы учитывают этот контекст.
+В зависимости от формата данных в буфере с ним могут взаимодействовать те или иные приложения.
+
+                  Работа с буфером обмена
+Буфер обмена нужно скорее воспринимать как указатель,  а не как переменную.
+У буфера нет своей заранее выделенной области. Поэтому память выделяется в самой программе. 
+Примеры функций для работы с буфером:
+int ClipboardInputText(LPWSTR buffer)//записать строку в системный буфер
+{
+    DWORD len;//длина сообщения
+    HANDLE hMem;//дескриптор глобальной области памяти
+    len = wcslen(buffer) + 1; // определение длины строки в формате юникода   
+    hMem = GlobalAlloc(GMEM_MOVEABLE, len * sizeof(LPWSTR)); //выделение памяти в глобальной области видимости    
+    memcpy(GlobalLock(hMem), buffer, len * sizeof(LPWSTR));// копирование области памяти из buffer в hMem
+    GlobalUnlock(hMem); //разблокировать содержимое этой памяти (сделать доступным для других программ)
+    OpenClipboard(0);//открыть буфер обмена
+    EmptyClipboard();//очистить буфер обмена
+    SetClipboardData(CF_UNICODETEXT, hMem);//записать в буфер обмена данные соответствующего типа
+    CloseClipboard();//закрыть буфер обмена, сделать его доступным для других приложений    
+    return 0;
+}
+
+int ClipboardOutputText()//считать информацию из системного буфера
+{
+    OpenClipboard(NULL);//открыть буфер обмена   
+    LPWSTR Mess = (LPWSTR)GetClipboardData(CF_UNICODETEXT);//Считать из глобального участка памяти, привести это все к стороке
+    CloseClipboard();//закрыть буфер обмена, сделать его доступным для других приложений 
+    MessageBox(NULL, Mess, L"Содержимое буффера обмена", MB_OK);
+    return 0;
+}
+ 
+         Работа с системным реестром Windows 
+Формально реестр представляет из себя базу данных.
+Элементами этой базы данных являются параметры. Организация параметров представляет из себя иерархию.
+Чаще всего реестр используется для хранения параметров операционной системы или прикладных программ.
+Реестр задумывался как ниткой общее хранилище настроек. До этого программы хранили свои настройки в конфигурационных файлах (или файлах инициализации).
+Использование конфигурационных файлов имела ряд недостатков:
+  oo   Не было централизованного хранилища этих файлов.
+  oo   Проблема защиты файлов:
+     oo      Текстовые файлы можно было удалить 
+     oo      Можно было изменить содержимое текстовых файлов по-своему ведому
+                                       
+Для работы с реестром в системе Windows предусмотрена специальная утилита RegEdit.EXE
+                                       
+Сам реестр имеет 5 глобальных веток:
+  oo   HKEY CLASSES ROOT --  хранится информация о зарегистрированных классах, расширениях документов; 
+  oo   HKEY CURRENT USER  --  хранится информация о текущей пользовательской конфигурации, внешнем виде рабочего стола, сетевых настройках;
+  oo   HKEY LOCAL MACHINE  --  хранится информация о системной и аппаратной конфигурации;
+  oo   HKEY USERS  --  хранится информация обо всех зарегистрированных пользователях; 
+  oo    HKEY_CURRENT_CONFIG  --  текущая аппаратная конфигурация.
+Остальные параметры имеют более длинные пути,  которые начинаются в одной из глобальных веток.
+Крупные ветки называются ульями.
+Более мелкие ветки называются ключами реестра.
+Каждый ключ может содержать внутри себя другие ключи или параметры.
+Параметры также имеют свои определенные типы:
+  oo   Строковый параметр. Содержит последовательность символов в определенной кодировке. Его идентификатор в системе REG_SZ
+  oo   Двоичный параметр. По сути содержит массив байт. Его идентификатор в системе REG_BINARY
+  oo   Машинное слово для 32-битных систем. Его идентификатор в системе REG_DWORD
+  oo   Машинное слово для 64 разрядной системы. Его идентификатор в системе REG_QWORD
+  oo   Мультистроковый параметр.  другими словами массив строк. Его идентификатор в системе REG_MULTI_SZ
+  oo   Расширяемый строковый параметр. По сути представляет из себя строку переменной длины. REG_EXPAND_SZ
+
+          API функции для работы с реестром 
+Дескрипторов ключа реестра является переменная типа HKEY.
+HKEY Является указателем на соответствующую структуру.
+Для открытия ключа реестра используется функция RegOpenKey(HKEY_CURRENT_USER, NULL, &hKey);
+Она возвращает числовое значение (которое интерпретируется как код). Код успешного завершения этой функции определяется макросом ERROR_SUCCESS
+В качестве аргументов этой функции выступают три параметра:
+  oo   Название глобальной ветки реестра (одной из 5). Передаётся в качестве макроса 
+  oo   Пути к конкретному ключу от глобальной ветки (Весь остальной путь). Если нужно создать ключ в самой ветки,  то в качестве второго параметра пишется NULL
+  oo   Ссылка на дескриптор HKEY.
+Примеры использования этой функции в программном коде:
+HKEY hKey = NULL;//дескриттор ключа реестра (он является структурой)
+    if (RegOpenKey(HKEY_CURRENT_USER, NULL, &hKey) != ERROR_SUCCESS) //открываем раздел HKEY_CURRENT_USER
+        return 1;
+Функция RegCreateKey открывает ключ,  если он есть или создаёт его. Имеет такие же параметры и тип возвращаемого значения,  как и RegOpenKey
+
+Для того,  чтобы создать параметр используется функция RegSetValue.
+Возвращаемое значение у неё такое же, как и у предыдущих функций, Аргументы следующие:
+  oo   Дескриптор ключа реестра
+  oo   Название параметра,  который мы будем создавать или открывать
+  oo   Тип параметра реестра 
+  oo   Значение параметра
+  oo   Объем выделяемой памяти под значения параметра
+Пример использования данной функции:
+if (RegSetValueW(hKey, L"Mykey", REG_SZ, L"Значение по умолчанию", 22 * sizeof(WCHAR)) == ERROR_SUCCESS)
+    {
+        MessageBox(NULL, L"Ключ успешно создан и ему присвоено значение по умолчанию", L"Информация", MB_OK);
+    }
+
+
+ Для того чтобы получить значение параметра Используется функция RegGetValue
+Возвращаемое значение у неё то же,  аргументы следующие:
+  oo   Дескриптор ключа реестра
+  oo   Путь к подразделу реестра
+  oo   Типы допустимых значений
+  oo   Указатель на переменную,  которая хранит тип данных для ключа реестра. То есть функция возвращает код того параметра,  который считала из реестра.
+  oo   Указатель на переменную,  в которую запишется значение из реестра 
+Пример использования данной функции:
+DWORD DWValue = 0;
+    if (RegGetValueW(hKey, L"Mykey",L"MyDwordParam" , RRF_RT_ANY, &DataType, &DWValue, &DataLen) == ERROR_SUCCESS)
+    {
+        LPWSTR OutputString = malloc(512);
+        swprintf(OutputString, 512, TEXT("%d"), DWValue*2);
+        MessageBox(NULL, OutputString, L"Значение параметра", MB_OK);
+    }
+    else
+    {
+        MessageBox(NULL, L"Что-то пошло не так", L"Информация", MB_OK);
+    }
+Для удаления параметра используется функция RegDeleteValue
+Возвращает тот же код, вот а в качестве аргументов указываются Дескриптор ключа реестра и имя параметра.
+ if (RegDeleteValue(hKey, L"MyDwordParam") == ERROR_SUCCESS)
+    {
+        MessageBox(NULL, L"Параметр успешно удален", L"Информация", MB_OK);
+    }
+
+Для удаления ключа используется функция RegDeleteKey.
+Её использование по аналогии с удалением параметра 
+if (RegDeleteKey(hKey, L"MyKey") == ERROR_SUCCESS)
+    {
+        MessageBox(NULL, L"Ключ успешно удален", L"Информация", MB_OK);
+    }
+Для освобождения памяти под дескрипторы ключа используется функция  RegCloseKey(hKey);
+В качестве аргумента ей необходимо передать дескриптор ключа реестра 
+
+   Перехватчики системных событий (Windows HOOK)
+Хуками называются перехватчики системных событий. Они позволяют программно подменять обработчик этих событий.
+Мы будем рассматривать работу хуков на примере перехватчика сигналов нажатия клавиатуры.
+
+За установку хука отвечает функция SetWindowsHookEx Она возвращает Указатель на структуру HHOOK.
+Она имеет следующие аргументы:
+  oo   Флаг,  отвечающий за тип события 
+  oo   Указатель на функцию,  которая будет являться обработчиком события 
+  oo   Дескриптор в библиотеке DLL,  связанный с с обработчиком события. Если обработчик находится не в dll, то ставим NULL
+  oo   Идентификатор потока,  на которой распространяется хук. Если указать 0,  то он будет работать со всеми потоками 
+Сама работа huka направлена на обработку системных событий. Поэтому также необходимо обрабатывать список входящих системных событий.
+за это отвечает функция GetMessageW. Она возвращает логическое значение, аргументы у неё следующие:
+  oo   Указатель на структуру MSG
+  oo   Дескриптор окна.  мы ставим NULL
+  oo   Порог самого низкоуровневого сообщения. NULL - Для всех входящих сообщений 
+  oo   Порог самого высокоуровневого сообщения. NULL - Для всех входящих сообщений 
+
+Для того чтобы интерпретировать события нажатия виртуальные клавиши используется функция TranslateMessage.
+Она возвращает логическое значение и принимает в качестве аргумента указатель на структуру MSG.
+Для отправки сообщения в в обработчик хука используется функция DispatchMessage.
+Она возвращает числовой код,  а в качестве аргумента также принимает указатель на структуру MSG.
+
+Функция UnhookWindowsHookEx(hHook); Освобождает дескриптор хука.
+ 
+Также для правильного интерпретации нажатые клавиши важно понятию регистр.
+Символ должен быть напечатан в Верхнем регистре в двух раздельных случаях:
+  oo   Если зажата клавиша Shift
+  oo    если нажата клавиша caps lock 
+Для проверки этих ситуаций используются функция 
+GetKeyState
+Она принимает в качестве аргумента код виртуальной клавиши и возвращает значение типа SHORT.
+И для того чтобы это значение перевести в логический тип используется операция конъюнкция с определённой константой
+Пример функции:
+BOOL IsCaps(void)//функция, которая проверяет регистр букв. TRUE - если в верхнем регистре
+{
+    //GetKeyState используется в основном для определения состояния нажатия системной кнопки
+    //VK - Virtual Key
+    // ^ - это XOR (потому что Shift во время нажатого CapsLock опять делает букву в нижнем регистре)
+    if ((GetKeyState(VK_CAPITAL) & 0x0001) != 0 ^ (GetKeyState(VK_SHIFT) & 0x8000) != 0)
+        return TRUE;
+    return FALSE;
+}
+
+Также для работы программы необходимо вспомогательная функция записи в файл.
+Здесь используется стандартный подход языка си.  единственное ограничение на то что записываются символы юникода.
+VOID WriteToFile(LPWSTR wstr)//функция записи в файл
+{
+    FILE* f = NULL;
+    if (!_wfopen_s(&f, PATH, L"ab"))
+    {
+        fwrite(wstr, sizeof(WCHAR), wcslen(wstr), f);
+        fclose(f);
+    }
+}
+Теперь рассмотрим более детально функцию обработки хука 
+LRESULT CALLBACK LogKey(int iCode, WPARAM wParam, LPARAM lParam)//функция обработчика системного сообщения
+В качестве аргументов данной функции передаются параметры из структуры MSG
+wParam Содержит код системного события.
+Для нас нужно событие нажатой клавиши.  оно будет обрабатываться следующим образом:
+if (wParam == WM_KEYDOWN)
+
+В lParam Содержится код действия, которое повлекло событий ( например,  Какая именно кнопка нажата).
+Но для этого его предварительно необходимо распарсить в структуру Хука
+PKBDLLHOOKSTRUCT pHook = (PKBDLLHOOKSTRUCT)lParam;
+ Далее для работы Нам необходимо получить раскладку клавиатуры 
+Это можно сделать путем использования комбинации функций 
+WORD KeyLayout = LOWORD(GetKeyboardLayout(GetWindowThreadProcessId(GetForegroundWindow(), 0)));
+//GetForegroundWindow возвращает дескриптор активного в данный момент окна
+//GetWindowThreadProcessId возвращает ID данного процесса
+//GetKeyboardLayout возвращает раскладку клавиатуры, используемую в этом процессе
+LOWORD Преобразует в формат Word результат,  возвращаемое функцией GetKeyboardLayout
+
+Код виртуальные клавиши получается следующим образом:
+DWORD iKey = MapVirtualKeyW(pHook->vkCode, NULL) << 16; 
+Первые 32 символа является непечатными.
+И чтобы их правильно интерпретировать нужно выполнить следующую операцию 
+if (!(pHook->vkCode <= 1 << 5)) // 32 (т.к. первые 32 символа являются не печатными)
+            iKey |= 0x1 << 24; //Задаём истину для 24 бита (|= - побитовое присваивание a|= b эквивалентно a = a|b)
+Занята репутацию кода клавиши отвечает функция GetKeyNameText
+в качестве аргументов она принимает следующее:
+  oo   Код нажатой клавиши в формате Long 
+  oo   Строка,  в которую запишется название клавиши 
+  oo   Длина строки 
+Далее надо обработать результат функции  GetKeyNameText
+Если длина строки больше 1, то это означает,  что нажата не символьная клавиша
+Её название будет записано в текстовый файл в квадратных скобках:
+if (wcslen(KeyString) > 1) //Если нажата не текстовая клавиша
+        {
+            WriteToFile(L"[");
+            WriteToFile(KeyString);
+            WriteToFile(L"]");
+        }
+
+В другом случае мы записываем название символьные клавиши как есть (Только перед этим необходимо проверить её регистр)
+if (!IsCaps()) KeyString[0] = tolower(KeyString[0]);//переводим в нижний регистр.
+Функция GetKeyNameText  по умолчанию возвращает название клавиши в верхнем регистре И только на латинице.
+Для того,  чтобы записать название клавиши на кириллице необходимо также учесть раскладку клавиатуры 
+if (KeyLayout == ENG)//если английская раскладка        
+            {
+                //сюда будем писать код, если есть какие-то спецсимволы на английской раскладке
+            }
+            if (KeyLayout == RUS)//если русская раскладка
+            {
+                KeyString[0] = EnToRus(KeyString[0]);
+            }
+Для перевода английской клавиша в русскую можно воспользоваться самостоятельно написанной функцией через switch.
+Пример такой функции:
+WCHAR EnToRus(WCHAR c)
+{
+    switch (c)
+    {
+    case L'q':
+        return L'й';
+    case L'w':
+        return L'ц';
+//и так далее
+    default:
+        return L' ';
+    }
+}
+
+
+Также надо учесть,  что в конце обработчика хука необходимо вернуть следующее:
+return CallNextHookEx(NULL, iCode, wParam, lParam);