// #import "@local/nure:0.0.0": *
#import "lib.typ": *

#show: lab-pz-template.with(
  doctype: "ЛБ",
  title: "Створення та використання бібліотек. Частина 2",
  subject_shorthand: "ОС",
  department_gen: "Програмної інженерії",
  authors: (
    (
      name: "Ситник Є. С.",
      full_name_gen: "Ситника Єгора Сергійовича",
      group: "ПЗПІ-23-2",
      gender: "m",
      variant: none,
    ),
  ),
  mentor: (
    name: "Мельнікова Р. В.",
    gender: "f",
    degree: "доц. каф. ПІ",
  ),
  worknumber: 3,
)

#v(-spacing)

== Мета роботи
Вивчити розширені прийоми та методи створення та використання різних типів динамічних бібліотек.

== Хід роботи

=== Створити новий варіант бібліотеки "rsa" із застосуванням ".def" файлу, та динамічного завантаження під час виконання програми.

Файли із розширенням ".def" використовуються для позначення "символів", що будуть експортовані із ".dll" бібліотеки для зовнішнього використання. Зараз застосування таких файлів можна зустріти дуже рідко, адже його майже повністю замінили атрибути "\_\_declspec(dllexport)" та "\_\_declspec(dllimport)". Оскільки такі атрибути були використані під час виконання попередньої лабораторної роботи нам необхідно їх прибрати із заголовочного файлу нової версії бібліотеки, після цього створимо файл "rsa.def" в який додамо назви функцій, що мають бути експортовані.

#figure(image("img/1/h.png", width: 70%), caption: [Оновлений вміст файлу "rsa.h"]) <1h>
#figure(image("img/1/def.png"), caption: [Вміст файлу "rsa.def"]) <1def>

Також необхідно передати створений "rsa.def" файл компілятору. Для цього оновимо команду компіляції, додавши шлях до відповідного файлу.

#figure(image("img/1/just.png"), caption: [Оновлений "рецепт" компіляції бібліотеки "rsa.dll"]) <1just>

Щоб перевірити, що потрібні "символи" експортуються із "rsa.dll" можемо скористатись програмою "dumpbin".

#figure(
  image("img/1/check.png", width: 65%),
  caption: [Перевірка зі допомогою "dumpbin" експортованих із бібліотеки "символів"],
) <1check>

Можемо побачити, що потрібні "символи" експортуються із "rsa.dll".

Тепер створимо нову версію програми, в якій використаємо динамічне завантаження бібліотеки під час виконання. Для цього змінимо файл "main.cpp". На самому початку виконання програми нам необхідно завантажити "rsa.dll" файл та експортувати із нього функції, типи яких оголосимо заздалегідь. Також додамо виклик "FreeLibrary" перед кожним місцем завершення програми після завантаження бібліотеки.

#figure(
  image("img/1/func_types.png", width: 100%),
  caption: [Оголошені типи функцій для експорту],
) <1func_types>

#figure(
  image("img/1/dll_load.png", width: 100%),
  caption: [Завантаження бібліотеки та імпорту функцій із неї],
) <1dll_load>

Також не забудемо оновити "рецепт" компіляції програми.

#figure(image("img/1/just2.png"), caption: [Оновлений "рецепт" компіляції програми]) <1just2>

Перевіримо роботу програми.

#figure(image("img/1/result.png"), caption: [Результат виконання програми]) <1result>

Програма працює як і раніше.

=== Створити файли ресурсів із таблицями рядків українською та англійською мовою, та програму для їх використання.

Файли ресурсів це файли, які містять скрипти, що описують різні не-кодові елементи, які використовуються програмою (наприклад рядки, іконки, бітові карти та інше). Файли ресурсів мають розширення ".rc" та створюються із використанням специфічного синтаксису. Створимо файли "locale.cpp" та "locale.rc" для української та англійської локалізацій, спільний файл "locale.h" для позначення ID ресурсів, та файл "main.cpp" для написання основної програми.

В файлі заголовків визначимо псевдоніми, що ми будемо використовувати замість числових ID.

#figure(image("img/2/locale_h.png"), caption: [Вміст файлу "locale.h"]) <2locale_h>

В файлах "locale.cpp" оголосимо функції "DLLMain", що викликаються під час завантаження/вивантаження бібліотеки, та надрукуємо інформацію для відлагодження.

#figure(image("img/2/locale_cpp.png"), caption: [Вміст файлів "locale.cpp"]) <2locale_cpp>

В файлах "locale.rc" оголосимо строки, що відповідатимуть прізвищу, факультету, групі та дисципліні двома мовами, використаємо оголошені раніше псевдоніми в якості ID.

#figure(image("img/2/locale_ua.rc.png"), caption: [Вміст файлу "locale.rc" із українською локалізацій]) <2locale_ua.rc>
#figure(image("img/2/locale_en.rc.png"), caption: [Вміст файлу "locale.rc" із англійською локалізацією]) <2locale_en.rc>

Створимо "рецепти" для компіляції створених ресурсних файлів. Для перетворення файлів ".rc" у бінарний формат використовується програма "windres", за її допомогою файли ".rc" перетворюються в об'єктні файли ".res", які потім передаються компілятору для створення ".dll". Оскільки я використовую операційну систему ArchLinux я маю додати деякі аргументи для "windres", а саме "-c 65001" для встановлення кодування UTF-8, та "-O coff", щоб вказати формат бінарних файлів, що відповідає формату Windows.

#figure(image("img/2/just_locale.png"), caption: ["Рецепти" компіляції файлів ресурсів]) <2locale_just>

В файлі "main.cpp" запитаємо у користувача мову, та завантажимо відповідний цій мові ".dll" файл, після чого завантажимо з цього файлу строки використовуючи визначені раніше псевдоніми, та виведемо ці строки в консоль.

#figure(image("img/2/main.png"), caption: [Вміст файлу "main.cpp"]) <2main>

Створимо "рецепт" для компіляції програми.

#figure(image("img/2/just_app.png"), caption: ["Рецепти" компіляції програми]) <2just_app>

Перевіримо роботу програми із обома мовами.

#figure(image("img/2/result_ua.png"), caption: [Результат виконання із українською мовою]) <2result_ua>
#figure(image("img/2/result_en.png"), caption: [Результат виконання із англійською мовою]) <2result_en>

=== Створити новий проект для підрахунку контрольної суми за допомогою алгоритму "crc32" та інтегрувати його в бібліотеку "rsa".

Алгоритм "crc32" -- це алгоритм обчислення контрольної суми, який використовується для перевірки цілісності даних. Його основна мета це виявлення випадкових помилок передачі та зберігання (перевірка цілісності) даних, але не захист від навмисних змін даних шахраями, адже існують способи навмисно змінити дані зберігши при цьому ту саму контрольну суму.

Створимо новий проєкт, що складатиметься із бібліотеки та консольного застосунку. Бібліотека буде надавати функції для генерації контрольної суми, додавання згенерованої контрольної суми до файлу, та перевірки цілісності файлу на основі доданої до нього контрольної суми. Консольний застосунок виконуватиме 2 команди -- додавання контрольної суми до файлу, та перевірка файлу.

Створимо файл заголовків "crc.h", в якому оголосимо відповідні функції, та файл "crc.def", для експорту функцій із ".dll".

#figure(image("img/3/crc.h.png", width: 80%), caption: [Вміст файлу "crc.h"]) <3crc.h>
#figure(image("img/3/crc.def.png"), caption: [Вміст файлу "crc.def"]) <3crc.def>

Алгоритм "crc32" передбачає побітове ділення даних на поліном, що може бути досить повільним процесом, особливо для великих обсягів даних. Для прискорення цієї операції перед початком алгоритму можна обрахувати проміжні значення та зберегти їх в таблицю (для 1 байту це всього 256 унікальних значень). Створимо таблицю для збереження попередньо обрахованих значень, та функцію, що їх обрахує.

#figure(image("img/3/table.png"), caption: [Функція наповнення таблиці]) <3table>

Реалізуємо функцію, що буде обраховувати контрольну суму для наданого буферу.

#figure(image("img/3/update.png"), caption: [Функція обрахунку контрольної суми для буферу]) <3update>

Створимо функцію, що відкриватиме файл за вказаною адресою, обраховуватиме його контрольну суму, та додаватиме обраховане значення в кінець файлу.

#figure(image("img/3/append.png", width: 75%), caption: [Функція додавання контрольної суми до файлу]) <3append>

Створимо функцію, що відкриватиме файл за вказаною адресою, зчитуватиме збережену в ньому контрольну суму, обраховуватиме контрольну суму повторно, та порівнюватиме збережене та обраховане значення.

#figure(image("img/3/verify.png", width: 75%), caption: [Функція перевірки цілісності файлу]) <3verify>

Створимо консольний застосунок, що прийматиме 2 аргументи -- режим роботи та шлях до файлу, та викликатиме відповідні режиму функції.

#figure(image("img/3/main.png"), caption: [Вміст файлу "main.cpp"]) <3main>

Також додамо "рецепти" для компіляції створених бібліотеки та застосунку.

#figure(image("img/3/just.png"), caption: [Створені "рецепти" компіляції]) <3just>

Перевіримо роботу програми на текстовому файлі.

#figure(image("img/3/test.png"), caption: [Перевірка роботи програми]) <3test>

Можемо побачити, що програма працює як задумано.

Тепер додамо перевірку цілісності в бібліотеку "rsa", використавши функцію перевірки із щойно створеної бібліотеки "crc".

#figure(image("img/3/rsa.png"), caption: [Перевірка роботи програми]) <3rsa>

Протестуємо перевірку цілісності. Для цього запустимо програму, щоб переконатись, що вона працює, потім за допомогою редактору змінимо вміст "rsa.dll" (наприклад видаливши визначення якогось із експортованих символів), та спробуємо запустити програму ще раз.

#figure(image("img/3/test_rsa.png"), caption: [Перевірка роботи програми]) <3test_rsa>

Можемо побачити, що перевірка цілісності працює, як і очікувалося.

== Висновки
Під час виконання даної лабораторної роботи я вивчив розширені прийоми та методи створення та використання різних типів динамічних бібліотек.
