Чистая Windows и разработка «без всего»
Есть компьютер с чистой копией Windows, без доступа в интернет и без каких‑либо установленных средств разработки. Только одна чистая пользовательская «венда». Не поверите, но даже в таких спартанских условиях возможно написать и запустить полноценную программу. И сейчас я расскажу как.
Ради этого скриншота я честно развернул пользовательскую версию Windows 11 в виртуальной машине. Чего не сделаешь ради искусства!
Ужасы познания
На самом деле в ОС семейства Windows с самого их начала было внутри столько всякого интересного, что никакой статьи не хватит описать, но почему-то мало кто об этом знает даже из разработчиков, особенно современных.
Спросите ради интереса знакомого программиста, возможно ли программировать на «чистой» пользовательской Windows без установки Visual Studio — удивитесь ответам.
Ну и разумеется насаждаемый «пользовательский» подход самой Microsoft, которая ковыряние во внутренностях своих продуктов мягко говоря никогда не одобряла, создал ореол простоты и надежности, без необходимости разбираться как оно внутри устроено.
Поэтому описанное ниже наверное вызовет определенный ужас как у обычных пользователей так и некоторых программистов — особенно если они обучались по видеокурсам ничего не знают об истории ОС Windows.
Начну с цитаты из одной интересной статьи:
Over the past few months, I've received several variations on this question for other operating systems and all of the released versions of the .NET Framework. When the .NET Framework is installed as a part of the OS, it does not appear in the Programs and Features (or Add/Remove Programs) control panel. The following is a complete list of which version of the .NET Framework is included in which version of the OS
И ниже длинный такой список с версиями. А вот еще один если вдруг первого оказалось недостаточно.
Ну казалось бы и.. что? Чего тут такого?
Про .NET SDK все и так знают, временами его необходимо установить «для запуска игор», временами он сам ставится в виде зависимой библиотеки и никому не мешает.
Все так, да.
Только что-то мне подсказывает внутрь вы не заглядывали, правда? Поэтому на что эта штука на самом деле способна не представляете.
А я представляю и сейчас расскажу.
Заходите в папку Windows на вашем компьютере, вот сюда:
Файлт csc.exe — самый настоящий компилятор, фактически портал в ад на вашем обычном домашнем компьютере.
Почему все так страшно?
Потому что через какое-то время вы обнаружите себя сильно заросшим, с бородой и красными глазами, проводящим ночи за компьютером и медленно мутирующим в программиста.
Шучу.
А если серьезно:
появляется возможность создания нативных программ сразу на вашем компьютере, минуя стадию проверки электронной подписи, проверки антивирусом, проверки электронного письма и так далее.
В отличие от VB или PowerShell-скриптов, которые анализируются перед запуском любым приличным антивирусом, антивирусы не анализируют исходный код программ на C# и куда лояльнее относятся к программам собранным локально на этой же машине.
Так что веселье начинается.
Простой пример
Для начала будет простой пример, который просто показывает стандартный диалог с сообщением. Именно его в запущенном виде вы можете видеть на заглавной картинке в статье.
Весь процесс от кода до запуска я записал на видео:
Исходный код тут казалось бы максимально простой, но с одним интересным нюансом про который ниже:
using System;
using System.Runtime.InteropServices;
namespace yoba
{
class Program
{
// импортирование нативной WinAPI функции MessageBox.
[DllImport("user32.dll")]
public static extern int MessageBox(IntPtr hWnd, string lpText, string lpCaption, uint uType);
static void Main(string[] args)
{
//вызываем и показываем диалог
MessageBox(IntPtr.Zero, "Йоу!", "Добро пожаловать в разработку!", 0);
}
}
}
Сохраняете этот текст обычным «блокнотом» в файл yoba.cs и запускаете сборку:
c:\Windows\Microsoft.NET\Framework\v3.5\csc.exe yoba.cs
Таким образом я запускал сборку на Windows 10, но имейте ввиду что версия системного .NET SDK может отличаться и например в Windows 11 уже будет:
c:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe yoba.cs
После сборки рядом с исходным файлом yoba.cs появится бинарник yoba.exe, который вы сможете запустить.
А теперь про нюанс.
Нюанс
Существует определенное предубеждение по отношению к managed‑языкам вроде Java и С# — они не подходят для серьезных дел вроде написания эксплоитов, использования 0day‑уязвимостей и пенетрации ядра.
Что все подобные вещи творят
в глубокой тайнена чистом Си, в крайнем случае на C++ а все эти ваши Java/C# не более чем «погремушки для детей», не достойные даже косого взгляда серьезного профессионала.
Вот тут и начинается нюанс, посмотрите на эту радость:
[DllImport("user32.dll")]
public static extern int MessageBox(IntPtr hWnd,
string lpText, string lpCaption, uint uType);
Это мои дорогие читатели, ни что иное как вызов нативного WinAPI, с помощью которого творили всякое нехорошее в далекие 90е.
C# и .NET имеет оооочень глубокую интеграцию с Windows, несмотря на всю свою «безопасность» и управляемость, поэтому легко и просто может заменить собой и Си и С++ в качестве инструмента для нехороших дел.
И оно живет на вашем компьютере, дома и в офисе, с постоянной пропиской и регистрацией.
Но разумеется столь простого примера несколько мало для осознания глубины проблемы, поэтому я подготовил кое-что более серьезное.
Сложный пример: выключаем Windows
Итак, это будет относительно небольшое приложение на C#, выключающее компьютер без предупреждения и подтверждения пользователя. И само собой без прав администратора.
Просто так, внезапно.
Последствия думаю каждый из читателей сможет оценить для себя сам.
Весь процесс на видео (разумеется это виртуальная машина):
А теперь код:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security;
using System.Diagnostics;
using System.Management;
using System.Security.Permissions;
using System.Runtime.InteropServices;
namespace yoba
{
// See http://www.developmentnow.com/g/33_2004_12_0_0_33290/Access-...
// Calling this code on backup/restore seems to enable BCD
public class TokenHelper
{
// PInvoke stuff required to set/enable security privileges
[DllImport("advapi32", SetLastError=true),
SuppressUnmanagedCodeSecurityAttribute]
static extern int OpenProcessToken(
System.IntPtr ProcessHandle, // handle to process
int DesiredAccess, // desired access to process
ref IntPtr TokenHandle // handle to open access token
);
[DllImport("kernel32", SetLastError=true),
SuppressUnmanagedCodeSecurityAttribute]
static extern bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true),
SuppressUnmanagedCodeSecurityAttribute]
static extern int AdjustTokenPrivileges(
IntPtr TokenHandle,
int DisableAllPrivileges,
IntPtr NewState,
int BufferLength,
IntPtr PreviousState,
ref int ReturnLength);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true),
SuppressUnmanagedCodeSecurityAttribute]
static extern bool LookupPrivilegeValue(
string lpSystemName,
string lpName,
ref LUID lpLuid);
[StructLayout(LayoutKind.Sequential)]
internal struct LUID
{
internal int LowPart;
internal int HighPart;
}
[StructLayout(LayoutKind.Sequential)]
struct LUID_AND_ATTRIBUTES
{
LUID Luid;
int Attributes;
}
[StructLayout(LayoutKind.Sequential)]
struct _PRIVILEGE_SET
{
int PrivilegeCount;
int Control;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=1)] // ANYSIZE_ARRAY = 1
LUID_AND_ATTRIBUTES [] Privileges;
}
[StructLayout(LayoutKind.Sequential)]
internal struct TOKEN_PRIVILEGES
{
internal int PrivilegeCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=3)]
internal int[] Privileges;
}
const int SE_PRIVILEGE_ENABLED = 0x00000002;
const int TOKEN_ADJUST_PRIVILEGES = 0X00000020;
const int TOKEN_QUERY = 0X00000008;
const int TOKEN_ALL_ACCESS = 0X001f01ff;
const int PROCESS_QUERY_INFORMATION = 0X00000400;
public static bool SetPrivilege (string lpszPrivilege, bool
bEnablePrivilege )
{
bool retval = false;
int ltkpOld = 0;
IntPtr hToken = IntPtr.Zero;
TOKEN_PRIVILEGES tkp = new TOKEN_PRIVILEGES();
tkp.Privileges = new int[3];
TOKEN_PRIVILEGES tkpOld = new TOKEN_PRIVILEGES();
tkpOld.Privileges = new int[3];
LUID tLUID = new LUID();
tkp.PrivilegeCount = 1;
if (bEnablePrivilege)
tkp.Privileges[2] = SE_PRIVILEGE_ENABLED;
else
tkp.Privileges[2] = 0;
if(LookupPrivilegeValue(null , lpszPrivilege , ref tLUID))
{
Process proc = Process.GetCurrentProcess();
if(proc.Handle != IntPtr.Zero)
{
if (OpenProcessToken(proc.Handle, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
ref hToken) != 0)
{
tkp.PrivilegeCount = 1;
tkp.Privileges[2] = SE_PRIVILEGE_ENABLED;
tkp.Privileges[1] = tLUID.HighPart;
tkp.Privileges[0] = tLUID.LowPart;
const int bufLength = 256;
IntPtr tu = Marshal.AllocHGlobal( bufLength );
Marshal.StructureToPtr(tkp, tu, true);
if(AdjustTokenPrivileges(hToken, 0, tu, bufLength, IntPtr.Zero, ref
ltkpOld) != 0)
{
// successful AdjustTokenPrivileges doesn't mean privilege could be changed
if (Marshal.GetLastWin32Error() == 0)
{
retval = true; // Token changed
}
}
TOKEN_PRIVILEGES tokp = (TOKEN_PRIVILEGES) Marshal.PtrToStructure(tu,
typeof(TOKEN_PRIVILEGES) );
Marshal.FreeHGlobal( tu );
}
}
}
if (hToken != IntPtr.Zero)
{
CloseHandle(hToken);
}
return retval;
}
}
class ShutDown
{
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool ExitWindowsEx(int flg, int rea);
internal const int EWX_FORCE = 0x00000004;
internal const int EWX_POWEROFF = 0x00000008;
static void Main(string[] args)
{
TokenHelper.SetPrivilege("SeShutdownPrivilege",true);
ExitWindowsEx(EWX_FORCE | EWX_POWEROFF, 0);
}
}
}
Обращаю внимание что это не эксплоит, не дыра, не баг и не уявимость а вполне себе стандартный функционал. Просто так получилось что о нем мало кто знает.
Собирается по аналогии с предыдущим примером:
c:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe Shutdown.cs
После запуска компьютер практически немедленно выключится:
проверено и в виртуальной машине и на железе, на 10й и 11й Windows.
Рассказываю как это работает.
Ключевая функция — ExitWindowsEx, которая и отвечает за завершение работы ОС. Эта функция очень старая и известная, существует еще со времен Windows 95.
Но для ее вызова нужны «привилегии», которые и выставляет программно класс TokenHelper.
Константы ниже:
internal const int EWX_FORCE = 0x00000004;
internal const int EWX_POWEROFF = 0x00000008;
используются вместе с "побитовым или" для указания на требуемое действие.
Вот еще допустимые варианты:
internal const int EWX_LOGOFF = 0x00000000;
internal const int EWX_SHUTDOWN = 0x00000001;
internal const int EWX_REBOOT = 0x00000002;
internal const int EWX_FORCEIFHUNG = 0x00000010;
Описание их всех находится все там же — в официальном руководстве, не поверите.
Теперь давайте разбираться как же работает столь жесткое забивание на систему защиты еще и стандартными средствами:
TokenHelper.SetPrivilege("SeShutdownPrivilege",true);
И начнем мы с импортов.
Первое что импортируется это функция OpenProcessToken:
[DllImport("advapi32", SetLastError=true),
SuppressUnmanagedCodeSecurityAttribute]
static extern int OpenProcessToken(
System.IntPtr ProcessHandle, // handle to process
int DesiredAccess, // desired access to process
ref IntPtr TokenHandle // handle to open access token
);
Функция отвечает за получение данных о наборе «привилегий», связанных с конкретным процессом. Собственно набор таких привилегий и называется «токеном».
Вот как эта функция вызывается:
if (OpenProcessToken(proc.Handle, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
ref hToken) != 0)
{
..
Тут надо отметить передачу по ссылке в стиле Си (ref hToken), когда в функцию передается ссылка на объект C#, дальше функция этот объект заполняет данными. А возвращает она просто true или false — статус выполнения, отработала функция или нет.
Дальше импортируется простая и банальная функция освобождения ресурсов:
[DllImport("kernel32", SetLastError=true),
SuppressUnmanagedCodeSecurityAttribute]
static extern bool CloseHandle(IntPtr handle);
Вызывается она в самом конце, после всей логики и нужна только для освобождения использованной памяти под токен привилегий:
if (hToken != IntPtr.Zero)
{
CloseHandle(hToken);
}
Наконец главная функция, непосредственно отвечающая за переключение привилегий:
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true),
SuppressUnmanagedCodeSecurityAttribute]
static extern int AdjustTokenPrivileges(
IntPtr TokenHandle,
int DisableAllPrivileges,
IntPtr NewState,
int BufferLength,
IntPtr PreviousState,
ref int ReturnLength);
Вот весь ключевой блок логики смены привилегий:
if (OpenProcessToken(proc.Handle, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
ref hToken) != 0)
{
tkp.PrivilegeCount = 1;
tkp.Privileges[2] = SE_PRIVILEGE_ENABLED;
tkp.Privileges[1] = tLUID.HighPart;
tkp.Privileges[0] = tLUID.LowPart;
const int bufLength = 256;
IntPtr tu = Marshal.AllocHGlobal( bufLength );
Marshal.StructureToPtr(tkp, tu, true);
if(AdjustTokenPrivileges(hToken, 0, tu, bufLength, IntPtr.Zero, ref
ltkpOld) != 0)
{
// successful AdjustTokenPrivileges doesn't mean privilege could be changed
if (Marshal.GetLastWin32Error() == 0)
{
retval = true; // Token changed
}
}
TOKEN_PRIVILEGES tokp = (TOKEN_PRIVILEGES) Marshal.PtrToStructure(tu,
typeof(TOKEN_PRIVILEGES) );
Marshal.FreeHGlobal( tu );
}
Как видите вызов достаточно сложный, используется Сишный процедурный подход к заполнению полей структуры и передачи его по ссылке в вызываемую функцию.
После вызова проверяется наличие ошибки, также в стиле Си:
if (Marshal.GetLastWin32Error() == 0)
{
retval = true; // Token changed
}
0 это код возрата для успешного вызова, если он есть — считается что операция смены привилегий была выполнена успешно.
Наконец последняя функция, про которую стоит рассказать:
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true),
SuppressUnmanagedCodeSecurityAttribute]
static extern bool LookupPrivilegeValue(
string lpSystemName,
string lpName,
ref LUID lpLuid);
Она отвечает за поиск привилегии по имени, полагаю ведь заметили что мы передаем некое кодовое наименование при вызове TokenHelper:
TokenHelper.SetPrivilege("SeShutdownPrivilege",true);
Именно эта функция отвечает за поиск конкретной привилегии по названию «SeShutdownPrivilege», вот так выглядит ее вызов:
if (bEnablePrivilege)
tkp.Privileges[2] = SE_PRIVILEGE_ENABLED;
else
tkp.Privileges[2] = 0;
if(LookupPrivilegeValue(null , lpszPrivilege , ref tLUID))
{
..
Переменная bEnablePrivilege булевая, это и есть то самое true передаваемое в качестве второго аргумента, а блок:
if (bEnablePrivilege)
tkp.Privileges[2] = SE_PRIVILEGE_ENABLED;
else
tkp.Privileges[2] = 0;
Отвечает за формирование правильного вызова с использованием системных констант (SE_PRIVILEGE_ENABLED).
При вызове также передается ссылка (ref tLUID) на объект LUID, который будет содержать после вызова указание на найденную привилегию.
Вот такие интересные дела.
Итого
Все описанное не призыв к немедленным действиям, а лишь повод к размышлению о смысле бытия. Ну там насчет надежности, безопасности и всего такого — что вам продает большая иностранная корпорация.
Задумайтесь, если увидите любимую "венду" на атомной станции или военном объекте — без всяких ЦРУ и хакеров в ОС Windows адова гора функционала, который легко и просто можно использовать во вред.
P.S.
Статья была опубликована на Хабре, более вольный оригинал доступен в нашем блоге.
Использование C# для преобразования HTML в Word или Word в HTML
Преобразование между HTML, стандартным форматом веб-контента, и Word, универсальным форматом офисных документов, является частой задачей во многих корпоративных приложениях. Будь то генерация отчётов Word из динамического содержимого базы данных или публикация существующих документов Word в виде веб-страниц — владение эффективными методами конвертации значительно повышает производительность.
В этой статье мы расскажем, как с помощью профессионального компонента для работы с документами Spire.Doc for .NET и кода на C# легко выполнять взаимное преобразование между HTML и Word.
Почему стоит выбрать Spire.Doc for .NET?
Сам по себе .NET не предоставляет встроенной поддержки для работы с документами Word или HTML и их преобразования. Хотя существуют открытые решения (например, HtmlAgilityPack в сочетании с OpenXML SDK), они часто требуют от разработчика ручной обработки множества деталей и имеют недостатки в сохранении стилей, встраивании изображений и т.д.
Spire.Doc for .NET предлагает мощный и простой в использовании API, позволяющий создавать, редактировать и конвертировать документы без установки Microsoft Office на сервере. Он обеспечивает высокое качество преобразования:
Сохранение стилей: полная поддержка текстовых форматов, цветов, выравнивания и других CSS-стилей из HTML.
Обработка изображений: автоматическое распознавание и встраивание изображений из тегов <img> в HTML.
Табличная структура: сохранение исходной вёрстки HTML-таблиц без искажений.
Простота разработки: лаконичный API и низкий порог вхождения.
Подготовка: установка Spire.Doc
Перед началом кодирования необходимо добавить ссылку на Spire.Doc в проект. Рекомендуется выполнить установку через консоль диспетчера пакетов NuGet с помощью следующей команды:
Install-Package Spire.Doc
Сценарий 1: преобразование HTML-строки в Word
Этот сценарий очень гибок и подходит для случаев, когда HTML-содержимое получается из базы данных, API-интерфейсов или других динамических источников данных, и требуется мгновенно сгенерировать документ Word.
В приведённом ниже коде показано, как прочитать содержимое HTML-файла (в виде строки) и преобразовать его в файл .docx:
using Spire.Doc;
using Spire.Doc.Documents;
using System.IO;
namespace ConvertHtmlStringToWord
{
class Program
{
static void Main(string[] args)
{
// 1. Создание объекта Document
Document document = new Document();
// 2. Добавление раздела (Section)
Section section = document.AddSection();
// Опционально: установка полей страницы
section.PageSetup.Margins.All = 2;
// 3. Добавление абзаца (Paragraph)
Paragraph paragraph = section.AddParagraph();
// 4. Чтение HTML-строки из файла
string htmlFilePath = @"C:\Users\Administrator\Desktop\Html.html";
string htmlString = File.ReadAllText(htmlFilePath, System.Text.Encoding.UTF8);
// 5. Вставка HTML-строки в абзац
paragraph.AppendHTML(htmlString);
// 6. Сохранение в документ Word
document.SaveToFile("AddHtmlStringToWord.docx", FileFormat.Docx);
// 7. Освобождение ресурсов
document.Dispose();
}
}
}
Описание ключевых методов :
Paragraph.AppendHTML(htmlString) — это основной метод преобразования. Он анализирует переданную HTML-строку и полностью преобразует её форматирование, изображения и структуру в содержимое абзаца Word.
С помощью document.SaveToFile() можно указать формат вывода FileFormat.Docx.
Сценарий 2: прямое преобразование HTML-файла в Word
Если у вас уже есть готовый HTML-файл, который вы хотите напрямую преобразовать в документ Word, код будет ещё более лаконичным:
using Spire.Doc;
namespace ConvertHtmlToWord
{
class Program
{
static void Main(string[] args)
{
// 1. Создание объекта Document
Document document = new Document();
// 2. Непосредственная загрузка HTML-файла
document.LoadFromFile(@"C:\Users\Administrator\Desktop\MyHtml.html", FileFormat.Html);
// 3. Сохранение в документ Word
document.SaveToFile("HtmlToWord.docx", FileFormat.Docx);
// 4. Освобождение ресурсов
document.Dispose();
}
}
}
Этот способ позволяет выполнить преобразование за один шаг — метод Document.LoadFromFile() напрямую поддерживает загрузку файлов в формате FileFormat.Html, избавляя от необходимости вручную считывать содержимое файла.
Важные замечания
На практике следует обратить внимание на несколько аспектов, чтобы добиться наилучшего результата преобразования:
Поддержка CSS-стилей : Spire.Doc поддерживает большинство распространённых CSS-свойств, таких как font-size, color, text-align и др. Однако для очень сложных CSS-макетов возможны незначительные расхождения в итоговом отображении.
Пути к изображениям : теги <img> в HTML автоматически преобразуются в изображения в Word. Убедитесь, что пути к изображениям (как локальные, так и URL-адреса) доступны во время конвертации.
Табличная вёрстка : для сложных таблиц рекомендуется избегать специальных свойств вроде table-layout: fixed, чтобы обеспечить корректное отображение таблиц в Word.
Производительность : для больших файлов с большим объёмом содержимого или изображениями высокого разрешения рекомендуется выполнять преобразование в фоновом потоке или асинхронной задаче, чтобы не блокировать основной поток.
Заключение
С помощью Spire.Doc for .NET разработчики могут реализовать высококачественное и высокоточное взаимное преобразование HTML и Word в проектах на C#, используя минимальный объём кода. Будь то обработка динамически генерируемых HTML-строк или пакетное преобразование существующих HTML-файлов — эта библиотека предлагает стабильное и эффективное решение. Она помогает быстро выстроить автоматизированный процесс работы с документами, позволяя сосредоточиться на бизнес-логике приложения.
Как лаконично проверить структуру массива или кортежа?
С выходом C# 11 стали доступны списковые паттерны (list patterns), которые позволяют сопоставлять коллекции по элементам, как с начала, так и с конца — просто, читаемо и без лишних условий.
Первое условие проверяет, что массив начинается на 1, 2, заканчивается 10, и содержит что угодно посередине (..). Это особенно полезно при валидации данных, когда важна структура последовательности, но не все элементы. Списковые паттерны упрощают такие проверки, делая код более декларативным и понятным.
Второе — определяет, что точка находится в первой четверти, если обе координаты больше нуля. Такой подход позволяет сопоставлять элементы кортежей напрямую в условиях, избегая промежуточных переменных. Это делает логику более компактной и выразительной, особенно при работе с координатами, диапазонами или другими структурированными данными.
Списковые паттерны также поддерживают вложенные проверки, сопоставление с отбросом (_), и комбинацию с другими паттернами C#. Они интегрируются с switch-выражениями и pattern matching, предоставляя единообразный способ работы с различными структурами данных. Это существенно сокращает количество boilerplate-кода и улучшает читаемость при работе с массивами, списками, кортежами и другими индексируемыми коллекциями.
Ещё больше полезного контента в телеграмм канале EasyProger - https://t.me/easyprogers
Inject dotnet runtime на C#
Записал видео, про встраивание dotnet runtime-а в чужое приложение с помощью C# file-based application без дополнительных unmanaged библиотек (т.е. без C++, на C# и assembler-е). Использую DLL Injection (через CreateRemoteThread), C# ассемблер (Iced), P/Invoke source generator для WinAPI (Microsoft.Windows.CsWin32) и Spectre.Console (CLI).
Ну и на всякий закину ссылки на ВКВидео\Рутуб у кого с ютубом плохо.
Как читать Excel в C#: Excel в DataTable и базу данных
Работа с файлами Excel является распространенной задачей во многих приложениях на C#, будь то для анализа данных, отчетности или интеграции с базами данных. В этом блоге мы рассмотрим, как читать файлы Excel в C# с использованием бесплатной библиотеки Excel для .NET. Мы охватим:
Чтение рабочего листа Excel в C#
Импорт данных в DataTable
Сохранение данных Excel в базу данных (MySQL)
Библиотека C# для чтения Excel
Для работы с файлами Excel в C# мы будем использовать Free Spire.XLS для .NET — мощную библиотеку, которая позволяет разработчикам создавать, читать, редактировать и конвертировать файлы Excel без зависимости от Microsoft Office.
Установка
Установите библиотеку через диспетчер пакетов NuGet:
Install-Package FreeSpire.XLS
Чтение рабочего листа Excel в C#
Чтобы прочитать рабочий лист Excel, вы можете использовать следующий фрагмент кода на C#. Этот пример демонстрирует, как загрузить файл Excel и пройтись по его строкам и столбцам.
Основные шаги
Создайте объект Workbook: Инициализируйте рабочую книгу для работы.
Загрузите файл Excel: Укажите путь к вашему файлу Excel.
Получите доступ к рабочему листу: Получите первый рабочий лист из книги.
Получите диапазон данных: Извлеките диапазон ячеек, содержащих данные.
Переберите строки и столбцы: Выведите значение каждой ячейки.
using Spire.Xls;
namespace ReadExcelData
{
class Program
{
static void Main(string[] args)
{
// Создайте объект Workbook
Workbook wb = new Workbook();
// Загрузите существующий файл Excel
wb.LoadFromFile("C:\\Users\\Administrator\\Desktop\\Input.xlsx");
// Получите первый рабочий лист
Worksheet sheet = wb.Worksheets[0];
// Получите диапазон ячеек, содержащих данные
CellRange locatedRange = sheet.AllocatedRange;
// Переберите строки
for (int i = 0; i < locatedRange.Rows.Length; i++)
{
// Переберите столбцы
for (int j = 0; j < locatedRange.Rows[i].ColumnCount; j++)
{
// Получите данные конкретной ячейки
string cellValue = locatedRange[i + 1, j + 1].Value?.ToString() ?? "N/A";
// Выравните вывод с шириной 22
Console.Write($"{cellValue,-22}");
}
Console.WriteLine();
}
}
}
}
Импорт данных из Excel в DataTable
Чтобы импортировать данные из файла Excel в DataTable, вы можете использовать следующий фрагмент кода. Этот пример связывает импортированные данные с DataGridView для удобного визуального отображения.
Основные шаги
Создайте форму: Используйте приложение Windows Forms.
Загрузите файл Excel: Как и в предыдущем разделе, загрузите ваш файл Excel.
Экспортируйте данные в DataTable: Используйте метод ExportDataTable для конвертации данных.
Свяжите DataTable с DataGridView: Отобразите данные в элементе управления DataGridView.
using Spire.Xls;
using System.Data;
namespace ReadExcelIntoDataTable
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// Создайте объект Workbook
Workbook wb = new Workbook();
// Загрузите существующий файл Excel
wb.LoadFromFile("C:\\Users\\Administrator\\Desktop\\Input.xlsx");
// Получите первый рабочий лист
Worksheet sheet = wb.Worksheets[0];
// Экспортируйте данные из рабочего листа в DataTable
DataTable dataTable = sheet.ExportDataTable();
// Свяжите DataTable с DataGridView
dataGridView1.DataSource = dataTable;
// Освободите ресурсы
wb.Dispose();
}
}
}
Сохранение данных Excel в базу данных (MySQL)
Наконец, для сохранения импортированных данных в базу данных MySQL вы можете использовать следующий фрагмент кода. Этот пример извлекает данные из листа Excel и вставляет их в таблицу MySQL.
Основные шаги
Загрузите документ Excel: Как и в предыдущих шагах, загрузите ваш файл Excel.
Извлеките заголовки: Получите строку заголовков для использования в качестве имен столбцов в вашей базе данных.
Соберите данные: Извлеките каждую запись из строк данных.
Подключитесь к MySQL: Установите соединение с базой данных MySQL.
Создайте таблицу базы данных: Динамически создайте SQL для создания таблицы на основе заголовков.
Вставьте данные: Используйте параметризованные запросы для вставки каждой записи в базу данных.
using Spire.Xls;
using MySql.Data.MySqlClient;
namespace ExcelToMySQL
{
class Program
{
static void Main(string[] args)
{
// Создайте объект Workbook
Workbook wb = new Workbook();
// Загрузите документ Excel
wb.LoadFromFile("C:\\Users\\Administrator\\Desktop\\Input.xlsx");
// Получите конкретный лист
Worksheet sheet = wb.Worksheets[0];
// Извлеките заголовки
List<string> headers = new List<string>();
for (int col = 1; col <= sheet.LastColumn; col++)
{
string header = sheet.Range[1, col].Value?.ToString();
// Удалите пробелы, чтобы избежать конфликтов с именами столбцов MySQL
string cleanHeader = header?.Replace(" ", "");
headers.Add($"`{cleanHeader}`");
}
// Извлеките данные
List<List<string>> data = new List<List<string>>();
for (int row = 2; row <= sheet.LastRow; row++)
{
List<string> record = new List<string>();
for (int col = 1; col <= sheet.LastColumn; col++)
{
record.Add(sheet.Range[row, col].Value?.ToString() ?? string.Empty);
}
data.Add(record);
}
// Установите соединение с базой данных MySQL
string connectionString = "server=localhost;user=root;password=admin;database=excel_db;";
using (MySqlConnection connection = new MySqlConnection(connectionString))
{
connection.Open();
// Создайте таблицу с динамическими столбцами на основе заголовков
List<string> columns = new List<string>();
foreach (string header in headers)
{
// Предполагая, что все значения заголовков являются VARCHAR для простоты; настройте типы по мере необходимости
columns.Add($"{header} VARCHAR(255)");
}
// Создайте таблицу в базе данных
string columnsSql = string.Join(", ", columns);
string createTableQuery = $@"
CREATE TABLE IF NOT EXISTS my_table (
id INT AUTO_INCREMENT PRIMARY KEY,
{columnsSql}
)";
// Выполните запрос на создание таблицы
using (MySqlCommand createCommand = new MySqlCommand(createTableQuery, connection))
{
createCommand.ExecuteNonQuery();
}
// Подготовьте SQL-запрос INSERT
string placeholders = string.Join(", ", new string[headers.Count].Select(h => "?"));
string insertQuery = $"INSERT INTO my_table ({string.Join(", ", headers.Select(h => h.Trim('`')))}) VALUES ({placeholders})";
// Вставьте данные в таблицу
foreach (List<string> record in data)
{
using (MySqlCommand insertCommand = new MySqlCommand(insertQuery, connection))
{
for (int i = 0; i < record.Count; i++)
{
insertCommand.Parameters.AddWithValue($"@Param{i}", record[i]);
}
insertCommand.ExecuteNonQuery();
}
}
}
Console.WriteLine("Данные успешно экспортированы!");
}
}
}
Заключение
В этой статье мы рассмотрели, как читать данные из файлов Excel с помощью C#, импортировать эти данные в DataTable и сохранять их в базу данных MySQL. Используя библиотеку Free Spire.XLS, разработчики могут эффективно обрабатывать файлы Excel без необходимости в Microsoft Office. Этот подход может значительно улучшить рабочие процессы обработки данных в приложениях на C#.
Как конвертировать Word в PNG и JPEG на C#
Введение
Конвертация документов Word (DOC/DOCX) в изображения форматов PNG и JPEG полезна для обмена контентом без необходимости редактирования или для сохранения форматирования документа. В этой статье мы рассмотрим, как выполнить такую конвертацию в C# с использованием библиотеки для обработки документов Word в .NET.
C# библиотека для конвертации Word в изображения
Spire.Doc for .NET предоставляет эффективные методы для преобразования документов Word в изображения. Библиотека поддерживает конвертацию целых документов или отдельных страниц в высококачественные изображения форматов PNG, JPEG и BMP. Она проста в интеграции и предлагает гибкий контроль над разрешением для лучшего качества вывода.
Установка Spire.Doc через NuGet
Для использования Spire.Doc в вашем проекте необходимо установить его через NuGet Package Manager:
Откройте Visual Studio и ваш проект
Перейдите в Tools → NuGet Package Manager → Manage NuGet Packages for Solution
Найдите "Spire.Doc" во вкладке Browse
Выберите пакет и нажмите Install
Альтернативно, вы можете установить его через Package Manager Console, используя команду:
Install-Package Spire.Doc
Эта библиотека предоставляет бесплатную версию с ограниченным функционалом, которую также можно установить через NuGet:
Install-Package FreeSpire.Doc
После установки вы можете начать конвертировать документы Word в изображения, используя примеры кода ниже.
Конвертация DOC/DOCX в PNG
Следующий фрагмент кода демонстрирует, как преобразовать документ Word в изображения PNG:
using Spire.Doc;
using System;
using System.Drawing;
using System.Drawing.Imaging;
using Spire.Doc.Documents;
namespace ConvertWordToPng
{
class Program
{
static void Main(string[] args)
{
// Создаем объект Document
Document doc = new Document();
// Загружаем документ Word
doc.LoadFromFile("C:\\Users\\Administrator\\Desktop\\input.docx");
// Конвертируем весь документ в отдельные изображения
Image[] images = doc.SaveToImages(ImageType.Metafile);
// Проходим по коллекции изображений
for (int i = 0; i < images.Length; i++)
{
// Устанавливаем разрешение для конкретного изображения
Image newimage = ResetResolution(images[i] as Metafile, 150);
// Сохраняем изображение в формате PNG
string outputfile = String.Format("Image-{0}.png", i);
newimage.Save("C:\\Users\\Administrator\\Desktop\\Images\\" + outputfile, ImageFormat.Png);
}
}
// Метод для установки разрешения изображения
public static Image ResetResolution(Metafile mf, float resolution)
{
int width = (int)(mf.Width * resolution / mf.HorizontalResolution);
int height = (int)(mf.Height * resolution / mf.VerticalResolution);
Bitmap bmp = new Bitmap(width, height);
bmp.SetResolution(resolution, resolution);
using (Graphics g = Graphics.FromImage(bmp))
{
g.DrawImage(mf, Point.Empty);
}
return bmp;
}
}
}
Пояснение:
Загрузка документа: Класс Document загружает файл Word по указанному пути
Конвертация в изображения: Метод SaveToImages() преобразует каждую страницу в изображение Metafile
Настройка разрешения: Метод ResetResolution() обеспечивает высокое DPI (150 в данном случае)
Сохранение в PNG: Каждое изображение сохраняется в формате PNG с последовательным именем файла
Конвертация DOC/DOCX в JPEG
Следующий фрагмент кода преобразует документ Word в изображения JPEG:
using Spire.Doc;
using Spire.Doc.Documents;
using System;
using System.Drawing;
using System.Drawing.Imaging;
namespace ConvertWordToJPG
{
class Program
{
static void Main(string[] args)
{
// Создаем объект Document
Document doc = new Document();
// Загружаем документ Word
doc.LoadFromFile("C:\\Users\\Administrator\\Desktop\\input.docx");
// Конвертируем весь документ в отдельные изображения
Image[] images = doc.SaveToImages(ImageType.Bitmap);
// Проходим по коллекции изображений
for (int i = 0; i < images.Length; i++)
{
// Сохраняем изображение в формате JPEG
string outputfile = String.Format("Image-{0}.jpg", i);
images[i].Save("C:\\Users\\Administrator\\Desktop\\Images\\" + outputfile, ImageFormat.Jpeg);
}
}
}
}
Пояснение:
Загрузка документа: Класс Document загружает файл Word аналогично конвертации в PNG
Конвертация в Bitmap: Метод SaveToImages() преобразует страницы в изображения Bitmap
Сохранение в JPEG: Каждое изображение сохраняется в формате JPEG с последовательным именем файла
Заключение
Используя Spire.Doc for .NET, конвертация документов Word в PNG или JPEG становится простой задачей. Библиотека эффективно обрабатывает рендеринг документов и предоставляет гибкость в настройке разрешения и формата изображений. Независимо от того, нужны ли вам высококачественные PNG или сжатые JPEG, Spire.Doc упрощает процесс конвертации на C#.






