Описывается C#-программа вывода отчета в Excel.
Данные берутся из xml-файла, часть которого показана ниже:
<?xml version="1.0" encoding="utf-8"?>
<traffic>
<!-- Описание порции данных -->
<!-- processName - имя процесса -->
<!-- type - вид порции: 0 - входящие данные; 1 - исходящие -->
<!-- sTime - текущее время -->
<!-- cTime - время передачи данных -->
<!-- size - размер порции данных в битах -->
<portion>
<processName>AAM Updates Notifier</processName>
<type>1</type>
<sTime>23.11.2016 3:15:24.824</sTime>
<cTime>23.11.2016 3:15:24.825</cTime>
<size>157798,765625 B</size>
</portion>
<portion>
<processName>conhost</processName>
<type>0</type>
<sTime>23.11.2016 3:15:27.90</sTime>
<cTime>23.11.2016 3:15:27.90</cTime>
<size>323631,9375 B</size>
</portion>
…
</traffic>
В файле фиксируются данные интернет-трафика, наблюдаемого в подключенном к интернет компьютере.
В отчете отображаются сводные данные об обнаруженном и сохраненном в xml-файле трафике (рис. 1).
Рис. 1. Сводный интернет-трафик
Программа реализована как Windows Forms Application.
Файл с данными явно указывается в программе, поэтому форма содержит только дну кнопку (рис. 2).
Рис. 2. Форма приложения
Создав проект, в окне Solution Explorer выполнить:
правая кнопка мышки на References - Add Reference - .в окне Reference Manager выбрать Assemblies - Extensions - отметить флажком Microsoft.Office.Interop.Excel - OK (рис. 3. и рис. 4).
Рис. 3. Solution Explorer - References - Add Reference
Рис. 4. Reference Manager - Assemblies - Extensions - Microsoft.Office.Interop.Excel
В use-раздел формы приложения добавить:
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
Заметим, что массивы - это остатки от отладочной версии программы. В принципе, можно обойтись и без них, оперирую полученными выборками.
Загрузка xml-файла выполняется методом ReadXml объекта типа DataSet (portionsDS.ReadXml(fileName);).
Результат portionsDS содержит коллекцию таблиц Tables (DataTableCollection tbls = portionsDS.Tables;), обнаруженных в xml-файле.
Нас интересует первая таблица коллекции (DataTable tbl = tbls[0];), из которой извлекаем коллекцию ее строк:
После завершения обработки коллекции строк таблицы ресурсы, выделенные под portionsDS, освобождаются (portionsDS.Dispose();).
Выборки из списка prtnsList, полученного по данным, загруженным в portionsDS, осуществляются с использованием методов Where, GroupBy, Select и OrderBy.
Данные массивов с именами процессов и итогами выбираются из списка prtnsList методами Select и ToArray.
Очистка списка prtnsList выполняется методом Clear (prtnsList.Clear();).
Созданный объект xlApp типа Excel.Application позволяет плучить получить книгу Excel (Excel.Workbook xlWorkBook;), которая используется для получения первого листа этой книги:
Для работы с ячейками листа создаются объекты типа Excel.Range:
Excel.Range Cells = xlWorkSheet.Cells;
Excel.Range range = xlWorkSheet.Range["B1:B1", System.Type.Missing];
и другие.
При заполнении ячеек листа Excel употреблены следущие свойства и методы:
Заметим, что изменение Style.Font.Size, например Cells[1, 2].Style.Font.Size = 14;, отразится на всех ячейках с текстом стиля Style.
Готовый отчет наблюдаем, обращаясь к свойству Visible объекта xlApp:
Чтение xml-файла, формирование вспомогательных объектов и отчета обеспечивает следующий код:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
namespace WindowsFormsApplication3
{
public partial class Form1 : Form
{
// Порция для отчетов История и Итоги
public struct prtn
{
public string pNm;
public int pTp;
public string sTime, cTime;
public Int64 size;
public int mnt;
public prtn(string pNm2, int pTp2, string sTime2, string cTime2, Int64 size2, int mnt2)
{
pNm = pNm2;
pTp = pTp2;
sTime = sTime2;
cTime = cTime2;
size = size2;
mnt = mnt2;
}
}
// Список порций для отчета Итоги. Формируется в LoadXML()
public List<prtn> prtnsList = new List<prtn>();
// Период наблюдения
public DateTime sTimeMax, sTimeMin;
public Form1()
{
InitializeComponent();
}
// Загружаем xml-файл в DataSet portionsDS, а затем формируем список prtnsList
private bool LoadXML()
{
// Файл с трафиком
string fileName = "C:/AM/2018/outputTrafficXML.xml";
DataSet portionsDS = new DataSet();
try
{
prtnsList.Clear();
prtn prtnCrrnt = new prtn();
string processName; // Имя процесса
int pTp; // 0 - входящие данные; 1 - исходящие
// Текущее время и время порции
DateTime sTime, cTime;
// Символьное представление текущего времени и времени порции с миллисекундами
string sTime2, cTime2;
Int64 size; // Размер порции в битах
string size2; // Символьное представление размера порции
portionsDS.ReadXml(fileName); // Формируем DataSet portionsDS
DataTableCollection tbls = portionsDS.Tables; // Коллекция таблиц (у нас одна таблица traffic)
DataTable tbl = tbls[0]; // Получаем таблицу traffic
DataRowCollection portions = tbl.Rows; // Коллекция строк таблицы
// Период наблюдения
sTimeMax = DateTime.MinValue;
sTimeMin = DateTime.MaxValue;
foreach (DataRow portion in portions)
{
processName = Convert.ToString(portion[0]); // portion["processName"];
pTp = Convert.ToInt32(portion["type"]);
sTime = Convert.ToDateTime(portion["sTime"]);
// Корректируем период наблюдения
if (sTime > sTimeMax) sTimeMax = sTime;
if (sTime < sTimeMin) sTimeMin = sTime;
cTime = Convert.ToDateTime(portion["cTime"]);
sTime2 = Convert.ToString(portion["sTime"]);
cTime2 = Convert.ToString(portion["cTime"]);
size2 = Convert.ToString(portion["size"]);
// Заменяем, например, 157798,765625 B на 157798765625 и преобразовываем затем в Int64
size2 = size2.Replace("B", "");
size2 = size2.Replace(",", "").Trim();
size = Convert.ToInt64(size2);
prtnCrrnt.pNm = processName;
prtnCrrnt.pTp = pTp;
prtnCrrnt.sTime = sTime2;
prtnCrrnt.cTime = cTime2;
prtnCrrnt.size = size;
prtnCrrnt.mnt = 1;
prtnsList.Add(prtnCrrnt);
}
}
catch (System.IO.IOException e)
{
MessageBox.Show("Плохой файл " + fileName + ". Сообщение " + e.Message);
return false;
}
finally
{
portionsDS.Dispose();
}
return true;
}
// Вывод итогов
private void buttonTotals_Click(object sender, EventArgs e)
{
// Формируем по xml-файлу список prtnsList
if (!LoadXML()) return;
// Имена всех процессы без повторений в алфавитном порядке
var prtnsListGrouped = prtnsList.GroupBy(prtn => prtn.pNm).Select(
grp => new prtn { pNm = grp.Key }).OrderBy(prtn => prtn.pNm);
// Итоги по входящим данным
var prtnsListGrouped_0 = prtnsList.Where(prtn => prtn.pTp == 0).GroupBy(prtn => prtn.pNm).Select(
grp => new prtn { pNm = grp.Key, size = grp.Sum(prtn => prtn.size), mnt = grp.Sum(prtn => prtn.mnt) }).OrderBy(prtn => prtn.pNm);
// Итоги по исходящим данным
var prtnsListGrouped_1 = prtnsList.Where(prtn => prtn.pTp == 1).GroupBy(prtn => prtn.pNm).Select(
grp => new prtn { pNm = grp.Key, size = grp.Sum(prtn => prtn.size), mnt = grp.Sum(prtn => prtn.mnt) }).OrderBy(prtn => prtn.pNm);
// Имена всех процессов
string[] arrPrcAll = prtnsListGrouped.Select(prtn => prtn.pNm).ToArray();
// Входящие порции
// Имена процессов
string[] arrPrcIn = prtnsListGrouped_0.Select(prtn => prtn.pNm).ToArray();
// Суммарные порции входящих данных соответственно процессов
Int64[] prtnInTotal = prtnsListGrouped_0.Select(prtn => prtn.size).ToArray();
// Число порций входящих данных
int[] mntInTotal = prtnsListGrouped_0.Select(prtn => prtn.mnt).ToArray();
// Исходящие порции
// Имена процессов
string[] arrPrcOut = prtnsListGrouped_1.Select(prtn => prtn.pNm).ToArray();
// Суммарные порции исходящих данных
Int64[] prtnOutTotal = prtnsListGrouped_1.Select(prtn => prtn.size).ToArray();
// Число порций исходящих данных соответственно процессов
int[] mntOutTotal = prtnsListGrouped_1.Select(prtn => prtn.mnt).ToArray();
// Вывод отчета в Excel
Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
if (xlApp == null)
{
MessageBox.Show("Excel не найден");
return;
}
Excel.Workbook xlWorkBook;
xlWorkBook = xlApp.Workbooks.Add();
Excel.Worksheet xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
// Все ячейки листа xlWorkSheet
Excel.Range Cells = xlWorkSheet.Cells;
// Высота текста заголовка отчета
Cells[1, 2].Font.Size = 14;
// Заголовки
Cells[1, 2] = "Интернет-трафик за период с " + sTimeMin + " по " + sTimeMax;
Cells[2, 2] = "№";
Cells[2, 3] = "Процесс";
Cells[2, 4] = "Получено";
Cells[2, 6] = "Передано";
Cells[3, 4] = "Бит";
Cells[3, 5] = "Число порций";
Cells[3, 6] = "Бит";
Cells[3, 7] = "Число порций";
int lstClmn = 8; // Следующий после крайнего справа столбца
// Выравнивание
for (int i = 2; i < 4; i++)
for (int j = 2; j < lstClmn; j++)
Cells[i, j].HorizontalAlignment = Microsoft.Office.Interop.Excel.Constants.xlCenter;
// Ширина столбцов
Excel.Range range = xlWorkSheet.Range["B1:B1", System.Type.Missing];
range.EntireColumn.ColumnWidth = 7;
range = xlWorkSheet.Range["C1:C1", System.Type.Missing];
range.EntireColumn.ColumnWidth = 30;
range = xlWorkSheet.Range["D1:G1", System.Type.Missing];
range.EntireColumn.ColumnWidth = 15;
// Границы ячеек с заголовками
Cells[2, 2].Borders(1).ColorIndex = 1; // Граница слева
Cells[2, 3].Borders(1).ColorIndex = 1; // Граница слева
Cells[2, 3].Borders(2).ColorIndex = 1; // Граница справа
Cells[2, 5].Borders(2).ColorIndex = 1; // Граница справа
Cells[2, 7].Borders(2).ColorIndex = 1; // Граница справа
for (int i = 2; i < lstClmn; i++) Cells[2, i].Borders(3).ColorIndex = 1; // Граница сверху
for (int i = 4; i < lstClmn; i++) Cells[3, i].Borders(3).ColorIndex = 1; // Граница сверху
for (int i = 2; i < lstClmn; i++) Cells[3, i].Borders(4).ColorIndex = 1; // Граница снизу
for (int i = 2; i < lstClmn; i++)
for (int k = 1; k < 3; k++)
Cells[3, i].Borders(k).ColorIndex = 1; // Границы слева и справа
// Объединение ячеек
range = xlWorkSheet.get_Range("D2:E2");
range.Merge(Type.Missing);
range = xlWorkSheet.get_Range("F2:G2");
range.Merge(Type.Missing);
// Всего имен процессов
int prcMnt = arrPrcAll.Length;
// Границы ячеек с данными
for (int i = 4; i < 4 + prcMnt; i++)
for (int j = 2; j < lstClmn; j++)
for (int k = 1; k < 5; k++)
Cells[i, j].Borders(k).ColorIndex = 1; // Границы слева, справа, сверху и снизу
// Выводим номера строк и имена процессов
int n = 0;
for (int i = 0; i < prcMnt; i++)
{
n++;
Cells[i + 4, 2] = n;
Cells[i + 4, 3] = arrPrcAll[i];
// Перенос текста
Cells[i + 4, 3].WrapText = true;
}
// Число входящих и исходящих данных в итоговых массивах
int inMnt = arrPrcIn.Length;
int outMnt = arrPrcOut.Length;
// Получено
for (int i = 0; i < prcMnt; i++)
{
string prcNm = arrPrcAll[i];
for (int j = 0; j < inMnt; j++)
{
string prcNm2 = arrPrcIn[j];
if (prcNm2 == prcNm)
{
Cells[i + 4, 4] = prtnInTotal[j];
Cells[i + 4, 5] = mntInTotal[j];
break;
}
}
}
// Передано
for (int i = 0; i < prcMnt; i++)
{
string prcNm = arrPrcAll[i];
for (int j = 0; j < outMnt; j++)
{
string prcNm2 = arrPrcOut[j];
if (prcNm2 == prcNm)
{
Cells[i + 4, 6] = prtnOutTotal[j];
Cells[i + 4, 7] = mntOutTotal[j];
break;
}
}
}
// Показываем отчет Итоги, выведенный в Excel
xlApp.Visible = true;
}
}
}
Используя те же объекты (структуру prtn, список prtnsList и пр.) по данным xml-файла формируется отчет История процесса (рис. 5).
Рис. 5. Отчет история процесса
Вывод этого отчета обеспечивает следующий код (приводится отладочный вариант, в котором имя процесса задется в теле процедуры):
// Вывод истории процесса
private void buttonHistory_Click(object sender, EventArgs e)
{
// Формируем по xml-файлу список prtnsList
if (!LoadXML()) return;
string pNm = "opera"; // Имя процесса
// Отбираем входящие порции для процесса с именем pNm
// Отбор по имени
var prtnsL = prtnsList.Where(prtn => prtn.pNm == pNm).Select(
prtn => new { pTp = prtn.pTp, sTime = prtn.sTime, cTime = prtn.cTime, size = prtn.size });
// Отбор входящих порций процесса pNm
var prtnsL_0 = prtnsL.Where(prtn => prtn.pTp == 0).Select(prtn => new { sTime = prtn.sTime, cTime = prtn.cTime, size = prtn.size });
// Число входящих порций процесса pNm
int inMnt = prtnsL_0.Count();
// Текущее время для входных данных (время замера входных данных)
string[] arrTCrrntIn = new string[inMnt];
// Замеренное время для входных данных
string[] arrTIn = new string[inMnt];
// Размеры входящих порций процесса pNm
Int64[] prtnIn = new Int64[inMnt];
int k = 0;
// Массивы с временем и размерами входящих порций
foreach (var p in prtnsL_0)
{
arrTCrrntIn[k] = p.sTime;
arrTIn[k] = p.cTime;
prtnIn[k] = p.size;
k++;
}
// Отбор исходящих порций процесса pNm
var prtnsL_1 = prtnsL.Where(prtn => prtn.pTp == 1).Select(prtn => new { sTime = prtn.sTime, cTime = prtn.cTime, size = prtn.size });
// Число исходящих порций процесса pNm
int outMnt = prtnsL_1.Count();
// Текущее время для исходящих данных (время замера выходных данных)
string[] arrTCrrntOut = new string[inMnt];
// Замеренное время для исходящих данных
string[] arrTOut = new string[outMnt];
// Размеры исходящих порций процесса pNm
Int64[] prtnOut = new Int64[outMnt];
k = 0;
// Массивы с временем и размерами исходящих порций процесса pNm
foreach (var p in prtnsL_1)
{
arrTCrrntOut[k] = p.sTime;
arrTOut[k] = p.cTime;
prtnOut[k] = p.size;
k++;
}
Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
if (xlApp == null)
{
MessageBox.Show("Excel не найден");
return;
}
Excel.Workbook xlWorkBook;
Excel.Worksheet xlWorkSheet;
//object misValue = System.Reflection.Missing.Value;
//xlWorkBook = xlApp.Workbooks.Add(misValue);
xlWorkBook = xlApp.Workbooks.Add();
xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
// Все ячейки листа xlWorkSheet
Excel.Range Cells = xlWorkSheet.Cells;
// Высота текста заголовка отчета
Cells[1, 2].Font.Size = 12;
// Заголовки
Cells[1, 2] = "История процесса " + pNm + " за период с " + sTimeMin + " по " + sTimeMax;
Cells[2, 2] = "№";
Cells[2, 3] = "Текущее время";
Cells[2, 4] = "Время порции";
Cells[2, 5] = "Размер порции, бит";
Cells[3, 2] = "Входящие данные";
Cells[inMnt + 4, 2] = "Исходящие данные";
int lstClmn = 6; // Следующий после крайнего справа столбца
// Выравнивание
for (int i = 2; i < lstClmn; i++) xlWorkSheet.Cells[2, i].HorizontalAlignment = Microsoft.Office.Interop.Excel.Constants.xlCenter;
// Границы ячеек
for (int i = 2; i < 5 + inMnt + outMnt; i++)
for (int j = 2; j < lstClmn; j++)
for (k = 1; k < 5; k++)
{
if (i == 3 || i == 4 + inMnt) continue;
Cells[i, j].Borders(k).ColorIndex = 1; // Границы слева, справа, сверху и снизу
}
// Границы ячеек строках 3 и 4 + inMnt с текстом Входящие данные и Исходящие данные
Cells[3, 2].Borders(1).ColorIndex = 1; // Граница слева
Cells[3, 5].Borders(2).ColorIndex = 1; // Граница справа
Cells[4 + inMnt, 2].Borders(1).ColorIndex = 1; // Граница слева
Cells[4 + inMnt, 5].Borders(2).ColorIndex = 1; // Граница справа
// Ширина столбцов
Excel.Range range = xlWorkSheet.Range["B1:B1", System.Type.Missing];
range.EntireColumn.ColumnWidth = 8;
range = xlWorkSheet.Range["C1:E1", System.Type.Missing];
range.EntireColumn.ColumnWidth = 22;
// Вывод входящих данных процесса pNm
for(int i = 0; i < inMnt; i++)
{
Cells[i + 4, 2] = i + 1;
Cells[i + 4, 3] = arrTCrrntIn[i];
Cells[i + 4, 4] = arrTIn[i];
Cells[i + 4, 5] = prtnIn[i];
}
// Вывод исходящих данных процесса pNm
for(int i = 0; i < outMnt; i++)
{
Cells[i + inMnt + 5, 2] = i + 1;
Cells[i + inMnt + 5, 3] = arrTCrrntOut[i];
Cells[i + inMnt + 5, 4] = arrTOut[i];
Cells[i + inMnt + 5, 5] = prtnOut[i];
}
// Показываем отчет История процесса, выведенный в Excel
xlApp.Visible = true;
}