Initial commit clean history
This commit is contained in:
commit
c963c4d9e9
19 changed files with 2096 additions and 0 deletions
17
MailPrintConfig/MailPrintConfig.csproj
Normal file
17
MailPrintConfig/MailPrintConfig.csproj
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<RootNamespace>MailPrintConfig</RootNamespace>
|
||||
<AssemblyName>MailPrintConfig</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
900
MailPrintConfig/MainForm.cs
Normal file
900
MailPrintConfig/MainForm.cs
Normal file
|
|
@ -0,0 +1,900 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Drawing.Printing;
|
||||
|
||||
namespace MailPrintConfig;
|
||||
|
||||
public class MainForm : Form
|
||||
{
|
||||
private const int LabelW = 140;
|
||||
private const int CtrlW = 240;
|
||||
private const int RowH = 28;
|
||||
private const int Pad = 12;
|
||||
|
||||
// ── Allgemein ─────────────────────────────────────────────────
|
||||
private TextBox txtInterval = null!, txtSubjectFilter = null!, txtTempDir = null!, txtSumatraPath = null!;
|
||||
private CheckBox chkDelete = null!, chkMarkRead = null!;
|
||||
|
||||
// ── Web API ───────────────────────────────────────────────────
|
||||
private TextBox txtApiPort = null!, txtApiKey = null!;
|
||||
private CheckBox chkBindAll = null!;
|
||||
|
||||
// ── Grids ─────────────────────────────────────────────────────
|
||||
private DataGridView gridProfiles = null!, gridAccounts = null!, gridFolders = null!;
|
||||
|
||||
// ── Filter ────────────────────────────────────────────────────
|
||||
private TextBox txtGlobalAllowed = null!, txtGlobalBlocked = null!;
|
||||
|
||||
// ── Config / Steuerung ────────────────────────────────────────
|
||||
private TextBox txtConfigPath = null!;
|
||||
private Button btnLoad = null!, btnSave = null!, btnStartStop = null!, btnInstall = null!, btnUninstall = null!, btnSvcStart = null!, btnSvcStop = null!;
|
||||
private Label lblStatus = null!;
|
||||
private System.Diagnostics.Process? _proc;
|
||||
private System.Windows.Forms.Timer _timer = null!;
|
||||
|
||||
public MainForm()
|
||||
{
|
||||
Text = "MailPrint Konfiguration";
|
||||
Width = 1100; MinimumSize = new Size(900, 700);
|
||||
Font = new Font("Segoe UI", 9f);
|
||||
StartPosition = FormStartPosition.CenterScreen;
|
||||
|
||||
BuildUI();
|
||||
AutoDetectConfigPath();
|
||||
|
||||
Load += (_, _) => { if (File.Exists(txtConfigPath.Text)) LoadConfig(); };
|
||||
|
||||
_timer = new System.Windows.Forms.Timer { Interval = 1500 };
|
||||
_timer.Tick += (_, _) => RefreshStartStop();
|
||||
_timer.Start();
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// UI
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
private void BuildUI()
|
||||
{
|
||||
var tabs = new TabControl { Dock = DockStyle.Fill };
|
||||
tabs.TabPages.Add(BuildProfilesTab());
|
||||
tabs.TabPages.Add(BuildAccountsTab());
|
||||
tabs.TabPages.Add(BuildFoldersTab());
|
||||
tabs.TabPages.Add(BuildFilterTab());
|
||||
tabs.TabPages.Add(BuildApiTab());
|
||||
tabs.TabPages.Add(BuildGeneralTab());
|
||||
tabs.TabPages.Add(BuildAboutTab());
|
||||
Controls.Add(tabs);
|
||||
|
||||
var bottom = new Panel { Dock = DockStyle.Bottom, Height = 84 };
|
||||
int x = Pad;
|
||||
|
||||
// Zeile 1: Laden | Speichern | [Pfad…] [____path____]
|
||||
btnLoad = Btn("Laden", x, 10, 80); x += 86;
|
||||
btnSave = Btn("Speichern", x, 10, 84); x += 90;
|
||||
var btnBrowse = Btn("Pfad…", x, 10, 60); x += 66;
|
||||
txtConfigPath = new TextBox { Left = x, Top = 12, Width = 320, Anchor = AnchorStyles.Left | AnchorStyles.Top };
|
||||
|
||||
// Zeile 2: EXE starten | EXE stoppen | Dienst installieren | Dienst deinstallieren | Dienst starten | Dienst beenden
|
||||
int x2 = Pad;
|
||||
btnStartStop = Btn("▶ EXE starten", x2, 44, 120, Color.LightGreen); x2 += 126;
|
||||
btnInstall = Btn("⚙ Dienst installieren", x2, 44, 145, Color.LightBlue); x2 += 151;
|
||||
btnUninstall = Btn("✖ Dienst deinstall.", x2, 44, 140, Color.LightSalmon); x2 += 146;
|
||||
btnSvcStart = Btn("▶ Dienst starten", x2, 44, 125, Color.PaleGreen); x2 += 131;
|
||||
btnSvcStop = Btn("⏹ Dienst beenden", x2, 44, 120, Color.LightCoral);
|
||||
|
||||
lblStatus = new Label { Left = Pad, Top = 68, Width = 820, Height = 16, AutoSize = false, ForeColor = Color.DarkGreen };
|
||||
|
||||
btnLoad.Click += (_, _) => LoadConfig();
|
||||
btnSave.Click += (_, _) => SaveConfig();
|
||||
btnStartStop.Click += (_, _) => _ = ToggleExeAsync();
|
||||
btnBrowse.Click += (_, _) => BrowseConfig();
|
||||
btnInstall.Click += (_, _) => _ = ServiceActionAsync("install");
|
||||
btnUninstall.Click += (_, _) => _ = ServiceActionAsync("uninstall");
|
||||
btnSvcStart.Click += (_, _) => _ = ServiceActionAsync("start");
|
||||
btnSvcStop.Click += (_, _) => _ = ServiceActionAsync("stop");
|
||||
|
||||
bottom.Controls.AddRange([btnLoad, btnSave, btnBrowse, txtConfigPath,
|
||||
btnStartStop, btnInstall, btnUninstall, btnSvcStart, btnSvcStop, lblStatus]);
|
||||
Controls.Add(bottom);
|
||||
}
|
||||
|
||||
// ── Tab: Drucker-Profile ──────────────────────────────────────
|
||||
private TabPage BuildProfilesTab()
|
||||
{
|
||||
var tab = new TabPage("Drucker-Profile");
|
||||
tab.Controls.Add(new Label
|
||||
{
|
||||
Text = "Profil = Drucker + Papierfach + optionale Absender-Filter (überschreibt globale Filter).",
|
||||
Left = Pad, Top = Pad, Width = 1060, Height = 18, ForeColor = Color.DimGray
|
||||
});
|
||||
|
||||
gridProfiles = new DataGridView
|
||||
{
|
||||
Left = Pad, Top = 32, Width = 1060, Height = 500,
|
||||
Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom,
|
||||
AllowUserToAddRows = true, AllowUserToDeleteRows = false,
|
||||
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None,
|
||||
ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize,
|
||||
EditMode = DataGridViewEditMode.EditOnEnter,
|
||||
SelectionMode = DataGridViewSelectionMode.FullRowSelect,
|
||||
ScrollBars = ScrollBars.Both
|
||||
};
|
||||
|
||||
gridProfiles.Columns.Add(new DataGridViewTextBoxColumn { Name = "PName", HeaderText = "Profil-Name", Width = 120 });
|
||||
|
||||
var colPrinter = new DataGridViewComboBoxColumn { Name = "Printer", HeaderText = "Drucker", FlatStyle = FlatStyle.Flat, Width = 280 };
|
||||
colPrinter.Items.Add("");
|
||||
foreach (string p in PrinterSettings.InstalledPrinters) colPrinter.Items.Add(p);
|
||||
gridProfiles.Columns.Add(colPrinter);
|
||||
|
||||
var colSource = new DataGridViewComboBoxColumn { Name = "Source", HeaderText = "Papierfach", FlatStyle = FlatStyle.Flat, Width = 150 };
|
||||
colSource.Items.Add("");
|
||||
gridProfiles.Columns.Add(colSource);
|
||||
|
||||
gridProfiles.Columns.Add(new DataGridViewTextBoxColumn { Name = "Copies", HeaderText = "Kopien", Width = 55 });
|
||||
|
||||
var colDuplex = new DataGridViewComboBoxColumn { Name = "Duplex", HeaderText = "Duplex", FlatStyle = FlatStyle.Flat, Width = 110 };
|
||||
colDuplex.Items.AddRange(["Aus", "Lange Seite", "Kurze Seite"]);
|
||||
gridProfiles.Columns.Add(colDuplex);
|
||||
|
||||
gridProfiles.Columns.Add(new DataGridViewTextBoxColumn { Name = "Allowed", HeaderText = "E-Mail-Whitelist (Komma)", Width = 220 });
|
||||
gridProfiles.Columns.Add(new DataGridViewTextBoxColumn { Name = "Blocked", HeaderText = "E-Mail-Blacklist (Komma)", Width = 220 });
|
||||
|
||||
gridProfiles.DataError += (_, e) => e.ThrowException = false;
|
||||
gridProfiles.CellValueChanged += GridProfiles_CellValueChanged;
|
||||
gridProfiles.CurrentCellDirtyStateChanged += (_, _) =>
|
||||
{
|
||||
if (gridProfiles.IsCurrentCellDirty) gridProfiles.CommitEdit(DataGridViewDataErrorContexts.Commit);
|
||||
};
|
||||
|
||||
AttachContextMenu(gridProfiles);
|
||||
tab.Controls.Add(gridProfiles);
|
||||
return tab;
|
||||
}
|
||||
|
||||
// ── Tab: Postfächer ───────────────────────────────────────────
|
||||
private TabPage BuildAccountsTab()
|
||||
{
|
||||
var tab = new TabPage("Postfächer");
|
||||
tab.Controls.Add(new Label
|
||||
{
|
||||
Text = "Jedes Postfach wird unabhängig abgerufen und druckt auf das zugeordnete Drucker-Profil.",
|
||||
Left = Pad, Top = Pad, Width = 1060, Height = 18, ForeColor = Color.DimGray
|
||||
});
|
||||
|
||||
gridAccounts = new DataGridView
|
||||
{
|
||||
Left = Pad, Top = 32, Width = 1060, Height = 500,
|
||||
Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom,
|
||||
AllowUserToAddRows = true, AllowUserToDeleteRows = false,
|
||||
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None,
|
||||
ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize,
|
||||
EditMode = DataGridViewEditMode.EditOnEnter,
|
||||
SelectionMode = DataGridViewSelectionMode.FullRowSelect,
|
||||
ScrollBars = ScrollBars.Both
|
||||
};
|
||||
|
||||
gridAccounts.Columns.Add(new DataGridViewTextBoxColumn { Name = "AName", HeaderText = "Name", Width = 100 });
|
||||
|
||||
var colProto = new DataGridViewComboBoxColumn { Name = "Protocol", HeaderText = "Protokoll", FlatStyle = FlatStyle.Flat, Width = 70 };
|
||||
colProto.Items.AddRange(["IMAP", "POP3"]);
|
||||
gridAccounts.Columns.Add(colProto);
|
||||
|
||||
gridAccounts.Columns.Add(new DataGridViewTextBoxColumn { Name = "Host", HeaderText = "Host", Width = 200 });
|
||||
gridAccounts.Columns.Add(new DataGridViewTextBoxColumn { Name = "Port", HeaderText = "Port", Width = 52 });
|
||||
gridAccounts.Columns.Add(new DataGridViewCheckBoxColumn { Name = "Ssl", HeaderText = "SSL", Width = 38 });
|
||||
gridAccounts.Columns.Add(new DataGridViewTextBoxColumn { Name = "User", HeaderText = "Benutzername", Width = 180 });
|
||||
var colPass = new DataGridViewTextBoxColumn { Name = "Pass", HeaderText = "Passwort", Width = 180 };
|
||||
colPass.DefaultCellStyle.NullValue = null;
|
||||
colPass.DefaultCellStyle.Font = new Font("Courier New", 9f);
|
||||
gridAccounts.Columns.Add(colPass);
|
||||
gridAccounts.Columns.Add(new DataGridViewTextBoxColumn { Name = "Folder", HeaderText = "Ordner", Width = 80 });
|
||||
|
||||
var colProfile = new DataGridViewComboBoxColumn { Name = "Profile", HeaderText = "Drucker-Profil", FlatStyle = FlatStyle.Flat, Width = 150 };
|
||||
colProfile.Items.Add("");
|
||||
gridAccounts.Columns.Add(colProfile);
|
||||
|
||||
gridAccounts.DataError += (_, e) => e.ThrowException = false;
|
||||
// Passwort maskieren
|
||||
gridAccounts.CellFormatting += (_, e) =>
|
||||
{
|
||||
if (e.RowIndex >= 0 && gridAccounts.Columns[e.ColumnIndex].Name == "Pass" && e.Value is string pw)
|
||||
e.Value = new string('●', pw.Length);
|
||||
};
|
||||
gridAccounts.CellBeginEdit += (_, e) =>
|
||||
{
|
||||
if (e.RowIndex >= 0 && gridAccounts.Columns[e.ColumnIndex].Name == "Pass")
|
||||
{
|
||||
var cell = gridAccounts.Rows[e.RowIndex].Cells["Pass"];
|
||||
// beim Bearbeiten Klartext zeigen – Value ist bereits der echte Wert
|
||||
}
|
||||
};
|
||||
gridProfiles.CellValueChanged += (_, _) => RefreshProfileDropdowns();
|
||||
|
||||
AttachContextMenu(gridAccounts);
|
||||
tab.Controls.Add(gridAccounts);
|
||||
return tab;
|
||||
}
|
||||
|
||||
// ── Tab: Ordner-Überwachung ────────────────────────────────────
|
||||
private TabPage BuildFoldersTab()
|
||||
{
|
||||
var tab = new TabPage("Ordner-Überwachung");
|
||||
tab.Controls.Add(new Label
|
||||
{
|
||||
Text = "PDFs in diesen Ordnern werden automatisch erkannt und gedruckt.",
|
||||
Left = Pad, Top = Pad, Width = 1060, Height = 18, ForeColor = Color.DimGray
|
||||
});
|
||||
|
||||
gridFolders = new DataGridView
|
||||
{
|
||||
Left = Pad, Top = 32, Width = 1060, Height = 500,
|
||||
Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom,
|
||||
AllowUserToAddRows = true, AllowUserToDeleteRows = false,
|
||||
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None,
|
||||
ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize,
|
||||
EditMode = DataGridViewEditMode.EditOnEnter,
|
||||
SelectionMode = DataGridViewSelectionMode.FullRowSelect,
|
||||
ScrollBars = ScrollBars.Both
|
||||
};
|
||||
|
||||
gridFolders.Columns.Add(new DataGridViewTextBoxColumn { Name = "FName", HeaderText = "Name", Width = 120 });
|
||||
gridFolders.Columns.Add(new DataGridViewTextBoxColumn { Name = "FPath", HeaderText = "Pfad", Width = 400 });
|
||||
gridFolders.Columns.Add(new DataGridViewButtonColumn { Name = "FBrowse", HeaderText = "", Text = "…", Width = 30 });
|
||||
gridFolders.Columns.Add(new DataGridViewCheckBoxColumn { Name = "FSubfolders",HeaderText = "Unterordner", Width = 90 });
|
||||
gridFolders.Columns.Add(new DataGridViewCheckBoxColumn { Name = "FDelete", HeaderText = "Nach Druck löschen",Width = 120 });
|
||||
|
||||
var colFProfile = new DataGridViewComboBoxColumn { Name = "FProfile", HeaderText = "Drucker-Profil", FlatStyle = FlatStyle.Flat, Width = 180 };
|
||||
colFProfile.Items.Add("");
|
||||
gridFolders.Columns.Add(colFProfile);
|
||||
|
||||
gridFolders.DataError += (_, e) => e.ThrowException = false;
|
||||
gridFolders.CurrentCellDirtyStateChanged += (_, _) =>
|
||||
{
|
||||
if (gridFolders.IsCurrentCellDirty) gridFolders.CommitEdit(DataGridViewDataErrorContexts.Commit);
|
||||
};
|
||||
|
||||
// "…"-Button → Ordner auswählen
|
||||
gridFolders.CellClick += (_, e) =>
|
||||
{
|
||||
if (e.RowIndex < 0 || gridFolders.Columns[e.ColumnIndex].Name != "FBrowse") return;
|
||||
var rowIndex = e.RowIndex;
|
||||
BeginInvoke(() => BrowseFolder(rowIndex));
|
||||
};
|
||||
|
||||
gridProfiles.CellValueChanged += (_, _) => RefreshProfileDropdowns();
|
||||
AttachContextMenu(gridFolders);
|
||||
tab.Controls.Add(gridFolders);
|
||||
return tab;
|
||||
}
|
||||
|
||||
// ── Tab: Filter ───────────────────────────────────────────────
|
||||
private TabPage BuildFilterTab()
|
||||
{
|
||||
var tab = new TabPage("E-Mail-Filter (Global)");
|
||||
int y = Pad;
|
||||
|
||||
tab.Controls.Add(new Label
|
||||
{
|
||||
Text = "Globale Listen gelten für alle Profile, sofern das Profil keine eigene Liste definiert.\r\n" +
|
||||
"Format: eine E-Mail-Adresse pro Zeile. Leer = kein Filter.",
|
||||
Left = Pad, Top = y, Width = 800, Height = 34, ForeColor = Color.DimGray
|
||||
});
|
||||
y += 40;
|
||||
|
||||
tab.Controls.Add(new Label { Text = "E-Mail-Whitelist (nur diese Absender drucken):", Left = Pad, Top = y, Width = 390, AutoSize = false });
|
||||
tab.Controls.Add(new Label { Text = "E-Mail-Blacklist (diese Absender blockieren):", Left = Pad + 420, Top = y, Width = 390, AutoSize = false });
|
||||
y += 20;
|
||||
|
||||
txtGlobalAllowed = new TextBox
|
||||
{
|
||||
Left = Pad, Top = y, Width = 390, Height = 400,
|
||||
Multiline = true, ScrollBars = ScrollBars.Vertical,
|
||||
Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom
|
||||
};
|
||||
txtGlobalBlocked = new TextBox
|
||||
{
|
||||
Left = Pad + 420, Top = y, Width = 390, Height = 400,
|
||||
Multiline = true, ScrollBars = ScrollBars.Vertical,
|
||||
Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom
|
||||
};
|
||||
|
||||
tab.Controls.Add(txtGlobalAllowed);
|
||||
tab.Controls.Add(txtGlobalBlocked);
|
||||
return tab;
|
||||
}
|
||||
|
||||
// ── Tab: Web API ──────────────────────────────────────────────
|
||||
private TabPage BuildApiTab()
|
||||
{
|
||||
var tab = new TabPage("Web API");
|
||||
int y = Pad;
|
||||
txtApiPort = AddText(tab, "Port", ref y, "5100");
|
||||
chkBindAll = AddCheck(tab, "Alle Interfaces (0.0.0.0)", ref y, false);
|
||||
txtApiKey = AddText(tab, "API-Key", ref y);
|
||||
|
||||
var btnGen = Btn("🔑 Generieren", LabelW + Pad, y, 130);
|
||||
btnGen.Click += (_, _) => txtApiKey.Text = Guid.NewGuid().ToString("N");
|
||||
tab.Controls.Add(btnGen); y += RowH + 4;
|
||||
|
||||
tab.Controls.Add(new Label
|
||||
{
|
||||
Text = "Header: X-Api-Key: <key> | Leer = kein Schutz\r\n\r\n" +
|
||||
"POST /api/print/upload (multipart: file, printer, paperSource, copies)\r\n" +
|
||||
"POST /api/print/url { url, printer, paperSource, copies }\r\n" +
|
||||
"GET /api/print/printers\r\nGET /api/print/health\r\nGET /swagger",
|
||||
Left = Pad, Top = y, Width = 700, Height = 110, ForeColor = Color.DimGray
|
||||
});
|
||||
return tab;
|
||||
}
|
||||
|
||||
// ── Tab: Allgemein ────────────────────────────────────────────
|
||||
private TabPage BuildGeneralTab()
|
||||
{
|
||||
var tab = new TabPage("Allgemein");
|
||||
int y = Pad;
|
||||
txtInterval = AddText(tab, "Intervall (Sek.)", ref y, "60");
|
||||
txtSubjectFilter = AddText(tab, "Betreff-Filter", ref y);
|
||||
chkDelete = AddCheck(tab, "Nach Druck löschen", ref y, true);
|
||||
chkMarkRead = AddCheck(tab, "Als gelesen markieren", ref y, true);
|
||||
txtSumatraPath = AddText(tab, "SumatraPDF Pfad", ref y);
|
||||
|
||||
var btnS = Btn("…", LabelW + Pad + CtrlW + 4, y - RowH - 2, 28);
|
||||
btnS.Click += (_, _) =>
|
||||
{
|
||||
using var d = new OpenFileDialog { Filter = "SumatraPDF.exe|SumatraPDF.exe|Alle|*.*" };
|
||||
if (d.ShowDialog() == DialogResult.OK) txtSumatraPath.Text = d.FileName;
|
||||
};
|
||||
tab.Controls.Add(btnS);
|
||||
txtTempDir = AddText(tab, "Temp-Verzeichnis", ref y, Path.Combine(Path.GetTempPath(), "MailPrint"));
|
||||
return tab;
|
||||
}
|
||||
|
||||
// ── Tab: Über ─────────────────────────────────────────────────
|
||||
private TabPage BuildAboutTab()
|
||||
{
|
||||
var tab = new TabPage("Über");
|
||||
int y = Pad + 10;
|
||||
|
||||
void AddLine(string text, bool link = false, string url = "")
|
||||
{
|
||||
if (link)
|
||||
{
|
||||
var lbl = new LinkLabel
|
||||
{
|
||||
Text = text, Left = Pad, Top = y, AutoSize = true,
|
||||
Font = new Font("Segoe UI", 10f)
|
||||
};
|
||||
lbl.LinkClicked += (_, _) => System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(url) { UseShellExecute = true });
|
||||
tab.Controls.Add(lbl);
|
||||
}
|
||||
else
|
||||
{
|
||||
tab.Controls.Add(new Label
|
||||
{
|
||||
Text = text, Left = Pad, Top = y, AutoSize = true,
|
||||
Font = new Font("Segoe UI", 10f)
|
||||
});
|
||||
}
|
||||
y += 28;
|
||||
}
|
||||
|
||||
var version = System.Reflection.Assembly.GetExecutingAssembly()
|
||||
.GetName().Version?.ToString(3) ?? "?";
|
||||
|
||||
AddLine("MailPrint");
|
||||
y += 4;
|
||||
AddLine($"Version {version}");
|
||||
AddLine("Automatischer PDF-Druck per E-Mail und REST API.");
|
||||
AddLine("Kostenlos und quelloffen (MIT-Lizenz).");
|
||||
y += 16;
|
||||
|
||||
AddLine("Quellcode & Dokumentation:");
|
||||
AddLine("https://www.dimedtec.net/dimedtec/MailPrint", link: true, url: "https://www.dimedtec.net/dimedtec/MailPrint");
|
||||
y += 16;
|
||||
|
||||
AddLine("Verwendet:");
|
||||
AddLine("SumatraPDF – freier PDF-Viewer (GPLv3)");
|
||||
AddLine("https://www.sumatrapdfreader.org", link: true, url: "https://www.sumatrapdfreader.org");
|
||||
y += 8;
|
||||
AddLine("MailKit – IMAP/POP3 Bibliothek (MIT)");
|
||||
AddLine("https://github.com/jstedfast/MailKit", link: true, url: "https://github.com/jstedfast/MailKit");
|
||||
y += 8;
|
||||
AddLine(".NET – Microsoft (MIT)");
|
||||
AddLine("https://dotnet.microsoft.com", link: true, url: "https://dotnet.microsoft.com");
|
||||
|
||||
return tab;
|
||||
}
|
||||
|
||||
// ── Kontextmenü für Grids ──────────────────────────────────────
|
||||
private static void AttachContextMenu(DataGridView grid)
|
||||
{
|
||||
var menu = new ContextMenuStrip();
|
||||
var itemDelete = new ToolStripMenuItem("🗑 Zeile löschen");
|
||||
itemDelete.Click += (_, _) =>
|
||||
{
|
||||
foreach (DataGridViewRow row in grid.SelectedRows)
|
||||
if (!row.IsNewRow) grid.Rows.Remove(row);
|
||||
};
|
||||
menu.Items.Add(itemDelete);
|
||||
|
||||
grid.MouseDown += (_, e) =>
|
||||
{
|
||||
if (e.Button != MouseButtons.Right) return;
|
||||
var hit = grid.HitTest(e.X, e.Y);
|
||||
if (hit.RowIndex >= 0)
|
||||
{
|
||||
grid.ClearSelection();
|
||||
grid.Rows[hit.RowIndex].Selected = true;
|
||||
menu.Show(grid, e.Location);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// Papierfächer + Profil-Dropdown sync
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
private void GridProfiles_CellValueChanged(object? sender, DataGridViewCellEventArgs e)
|
||||
{
|
||||
if (e.RowIndex < 0 || gridProfiles.Columns[e.ColumnIndex].Name != "Printer") return;
|
||||
var printerName = gridProfiles.Rows[e.RowIndex].Cells["Printer"].Value?.ToString() ?? "";
|
||||
var sc = (DataGridViewComboBoxCell)gridProfiles.Rows[e.RowIndex].Cells["Source"];
|
||||
sc.Items.Clear(); sc.Items.Add("");
|
||||
if (!string.IsNullOrEmpty(printerName))
|
||||
{
|
||||
var ps = new PrinterSettings { PrinterName = printerName };
|
||||
foreach (PaperSource src in ps.PaperSources) sc.Items.Add(src.SourceName);
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshProfileDropdowns()
|
||||
{
|
||||
var names = gridProfiles.Rows.Cast<DataGridViewRow>()
|
||||
.Where(r => !r.IsNewRow)
|
||||
.Select(r => r.Cells["PName"].Value?.ToString() ?? "")
|
||||
.Where(n => n.Length > 0).ToList();
|
||||
|
||||
// Postfächer
|
||||
var colA = (DataGridViewComboBoxColumn)gridAccounts.Columns["Profile"];
|
||||
colA.Items.Clear(); colA.Items.Add("");
|
||||
foreach (var n in names) colA.Items.Add(n);
|
||||
foreach (DataGridViewRow row in gridAccounts.Rows)
|
||||
{
|
||||
if (row.IsNewRow) continue;
|
||||
var val = row.Cells["Profile"].Value?.ToString() ?? "";
|
||||
if (!string.IsNullOrEmpty(val) && !colA.Items.Contains(val)) colA.Items.Add(val);
|
||||
}
|
||||
|
||||
// Ordner-Überwachung
|
||||
var colF = (DataGridViewComboBoxColumn)gridFolders.Columns["FProfile"];
|
||||
colF.Items.Clear(); colF.Items.Add("");
|
||||
foreach (var n in names) colF.Items.Add(n);
|
||||
foreach (DataGridViewRow row in gridFolders.Rows)
|
||||
{
|
||||
if (row.IsNewRow) continue;
|
||||
var val = row.Cells["FProfile"].Value?.ToString() ?? "";
|
||||
if (!string.IsNullOrEmpty(val) && !colF.Items.Contains(val)) colF.Items.Add(val);
|
||||
}
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// Config laden / speichern
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
private void AutoDetectConfigPath()
|
||||
{
|
||||
var candidates = new[]
|
||||
{
|
||||
Path.Combine(AppContext.BaseDirectory, "appsettings.json"),
|
||||
Path.Combine(AppContext.BaseDirectory, "..", "publish", "appsettings.json"),
|
||||
@"C:\Services\MailPrint\appsettings.json",
|
||||
};
|
||||
txtConfigPath.Text = candidates.FirstOrDefault(File.Exists)
|
||||
?? Path.Combine(AppContext.BaseDirectory, "appsettings.json");
|
||||
}
|
||||
|
||||
private void BrowseFolder(int rowIndex)
|
||||
{
|
||||
string? result = null;
|
||||
var current = gridFolders.Rows[rowIndex].Cells["FPath"].Value?.ToString() ?? "";
|
||||
|
||||
// Eigener STA-Thread verhindert Blockierung durch COM-Dialog
|
||||
var t = new Thread(() =>
|
||||
{
|
||||
using var d = new FolderBrowserDialog
|
||||
{
|
||||
Description = "Ordner wählen",
|
||||
UseDescriptionForTitle = true,
|
||||
ShowNewFolderButton = true
|
||||
};
|
||||
if (Directory.Exists(current)) d.SelectedPath = current;
|
||||
result = d.ShowDialog() == DialogResult.OK ? d.SelectedPath : null;
|
||||
});
|
||||
t.SetApartmentState(ApartmentState.STA);
|
||||
t.Start();
|
||||
t.Join();
|
||||
|
||||
if (result != null)
|
||||
gridFolders.Rows[rowIndex].Cells["FPath"].Value = result;
|
||||
}
|
||||
|
||||
private void BrowseConfig()
|
||||
{
|
||||
using var d = new OpenFileDialog { Filter = "appsettings.json|appsettings.json|JSON|*.json" };
|
||||
if (d.ShowDialog() == DialogResult.OK) txtConfigPath.Text = d.FileName;
|
||||
}
|
||||
|
||||
private void LoadConfig()
|
||||
{
|
||||
if (!File.Exists(txtConfigPath.Text))
|
||||
{ SetStatus($"Nicht gefunden: {txtConfigPath.Text}", Color.Red); return; }
|
||||
|
||||
try
|
||||
{
|
||||
var mp = (JObject.Parse(File.ReadAllText(txtConfigPath.Text))["MailPrint"] as JObject) ?? new JObject();
|
||||
|
||||
txtInterval.Text = mp["PollIntervalSeconds"]?.ToString() ?? "60";
|
||||
txtSubjectFilter.Text = mp["SubjectFilter"]?.ToString() ?? "";
|
||||
chkDelete.Checked = mp["DeleteAfterPrint"]?.Value<bool>() ?? true;
|
||||
chkMarkRead.Checked = mp["MarkAsRead"]?.Value<bool>() ?? true;
|
||||
txtSumatraPath.Text = mp["SumatraPath"]?.ToString() ?? "";
|
||||
txtTempDir.Text = mp["TempDirectory"]?.ToString() ?? "";
|
||||
|
||||
txtGlobalAllowed.Text = string.Join(Environment.NewLine,
|
||||
(mp["AllowedSenders"] as JArray ?? new JArray()).Select(t => t.ToString()));
|
||||
txtGlobalBlocked.Text = string.Join(Environment.NewLine,
|
||||
(mp["BlockedSenders"] as JArray ?? new JArray()).Select(t => t.ToString()));
|
||||
|
||||
var api = mp["WebApi"] as JObject ?? new JObject();
|
||||
txtApiPort.Text = api["Port"]?.ToString() ?? "5100";
|
||||
chkBindAll.Checked = api["BindAllInterfaces"]?.Value<bool>() ?? false;
|
||||
txtApiKey.Text = api["ApiKey"]?.ToString() ?? "";
|
||||
|
||||
// Drucker-Profile
|
||||
gridProfiles.Rows.Clear();
|
||||
var printerCol = (DataGridViewComboBoxColumn)gridProfiles.Columns["Printer"];
|
||||
foreach (var p in mp["PrinterProfiles"] as JArray ?? new JArray())
|
||||
{
|
||||
var printer = p["PrinterName"]?.ToString() ?? "";
|
||||
var source = p["PaperSource"]?.ToString() ?? "";
|
||||
var allowed = string.Join(", ", (p["AllowedSenders"] as JArray ?? new JArray()).Select(t => t.ToString()));
|
||||
var blocked = string.Join(", ", (p["BlockedSenders"] as JArray ?? new JArray()).Select(t => t.ToString()));
|
||||
|
||||
if (!printerCol.Items.Contains(printer) && !string.IsNullOrEmpty(printer))
|
||||
printerCol.Items.Add(printer);
|
||||
|
||||
int ri = gridProfiles.Rows.Add(
|
||||
p["Name"]?.ToString() ?? "", printer, "", p["Copies"]?.ToString() ?? "1",
|
||||
DuplexToDisplay(p["Duplex"]?.ToString()), allowed, blocked);
|
||||
|
||||
var sc = (DataGridViewComboBoxCell)gridProfiles.Rows[ri].Cells["Source"];
|
||||
sc.Items.Clear(); sc.Items.Add("");
|
||||
if (!string.IsNullOrEmpty(printer))
|
||||
{
|
||||
var ps = new PrinterSettings { PrinterName = printer };
|
||||
foreach (PaperSource s in ps.PaperSources) sc.Items.Add(s.SourceName);
|
||||
}
|
||||
if (!sc.Items.Contains(source) && !string.IsNullOrEmpty(source)) sc.Items.Add(source);
|
||||
sc.Value = source;
|
||||
}
|
||||
|
||||
RefreshProfileDropdowns();
|
||||
|
||||
// Postfächer
|
||||
gridAccounts.Rows.Clear();
|
||||
foreach (var a in mp["Accounts"] as JArray ?? new JArray())
|
||||
{
|
||||
var proto = a["Protocol"]?.ToString() ?? "IMAP";
|
||||
var profName = a["PrinterProfileName"]?.ToString() ?? "";
|
||||
|
||||
var protoCol = (DataGridViewComboBoxColumn)gridAccounts.Columns["Protocol"];
|
||||
if (!protoCol.Items.Contains(proto)) protoCol.Items.Add(proto);
|
||||
|
||||
var profCol = (DataGridViewComboBoxColumn)gridAccounts.Columns["Profile"];
|
||||
if (!string.IsNullOrEmpty(profName) && !profCol.Items.Contains(profName))
|
||||
profCol.Items.Add(profName);
|
||||
|
||||
gridAccounts.Rows.Add(
|
||||
a["Name"]?.ToString() ?? "",
|
||||
proto,
|
||||
a["Host"]?.ToString() ?? "",
|
||||
a["Port"]?.ToString() ?? "993",
|
||||
a["UseSsl"]?.Value<bool>() ?? true,
|
||||
a["Username"]?.ToString() ?? "",
|
||||
a["Password"]?.ToString() ?? "",
|
||||
a["Folder"]?.ToString() ?? "INBOX",
|
||||
profName);
|
||||
}
|
||||
|
||||
// Ordner-Überwachung
|
||||
gridFolders.Rows.Clear();
|
||||
foreach (var f in mp["FolderWatchers"] as JArray ?? new JArray())
|
||||
{
|
||||
var profName = f["PrinterProfileName"]?.ToString() ?? "";
|
||||
var profCol = (DataGridViewComboBoxColumn)gridFolders.Columns["FProfile"];
|
||||
if (!string.IsNullOrEmpty(profName) && !profCol.Items.Contains(profName))
|
||||
profCol.Items.Add(profName);
|
||||
|
||||
gridFolders.Rows.Add(
|
||||
f["Name"]?.ToString() ?? "",
|
||||
f["Path"]?.ToString() ?? "",
|
||||
"…",
|
||||
f["IncludeSubfolders"]?.Value<bool>() ?? false,
|
||||
f["DeleteAfterPrint"]?.Value<bool>() ?? true,
|
||||
profName);
|
||||
}
|
||||
|
||||
SetStatus($"Geladen: {txtConfigPath.Text}", Color.DarkGreen);
|
||||
}
|
||||
catch (Exception ex) { SetStatus($"Fehler: {ex.Message}", Color.Red); }
|
||||
}
|
||||
|
||||
private void SaveConfig()
|
||||
{
|
||||
gridProfiles.EndEdit();
|
||||
gridAccounts.EndEdit();
|
||||
gridFolders.EndEdit();
|
||||
|
||||
var path = txtConfigPath.Text;
|
||||
var root = File.Exists(path) ? JObject.Parse(File.ReadAllText(path)) : new JObject();
|
||||
|
||||
var profiles = new JArray();
|
||||
foreach (DataGridViewRow r in gridProfiles.Rows)
|
||||
{
|
||||
if (r.IsNewRow) continue;
|
||||
profiles.Add(new JObject
|
||||
{
|
||||
["Name"] = r.Cells["PName"].Value?.ToString() ?? "",
|
||||
["PrinterName"] = r.Cells["Printer"].Value?.ToString() ?? "",
|
||||
["PaperSource"] = r.Cells["Source"].Value?.ToString() ?? "",
|
||||
["Copies"] = int.TryParse(r.Cells["Copies"].Value?.ToString(), out int c) ? c : 1,
|
||||
["Duplex"] = DuplexToJson(r.Cells["Duplex"].Value?.ToString()),
|
||||
["AllowedSenders"] = ToJArray(r.Cells["Allowed"].Value?.ToString()),
|
||||
["BlockedSenders"] = ToJArray(r.Cells["Blocked"].Value?.ToString())
|
||||
});
|
||||
}
|
||||
|
||||
var accounts = new JArray();
|
||||
foreach (DataGridViewRow r in gridAccounts.Rows)
|
||||
{
|
||||
if (r.IsNewRow) continue;
|
||||
accounts.Add(new JObject
|
||||
{
|
||||
["Name"] = r.Cells["AName"].Value?.ToString() ?? "",
|
||||
["Protocol"] = r.Cells["Protocol"].Value?.ToString() ?? "IMAP",
|
||||
["Host"] = r.Cells["Host"].Value?.ToString() ?? "",
|
||||
["Port"] = int.TryParse(r.Cells["Port"].Value?.ToString(), out int p) ? p : 993,
|
||||
["UseSsl"] = r.Cells["Ssl"].Value is true,
|
||||
["Username"] = r.Cells["User"].Value?.ToString() ?? "",
|
||||
["Password"] = r.Cells["Pass"].Value?.ToString() ?? "",
|
||||
["Folder"] = r.Cells["Folder"].Value?.ToString() ?? "INBOX",
|
||||
["PrinterProfileName"] = r.Cells["Profile"].Value?.ToString() ?? ""
|
||||
});
|
||||
}
|
||||
|
||||
var folders = new JArray();
|
||||
foreach (DataGridViewRow r in gridFolders.Rows)
|
||||
{
|
||||
if (r.IsNewRow) continue;
|
||||
folders.Add(new JObject
|
||||
{
|
||||
["Name"] = r.Cells["FName"].Value?.ToString() ?? "",
|
||||
["Path"] = r.Cells["FPath"].Value?.ToString() ?? "",
|
||||
["IncludeSubfolders"] = r.Cells["FSubfolders"].Value is true,
|
||||
["DeleteAfterPrint"] = r.Cells["FDelete"].Value is true,
|
||||
["PrinterProfileName"] = r.Cells["FProfile"].Value?.ToString() ?? ""
|
||||
});
|
||||
}
|
||||
|
||||
root["MailPrint"] = new JObject
|
||||
{
|
||||
["PollIntervalSeconds"] = int.TryParse(txtInterval.Text, out int iv) ? iv : 60,
|
||||
["SubjectFilter"] = txtSubjectFilter.Text,
|
||||
["DeleteAfterPrint"] = chkDelete.Checked,
|
||||
["MarkAsRead"] = chkMarkRead.Checked,
|
||||
["SumatraPath"] = txtSumatraPath.Text,
|
||||
["TempDirectory"] = txtTempDir.Text,
|
||||
["AllowedExtensions"] = new JArray(".pdf"),
|
||||
["AllowedSenders"] = ToJArray(txtGlobalAllowed.Text, multiline: true),
|
||||
["BlockedSenders"] = ToJArray(txtGlobalBlocked.Text, multiline: true),
|
||||
["PrinterProfiles"] = profiles,
|
||||
["Accounts"] = accounts,
|
||||
["FolderWatchers"] = folders,
|
||||
["WebApi"] = new JObject
|
||||
{
|
||||
["Port"] = int.TryParse(txtApiPort.Text, out int ap) ? ap : 5100,
|
||||
["BindAllInterfaces"] = chkBindAll.Checked,
|
||||
["ApiKey"] = txtApiKey.Text
|
||||
}
|
||||
};
|
||||
|
||||
if (root["Serilog"] == null)
|
||||
root["Serilog"] = JObject.Parse("{\"MinimumLevel\":{\"Default\":\"Information\",\"Override\":{\"Microsoft\":\"Warning\",\"Microsoft.AspNetCore\":\"Warning\"}}}");
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
|
||||
File.WriteAllText(path, root.ToString(Formatting.Indented));
|
||||
SetStatus($"Gespeichert: {path}", Color.DarkGreen);
|
||||
}
|
||||
catch (Exception ex) { SetStatus($"Fehler: {ex.Message}", Color.Red); }
|
||||
}
|
||||
|
||||
private static string DuplexToDisplay(string? json) => json switch
|
||||
{
|
||||
"long" => "Lange Seite",
|
||||
"short" => "Kurze Seite",
|
||||
_ => "Aus"
|
||||
};
|
||||
|
||||
private static string DuplexToJson(string? display) => display switch
|
||||
{
|
||||
"Lange Seite" => "long",
|
||||
"Kurze Seite" => "short",
|
||||
_ => "none"
|
||||
};
|
||||
|
||||
private static JArray ToJArray(string? input, bool multiline = false)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input)) return new JArray();
|
||||
var sep = multiline ? new[] { "\r\n", "\n" } : new[] { "," };
|
||||
return new JArray(input.Split(sep, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(s => s.Trim()).Where(s => s.Length > 0));
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// EXE starten / stoppen
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
private async Task ToggleExeAsync()
|
||||
{
|
||||
btnStartStop.Enabled = false;
|
||||
try
|
||||
{
|
||||
if (_proc is { HasExited: false })
|
||||
{
|
||||
_proc.Kill(entireProcessTree: true);
|
||||
await _proc.WaitForExitAsync();
|
||||
_proc = null;
|
||||
SetStatus("MailPrint EXE gestoppt.", Color.DarkOrange);
|
||||
}
|
||||
else
|
||||
{
|
||||
var exePath = Path.Combine(
|
||||
Path.GetDirectoryName(txtConfigPath.Text) ?? AppContext.BaseDirectory,
|
||||
"MailPrint.exe");
|
||||
if (!File.Exists(exePath))
|
||||
{ SetStatus($"MailPrint.exe nicht gefunden: {exePath}", Color.Red); return; }
|
||||
|
||||
_proc = new System.Diagnostics.Process
|
||||
{
|
||||
StartInfo = new System.Diagnostics.ProcessStartInfo(exePath)
|
||||
{
|
||||
UseShellExecute = true,
|
||||
WorkingDirectory = Path.GetDirectoryName(exePath)!
|
||||
},
|
||||
EnableRaisingEvents = true
|
||||
};
|
||||
_proc.Exited += (_, _) => BeginInvoke(RefreshStartStop);
|
||||
_proc.Start();
|
||||
SetStatus($"MailPrint EXE gestartet (PID {_proc.Id})", Color.DarkGreen);
|
||||
}
|
||||
}
|
||||
finally { btnStartStop.Enabled = true; RefreshStartStop(); }
|
||||
}
|
||||
|
||||
// ── Dienst-Aktionen ───────────────────────────────────────────
|
||||
private const string ServiceName = "MailPrint";
|
||||
|
||||
private async Task ServiceActionAsync(string action)
|
||||
{
|
||||
var publishDir = Path.GetDirectoryName(txtConfigPath.Text) ?? AppContext.BaseDirectory;
|
||||
var exePath = Path.Combine(publishDir, "MailPrint.exe");
|
||||
var installPs = Path.Combine(publishDir, "..", "install-service.ps1");
|
||||
var uninstallPs= Path.Combine(publishDir, "..", "uninstall-service.ps1");
|
||||
|
||||
// Bestätigung nur bei Install/Deinstall
|
||||
if (action is "install" or "uninstall")
|
||||
{
|
||||
var msg = action == "install"
|
||||
? "Dienst 'MailPrint' jetzt installieren?"
|
||||
: "Dienst 'MailPrint' wirklich deinstallieren?";
|
||||
if (MessageBox.Show(msg, "Bestätigung", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
|
||||
return;
|
||||
}
|
||||
|
||||
SetStatus($"Führe aus: {action}…", Color.DarkBlue);
|
||||
DisableServiceButtons(true);
|
||||
|
||||
try
|
||||
{
|
||||
string cmd = action switch
|
||||
{
|
||||
"install" => $"-ExecutionPolicy Bypass -File \"{Path.GetFullPath(installPs)}\"",
|
||||
"uninstall" => $"-ExecutionPolicy Bypass -File \"{Path.GetFullPath(uninstallPs)}\"",
|
||||
"start" => $"-Command Start-Service -Name '{ServiceName}'",
|
||||
"stop" => $"-Command Stop-Service -Name '{ServiceName}' -Force",
|
||||
_ => throw new ArgumentException(action)
|
||||
};
|
||||
|
||||
var psi = new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = "powershell",
|
||||
Arguments = $"-NoProfile -NonInteractive {cmd}",
|
||||
UseShellExecute = true,
|
||||
Verb = "runas",
|
||||
WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden
|
||||
};
|
||||
|
||||
using var p = System.Diagnostics.Process.Start(psi)!;
|
||||
await p.WaitForExitAsync();
|
||||
|
||||
if (p.ExitCode == 0)
|
||||
{
|
||||
var successMsg = action switch
|
||||
{
|
||||
"install" => "Dienst 'MailPrint' wurde erfolgreich installiert.",
|
||||
"uninstall" => "Dienst 'MailPrint' wurde erfolgreich deinstalliert.",
|
||||
"start" => "Dienst 'MailPrint' wurde gestartet.",
|
||||
"stop" => "Dienst 'MailPrint' wurde beendet.",
|
||||
_ => "Aktion erfolgreich."
|
||||
};
|
||||
SetStatus(successMsg, Color.DarkGreen);
|
||||
MessageBox.Show(successMsg, "Erfolgreich", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
else
|
||||
SetStatus($"'{action}' fehlgeschlagen (Code {p.ExitCode})", Color.Red);
|
||||
}
|
||||
catch (Exception ex) { SetStatus($"Fehler: {ex.Message}", Color.Red); }
|
||||
finally { DisableServiceButtons(false); RefreshStartStop(); }
|
||||
}
|
||||
|
||||
private void DisableServiceButtons(bool disable)
|
||||
{
|
||||
if (InvokeRequired) { BeginInvoke(() => DisableServiceButtons(disable)); return; }
|
||||
btnInstall.Enabled = btnUninstall.Enabled = btnSvcStart.Enabled = btnSvcStop.Enabled = !disable;
|
||||
}
|
||||
|
||||
private void RefreshStartStop()
|
||||
{
|
||||
if (InvokeRequired) { BeginInvoke(RefreshStartStop); return; }
|
||||
bool running = _proc is { HasExited: false };
|
||||
btnStartStop.Text = running ? "⏹ EXE stoppen" : "▶ EXE starten";
|
||||
btnStartStop.BackColor = running ? Color.LightCoral : Color.LightGreen;
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// Hilfsmethoden
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
private void SetStatus(string msg, Color color)
|
||||
{
|
||||
if (InvokeRequired) { BeginInvoke(() => SetStatus(msg, color)); return; }
|
||||
lblStatus.Text = msg; lblStatus.ForeColor = color;
|
||||
}
|
||||
|
||||
private static Button Btn(string text, int x, int y, int w, Color? bg = null)
|
||||
{
|
||||
var b = new Button { Text = text, Left = x, Top = y, Width = w, Height = 26 };
|
||||
if (bg.HasValue) b.BackColor = bg.Value;
|
||||
return b;
|
||||
}
|
||||
|
||||
private TextBox AddText(Control p, string label, ref int y, string def = "")
|
||||
{
|
||||
AddLabel(p, label, y);
|
||||
var tb = new TextBox { Left = LabelW + Pad, Top = y, Width = CtrlW, Text = def };
|
||||
p.Controls.Add(tb); y += RowH; return tb;
|
||||
}
|
||||
|
||||
private CheckBox AddCheck(Control p, string label, ref int y, bool def)
|
||||
{
|
||||
var cb = new CheckBox { Text = label, Left = LabelW + Pad, Top = y, Width = CtrlW + 40, Checked = def };
|
||||
p.Controls.Add(cb); y += RowH; return cb;
|
||||
}
|
||||
|
||||
private static void AddLabel(Control p, string text, int y)
|
||||
=> p.Controls.Add(new Label { Text = text + ":", Left = Pad, Top = y + 4, Width = LabelW, AutoSize = false });
|
||||
|
||||
protected override void OnFormClosing(FormClosingEventArgs e)
|
||||
{
|
||||
_timer.Stop();
|
||||
base.OnFormClosing(e);
|
||||
}
|
||||
}
|
||||
5
MailPrintConfig/Program.cs
Normal file
5
MailPrintConfig/Program.cs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
using MailPrintConfig;
|
||||
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new MainForm());
|
||||
Loading…
Add table
Add a link
Reference in a new issue