Initial commit: PANDA 1.3
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
* text=auto
|
||||||
|
*.cs text eol=crlf
|
||||||
|
*.ps1 text eol=crlf
|
||||||
|
*.md text eol=lf
|
||||||
|
*.csv text eol=crlf
|
||||||
|
*.png binary
|
||||||
|
*.ico binary
|
||||||
|
*.exe binary
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
# Kompilierte Ausgaben
|
||||||
|
PANDA-Portable.exe
|
||||||
|
PANDA-Setup.exe
|
||||||
|
PANDA.Tests.exe
|
||||||
|
PANDA-Uninstall.Payload.exe
|
||||||
|
*.pdb
|
||||||
|
|
||||||
|
# Automatisch erzeugte Oberflächenvorschauen
|
||||||
|
panda-preview.png
|
||||||
|
panda-import-assistent.png
|
||||||
|
panda-setup.png
|
||||||
|
|
||||||
|
# Lokale Exporte und temporäre Tests
|
||||||
|
export.csv
|
||||||
|
*_veraendert.csv
|
||||||
|
portable-test/
|
||||||
|
|
||||||
|
# Editor- und Betriebssystemdateien
|
||||||
|
.vs/
|
||||||
|
*.user
|
||||||
|
*.suo
|
||||||
|
Thumbs.db
|
||||||
|
Desktop.ini
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Kundennummer;Vorname;Nachname;Ort;Interne Notiz
|
||||||
|
1001;Anna;Meyer;Berlin;Priorität A
|
||||||
|
1002;Jonas;Schmidt;Hamburg;Rückruf
|
||||||
|
1003;Zoe;Fischer;München;Neukunde
|
||||||
|
1004;Lena;Wagner;Köln;Bestandskunde
|
||||||
|
+371
@@ -0,0 +1,371 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
|
||||||
|
[assembly: AssemblyTitle("PANDA Setup")]
|
||||||
|
[assembly: AssemblyDescription("Installer für PANDA")]
|
||||||
|
[assembly: AssemblyProduct("PANDA")]
|
||||||
|
[assembly: AssemblyCompany("PANDA")]
|
||||||
|
[assembly: AssemblyVersion("1.3.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.3.0.0")]
|
||||||
|
|
||||||
|
namespace PandaSetup
|
||||||
|
{
|
||||||
|
internal static class Program
|
||||||
|
{
|
||||||
|
[STAThread]
|
||||||
|
private static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Application.EnableVisualStyles();
|
||||||
|
Application.SetCompatibleTextRenderingDefault(false);
|
||||||
|
|
||||||
|
if (args.Length == 1 && string.Equals(args[0], "--verify", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ResourcePayload.Read("PANDA.Application.exe");
|
||||||
|
ResourcePayload.Read("PANDA.Uninstaller.exe");
|
||||||
|
Environment.ExitCode = 0;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Environment.ExitCode = 2;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.Length == 2 && string.Equals(args[0], "--screenshot", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
using (var form = new SetupForm())
|
||||||
|
{
|
||||||
|
form.Show();
|
||||||
|
Application.DoEvents();
|
||||||
|
using (var bitmap = new Bitmap(form.Width, form.Height))
|
||||||
|
{
|
||||||
|
form.DrawToBitmap(bitmap, new Rectangle(Point.Empty, form.Size));
|
||||||
|
bitmap.Save(args[1], System.Drawing.Imaging.ImageFormat.Png);
|
||||||
|
}
|
||||||
|
form.Close();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Application.Run(new SetupForm());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class ResourcePayload
|
||||||
|
{
|
||||||
|
public static byte[] Read(string name)
|
||||||
|
{
|
||||||
|
using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name))
|
||||||
|
{
|
||||||
|
if (stream == null)
|
||||||
|
throw new InvalidDataException("Installationsdatei fehlt: " + name);
|
||||||
|
var bytes = new byte[stream.Length];
|
||||||
|
int offset = 0;
|
||||||
|
while (offset < bytes.Length)
|
||||||
|
{
|
||||||
|
int read = stream.Read(bytes, offset, bytes.Length - offset);
|
||||||
|
if (read == 0) break;
|
||||||
|
offset += read;
|
||||||
|
}
|
||||||
|
if (offset != bytes.Length || bytes.Length < 1024)
|
||||||
|
throw new InvalidDataException("Installationsdatei ist unvollständig: " + name);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class SetupForm : Form
|
||||||
|
{
|
||||||
|
private readonly Color Navy = Color.FromArgb(24, 38, 58);
|
||||||
|
private readonly Color Blue = Color.FromArgb(41, 112, 255);
|
||||||
|
private readonly Color Background = Color.FromArgb(244, 247, 251);
|
||||||
|
private readonly Color Muted = Color.FromArgb(94, 108, 128);
|
||||||
|
private readonly TextBox installPath = new TextBox();
|
||||||
|
private readonly CheckBox desktopShortcut = new CheckBox();
|
||||||
|
private readonly CheckBox startMenuShortcut = new CheckBox();
|
||||||
|
private readonly CheckBox launchAfterInstall = new CheckBox();
|
||||||
|
private readonly Button installButton = new Button();
|
||||||
|
private readonly Label statusLabel = new Label();
|
||||||
|
|
||||||
|
public SetupForm()
|
||||||
|
{
|
||||||
|
Text = "PANDA Setup";
|
||||||
|
StartPosition = FormStartPosition.CenterScreen;
|
||||||
|
FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||||
|
MaximizeBox = false;
|
||||||
|
MinimizeBox = false;
|
||||||
|
ClientSize = new Size(720, 520);
|
||||||
|
BackColor = Background;
|
||||||
|
Font = new Font("Segoe UI", 9F);
|
||||||
|
Icon = System.Drawing.Icon.ExtractAssociatedIcon(Application.ExecutablePath) ?? SystemIcons.Application;
|
||||||
|
BuildLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BuildLayout()
|
||||||
|
{
|
||||||
|
var root = new TableLayoutPanel
|
||||||
|
{
|
||||||
|
Dock = DockStyle.Fill,
|
||||||
|
ColumnCount = 1,
|
||||||
|
RowCount = 4,
|
||||||
|
Padding = new Padding(30, 24, 30, 22),
|
||||||
|
BackColor = Background
|
||||||
|
};
|
||||||
|
root.RowStyles.Add(new RowStyle(SizeType.Absolute, 100));
|
||||||
|
root.RowStyles.Add(new RowStyle(SizeType.Absolute, 120));
|
||||||
|
root.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
|
||||||
|
root.RowStyles.Add(new RowStyle(SizeType.Absolute, 74));
|
||||||
|
Controls.Add(root);
|
||||||
|
|
||||||
|
var heading = new Panel { Dock = DockStyle.Fill };
|
||||||
|
heading.Controls.Add(new Label
|
||||||
|
{
|
||||||
|
Text = "PANDA installieren",
|
||||||
|
Font = new Font("Segoe UI Semibold", 22F),
|
||||||
|
ForeColor = Navy,
|
||||||
|
AutoSize = true,
|
||||||
|
Location = new Point(0, 0)
|
||||||
|
});
|
||||||
|
heading.Controls.Add(new Label
|
||||||
|
{
|
||||||
|
Text = "Pseudonymisierung alphanumerischer Nutzdaten durch Alphabetverschiebung",
|
||||||
|
ForeColor = Muted,
|
||||||
|
AutoSize = true,
|
||||||
|
Location = new Point(2, 48)
|
||||||
|
});
|
||||||
|
heading.Controls.Add(new Label
|
||||||
|
{
|
||||||
|
Text = "Version 1.3.0 • Installation für den aktuellen Windows-Benutzer",
|
||||||
|
ForeColor = Blue,
|
||||||
|
AutoSize = true,
|
||||||
|
Location = new Point(2, 73)
|
||||||
|
});
|
||||||
|
root.Controls.Add(heading, 0, 0);
|
||||||
|
|
||||||
|
var location = new TableLayoutPanel
|
||||||
|
{
|
||||||
|
Dock = DockStyle.Fill,
|
||||||
|
ColumnCount = 2,
|
||||||
|
RowCount = 3,
|
||||||
|
BackColor = Color.White,
|
||||||
|
Padding = new Padding(18, 14, 18, 12),
|
||||||
|
Margin = new Padding(0, 0, 0, 14)
|
||||||
|
};
|
||||||
|
location.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
|
||||||
|
location.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 110));
|
||||||
|
location.RowStyles.Add(new RowStyle(SizeType.Absolute, 26));
|
||||||
|
location.RowStyles.Add(new RowStyle(SizeType.Absolute, 34));
|
||||||
|
location.RowStyles.Add(new RowStyle(SizeType.Absolute, 26));
|
||||||
|
var locationTitle = new Label
|
||||||
|
{
|
||||||
|
Text = "INSTALLATIONSORDNER",
|
||||||
|
Font = new Font("Segoe UI Semibold", 9F),
|
||||||
|
ForeColor = Navy,
|
||||||
|
AutoSize = true,
|
||||||
|
Anchor = AnchorStyles.Left
|
||||||
|
};
|
||||||
|
location.Controls.Add(locationTitle, 0, 0);
|
||||||
|
location.SetColumnSpan(locationTitle, 2);
|
||||||
|
installPath.Text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Programs", "PANDA");
|
||||||
|
installPath.Dock = DockStyle.Fill;
|
||||||
|
installPath.ForeColor = Navy;
|
||||||
|
location.Controls.Add(installPath, 0, 1);
|
||||||
|
var browse = new Button
|
||||||
|
{
|
||||||
|
Text = "Durchsuchen …",
|
||||||
|
Anchor = AnchorStyles.None,
|
||||||
|
Size = new Size(102, 28),
|
||||||
|
FlatStyle = FlatStyle.Flat,
|
||||||
|
BackColor = Color.White,
|
||||||
|
ForeColor = Navy,
|
||||||
|
Margin = new Padding(8, 0, 0, 0)
|
||||||
|
};
|
||||||
|
browse.FlatAppearance.BorderColor = Color.FromArgb(206, 216, 230);
|
||||||
|
browse.Click += delegate { BrowseForFolder(); };
|
||||||
|
location.Controls.Add(browse, 1, 1);
|
||||||
|
var note = new Label
|
||||||
|
{
|
||||||
|
Text = "Keine Administratorrechte erforderlich.",
|
||||||
|
ForeColor = Muted,
|
||||||
|
AutoSize = true,
|
||||||
|
Anchor = AnchorStyles.Left
|
||||||
|
};
|
||||||
|
location.Controls.Add(note, 0, 2);
|
||||||
|
location.SetColumnSpan(note, 2);
|
||||||
|
root.Controls.Add(location, 0, 1);
|
||||||
|
|
||||||
|
var options = new Panel { Dock = DockStyle.Fill, BackColor = Color.White, Padding = new Padding(18, 14, 18, 12) };
|
||||||
|
options.Controls.Add(new Label
|
||||||
|
{
|
||||||
|
Text = "OPTIONEN",
|
||||||
|
Font = new Font("Segoe UI Semibold", 9F),
|
||||||
|
ForeColor = Navy,
|
||||||
|
AutoSize = true,
|
||||||
|
Location = new Point(18, 16)
|
||||||
|
});
|
||||||
|
desktopShortcut.Text = "Desktop-Verknüpfung erstellen";
|
||||||
|
desktopShortcut.Checked = true;
|
||||||
|
desktopShortcut.AutoSize = true;
|
||||||
|
desktopShortcut.ForeColor = Navy;
|
||||||
|
desktopShortcut.Location = new Point(20, 52);
|
||||||
|
startMenuShortcut.Text = "Eintrag im Startmenü erstellen";
|
||||||
|
startMenuShortcut.Checked = true;
|
||||||
|
startMenuShortcut.AutoSize = true;
|
||||||
|
startMenuShortcut.ForeColor = Navy;
|
||||||
|
startMenuShortcut.Location = new Point(20, 82);
|
||||||
|
launchAfterInstall.Text = "PANDA nach der Installation starten";
|
||||||
|
launchAfterInstall.Checked = true;
|
||||||
|
launchAfterInstall.AutoSize = true;
|
||||||
|
launchAfterInstall.ForeColor = Navy;
|
||||||
|
launchAfterInstall.Location = new Point(20, 112);
|
||||||
|
options.Controls.Add(desktopShortcut);
|
||||||
|
options.Controls.Add(startMenuShortcut);
|
||||||
|
options.Controls.Add(launchAfterInstall);
|
||||||
|
root.Controls.Add(options, 0, 2);
|
||||||
|
|
||||||
|
var footer = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 2, RowCount = 1, Padding = new Padding(0, 18, 0, 0) };
|
||||||
|
footer.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
|
||||||
|
footer.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 190));
|
||||||
|
footer.RowStyles.Add(new RowStyle(SizeType.Absolute, 38));
|
||||||
|
statusLabel.Text = "Bereit zur Installation.";
|
||||||
|
statusLabel.ForeColor = Muted;
|
||||||
|
statusLabel.Dock = DockStyle.Fill;
|
||||||
|
statusLabel.TextAlign = ContentAlignment.MiddleLeft;
|
||||||
|
footer.Controls.Add(statusLabel, 0, 0);
|
||||||
|
installButton.Text = "Jetzt installieren";
|
||||||
|
installButton.Dock = DockStyle.Fill;
|
||||||
|
installButton.Margin = new Padding(0);
|
||||||
|
installButton.FlatStyle = FlatStyle.Flat;
|
||||||
|
installButton.FlatAppearance.BorderSize = 0;
|
||||||
|
installButton.BackColor = Blue;
|
||||||
|
installButton.ForeColor = Color.White;
|
||||||
|
installButton.Cursor = Cursors.Hand;
|
||||||
|
installButton.Click += delegate { Install(); };
|
||||||
|
footer.Controls.Add(installButton, 1, 0);
|
||||||
|
root.Controls.Add(footer, 0, 3);
|
||||||
|
AcceptButton = installButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BrowseForFolder()
|
||||||
|
{
|
||||||
|
using (var dialog = new FolderBrowserDialog())
|
||||||
|
{
|
||||||
|
dialog.Description = "Installationsordner für PANDA auswählen";
|
||||||
|
dialog.SelectedPath = installPath.Text;
|
||||||
|
if (dialog.ShowDialog(this) == DialogResult.OK)
|
||||||
|
installPath.Text = Path.Combine(dialog.SelectedPath, "PANDA");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Install()
|
||||||
|
{
|
||||||
|
string target;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
target = Path.GetFullPath(installPath.Text.Trim());
|
||||||
|
if (string.Equals(target, Path.GetPathRoot(target), StringComparison.OrdinalIgnoreCase))
|
||||||
|
throw new InvalidOperationException("Bitte wähle keinen Laufwerks-Stammordner aus.");
|
||||||
|
|
||||||
|
installButton.Enabled = false;
|
||||||
|
statusLabel.Text = "PANDA wird installiert …";
|
||||||
|
Cursor = Cursors.WaitCursor;
|
||||||
|
Application.DoEvents();
|
||||||
|
|
||||||
|
Directory.CreateDirectory(target);
|
||||||
|
string applicationPath = Path.Combine(target, "PANDA.exe");
|
||||||
|
string uninstallerPath = Path.Combine(target, "PANDA-Uninstall.exe");
|
||||||
|
File.WriteAllBytes(applicationPath, ResourcePayload.Read("PANDA.Application.exe"));
|
||||||
|
File.WriteAllBytes(uninstallerPath, ResourcePayload.Read("PANDA.Uninstaller.exe"));
|
||||||
|
|
||||||
|
string desktopLink = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "PANDA.lnk");
|
||||||
|
if (desktopShortcut.Checked)
|
||||||
|
Shortcut.Create(desktopLink, applicationPath, target, "PANDA starten");
|
||||||
|
else if (File.Exists(desktopLink))
|
||||||
|
File.Delete(desktopLink);
|
||||||
|
|
||||||
|
string startMenuFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Programs), "PANDA");
|
||||||
|
if (startMenuShortcut.Checked)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(startMenuFolder);
|
||||||
|
Shortcut.Create(Path.Combine(startMenuFolder, "PANDA.lnk"), applicationPath, target, "PANDA starten");
|
||||||
|
Shortcut.Create(Path.Combine(startMenuFolder, "PANDA deinstallieren.lnk"), uninstallerPath, target, "PANDA deinstallieren");
|
||||||
|
}
|
||||||
|
else if (Directory.Exists(startMenuFolder))
|
||||||
|
{
|
||||||
|
string oldApplicationLink = Path.Combine(startMenuFolder, "PANDA.lnk");
|
||||||
|
string oldUninstallLink = Path.Combine(startMenuFolder, "PANDA deinstallieren.lnk");
|
||||||
|
if (File.Exists(oldApplicationLink)) File.Delete(oldApplicationLink);
|
||||||
|
if (File.Exists(oldUninstallLink)) File.Delete(oldUninstallLink);
|
||||||
|
if (Directory.GetFileSystemEntries(startMenuFolder).Length == 0) Directory.Delete(startMenuFolder, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterUninstaller(target, applicationPath, uninstallerPath);
|
||||||
|
statusLabel.Text = "Installation erfolgreich abgeschlossen.";
|
||||||
|
Cursor = Cursors.Default;
|
||||||
|
MessageBox.Show(this, "PANDA wurde erfolgreich installiert.\r\n\r\nDie Deinstallation ist über Windows › Installierte Apps ‹ oder das Startmenü möglich.", "PANDA installiert", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
if (launchAfterInstall.Checked)
|
||||||
|
Process.Start(applicationPath);
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Cursor = Cursors.Default;
|
||||||
|
installButton.Enabled = true;
|
||||||
|
statusLabel.Text = "Installation fehlgeschlagen.";
|
||||||
|
MessageBox.Show(this, "PANDA konnte nicht installiert werden.\r\n\r\n" + exception.Message + "\r\n\r\nFalls PANDA bereits läuft, schließe das Programm und versuche es erneut.", "Installationsfehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RegisterUninstaller(string target, string applicationPath, string uninstallerPath)
|
||||||
|
{
|
||||||
|
using (RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\PANDA"))
|
||||||
|
{
|
||||||
|
key.SetValue("DisplayName", "PANDA");
|
||||||
|
key.SetValue("DisplayVersion", "1.3.0");
|
||||||
|
key.SetValue("DisplayIcon", applicationPath);
|
||||||
|
key.SetValue("Publisher", "PANDA");
|
||||||
|
key.SetValue("InstallLocation", target);
|
||||||
|
key.SetValue("UninstallString", "\"" + uninstallerPath + "\"");
|
||||||
|
key.SetValue("QuietUninstallString", "\"" + uninstallerPath + "\" --silent");
|
||||||
|
key.SetValue("NoModify", 1, RegistryValueKind.DWord);
|
||||||
|
key.SetValue("NoRepair", 1, RegistryValueKind.DWord);
|
||||||
|
key.SetValue("EstimatedSize", 100, RegistryValueKind.DWord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class Shortcut
|
||||||
|
{
|
||||||
|
public static void Create(string shortcutPath, string targetPath, string workingDirectory, string description)
|
||||||
|
{
|
||||||
|
Type shellType = Type.GetTypeFromProgID("WScript.Shell");
|
||||||
|
if (shellType == null)
|
||||||
|
throw new InvalidOperationException("Windows-Verknüpfungen werden auf diesem System nicht unterstützt.");
|
||||||
|
object shell = Activator.CreateInstance(shellType);
|
||||||
|
object shortcut = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
shortcut = shellType.InvokeMember("CreateShortcut", BindingFlags.InvokeMethod, null, shell, new object[] { shortcutPath });
|
||||||
|
Type shortcutType = shortcut.GetType();
|
||||||
|
shortcutType.InvokeMember("TargetPath", BindingFlags.SetProperty, null, shortcut, new object[] { targetPath });
|
||||||
|
shortcutType.InvokeMember("WorkingDirectory", BindingFlags.SetProperty, null, shortcut, new object[] { workingDirectory });
|
||||||
|
shortcutType.InvokeMember("Description", BindingFlags.SetProperty, null, shortcut, new object[] { description });
|
||||||
|
shortcutType.InvokeMember("IconLocation", BindingFlags.SetProperty, null, shortcut, new object[] { targetPath + ",0" });
|
||||||
|
shortcutType.InvokeMember("Save", BindingFlags.InvokeMethod, null, shortcut, null);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (shortcut != null && Marshal.IsComObject(shortcut)) Marshal.FinalReleaseComObject(shortcut);
|
||||||
|
if (shell != null && Marshal.IsComObject(shell)) Marshal.FinalReleaseComObject(shell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 660 KiB |
+1250
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,44 @@
|
|||||||
|
# PANDA
|
||||||
|
|
||||||
|
**P**seudonymisierung **a**lphanumerischer **N**utzdaten **d**urch **A**lphabetverschiebung
|
||||||
|
|
||||||
|
PANDA importiert CSV-Dateien, pseudonymisiert ausgewählte Werte durch eine umkehrbare Alphabetverschiebung und exportiert das Ergebnis wieder als CSV.
|
||||||
|
|
||||||
|
## Fest installieren
|
||||||
|
|
||||||
|
`PANDA-Setup.exe` doppelt anklicken. Der Installer:
|
||||||
|
|
||||||
|
- installiert PANDA für das aktuelle Windows-Benutzerkonto,
|
||||||
|
- kann Desktop- und Startmenü-Verknüpfungen anlegen,
|
||||||
|
- trägt PANDA unter **Windows → Installierte Apps** ein,
|
||||||
|
- installiert einen vollständigen Uninstaller.
|
||||||
|
|
||||||
|
Administratorrechte sind bei der Standardinstallation nicht erforderlich.
|
||||||
|
|
||||||
|
## Ohne Installation starten
|
||||||
|
|
||||||
|
Alternativ `PANDA-Portable.exe` doppelt anklicken. Diese Einzeldatei benötigt keine zusätzlichen Programmdateien.
|
||||||
|
|
||||||
|
## Bedienung
|
||||||
|
|
||||||
|
1. **Importieren** anklicken und eine CSV-Datei auswählen.
|
||||||
|
2. Festlegen, ob die erste Zeile Überschriften enthält.
|
||||||
|
3. Spalten an- oder abwählen. Die Dateivorschau aktualisiert sich sofort.
|
||||||
|
4. Im Importfenster **Importieren** anklicken.
|
||||||
|
5. Links einzelne oder mehrere Zellen markieren. Über die Checkbox neben einer Zeilennummer lässt sich die komplette Zeile auswählen. Alternativ **Alle Einträge** wählen.
|
||||||
|
6. Schrittweite festlegen und **Hochzählen (+)** oder **Runterzählen (-)** verwenden.
|
||||||
|
7. Das Ergebnis rechts kontrollieren und mit **CSV exportieren** speichern.
|
||||||
|
|
||||||
|
Auswahlmarkierungen werden ausschließlich in der linken Originaltabelle dargestellt.
|
||||||
|
|
||||||
|
Die Buchstaben `A-Z` und `a-z` werden zyklisch verschoben. Beispiel: `Z + 1 = A` und `a - 1 = z`. Zahlen, Leerzeichen, Satzzeichen und Umlaute bleiben unverändert.
|
||||||
|
|
||||||
|
## Dateien
|
||||||
|
|
||||||
|
- `PANDA-Setup.exe` – Installer inklusive Uninstaller
|
||||||
|
- `PANDA-Portable.exe` – portable Einzeldatei
|
||||||
|
- `PANDA.ico` / `PANDA-icon-final.png` – Programmsymbol
|
||||||
|
- `Beispiel.csv` – Beispieldaten
|
||||||
|
- `Program.cs` – Programmquellcode
|
||||||
|
- `Installer.cs` / `Uninstaller.cs` – Setupquellcode
|
||||||
|
- `build.ps1` / `Tests.cs` – Build und automatische Tests
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Panda
|
||||||
|
{
|
||||||
|
internal static class Tests
|
||||||
|
{
|
||||||
|
private static int failures;
|
||||||
|
|
||||||
|
private static void Main()
|
||||||
|
{
|
||||||
|
AssertEqual("BCD YZA", LetterShifter.Shift("ABC XYZ", 1), "shift up and wrap");
|
||||||
|
AssertEqual("Zab", LetterShifter.Shift("Abc", -1), "shift down and preserve case");
|
||||||
|
AssertEqual("Öl 123!", LetterShifter.Shift("Öl 123!", 0), "zero shift");
|
||||||
|
AssertEqual("Öm 123!", LetterShifter.Shift("Öl 123!", 1), "non A-Z characters unchanged");
|
||||||
|
|
||||||
|
string sample = "Name;Notiz\r\n\"Meyer, Anna\";\"Hallo; Welt\"\r\nBob;\"Zeile 1\r\nZeile 2\"\r\n";
|
||||||
|
AssertEqual(';', CsvCodec.DetectDelimiter(sample), "delimiter detection");
|
||||||
|
List<List<string>> parsed = CsvCodec.Parse(sample, ';');
|
||||||
|
AssertEqual(3, parsed.Count, "record count");
|
||||||
|
AssertEqual("Meyer, Anna", parsed[1][0], "quoted comma");
|
||||||
|
AssertEqual("Hallo; Welt", parsed[1][1], "quoted delimiter");
|
||||||
|
AssertEqual("Zeile 1\r\nZeile 2", parsed[2][1], "quoted newline");
|
||||||
|
|
||||||
|
var source = new CsvDocument { Delimiter = ';', FirstRowIsHeader = true };
|
||||||
|
source.Headers.AddRange(new[] { "A", "B", "C" });
|
||||||
|
source.Rows.Add(new List<string> { "a1", "b1", "c1" });
|
||||||
|
source.Rows.Add(new List<string> { "a2", "b2", "c2" });
|
||||||
|
var filtered = CsvCodec.SelectColumns(source, new List<int> { 2, 0 });
|
||||||
|
AssertEqual(2, filtered.Headers.Count, "selected column count");
|
||||||
|
AssertEqual("C", filtered.Headers[0], "selected column order");
|
||||||
|
AssertEqual("a2", filtered.Rows[1][1], "selected column values");
|
||||||
|
|
||||||
|
string tempPath = Path.Combine(Path.GetTempPath(), "csv-buchstaben-test-" + Guid.NewGuid().ToString("N") + ".csv");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var doc = new CsvDocument { Delimiter = ';', FirstRowIsHeader = true };
|
||||||
|
doc.Headers.Add("Name");
|
||||||
|
doc.Headers.Add("Notiz");
|
||||||
|
var rows = new List<IList<string>>
|
||||||
|
{
|
||||||
|
new List<string> { "Anna", "Hallo; \"Welt\"" },
|
||||||
|
new List<string> { "Bob", "Mehr\r\nZeilig" }
|
||||||
|
};
|
||||||
|
CsvCodec.Save(tempPath, doc, rows);
|
||||||
|
var loaded = CsvCodec.Load(tempPath, true);
|
||||||
|
AssertEqual("Hallo; \"Welt\"", loaded.Rows[0][1], "save/load quotes");
|
||||||
|
AssertEqual("Mehr\r\nZeilig", loaded.Rows[1][1], "save/load newline");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (File.Exists(tempPath)) File.Delete(tempPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failures > 0)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine(failures + " test(s) failed.");
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
Console.WriteLine("All tests passed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AssertEqual<T>(T expected, T actual, string name)
|
||||||
|
{
|
||||||
|
if (!EqualityComparer<T>.Default.Equals(expected, actual))
|
||||||
|
{
|
||||||
|
failures++;
|
||||||
|
Console.Error.WriteLine("FAIL " + name + ": expected [" + expected + "] actual [" + actual + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
|
||||||
|
[assembly: AssemblyTitle("PANDA Uninstaller")]
|
||||||
|
[assembly: AssemblyDescription("Deinstallationsprogramm für PANDA")]
|
||||||
|
[assembly: AssemblyProduct("PANDA")]
|
||||||
|
[assembly: AssemblyCompany("PANDA")]
|
||||||
|
[assembly: AssemblyVersion("1.3.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.3.0.0")]
|
||||||
|
|
||||||
|
namespace PandaUninstall
|
||||||
|
{
|
||||||
|
internal static class Program
|
||||||
|
{
|
||||||
|
[STAThread]
|
||||||
|
private static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Application.EnableVisualStyles();
|
||||||
|
bool silent = args.Length > 0 && string.Equals(args[0], "--silent", StringComparison.OrdinalIgnoreCase);
|
||||||
|
string installDirectory = Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory);
|
||||||
|
string ownPath = Assembly.GetExecutingAssembly().Location;
|
||||||
|
|
||||||
|
if (!silent)
|
||||||
|
{
|
||||||
|
DialogResult answer = MessageBox.Show("Soll PANDA vollständig von diesem Benutzerkonto entfernt werden?", "PANDA deinstallieren", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2);
|
||||||
|
if (answer != DialogResult.Yes)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DeleteIfExists(Path.Combine(installDirectory, "PANDA.exe"));
|
||||||
|
DeleteIfExists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "PANDA.lnk"));
|
||||||
|
|
||||||
|
string startMenuFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Programs), "PANDA");
|
||||||
|
DeleteIfExists(Path.Combine(startMenuFolder, "PANDA.lnk"));
|
||||||
|
DeleteIfExists(Path.Combine(startMenuFolder, "PANDA deinstallieren.lnk"));
|
||||||
|
if (Directory.Exists(startMenuFolder) && Directory.GetFileSystemEntries(startMenuFolder).Length == 0)
|
||||||
|
Directory.Delete(startMenuFolder, false);
|
||||||
|
|
||||||
|
Registry.CurrentUser.DeleteSubKeyTree(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\PANDA", false);
|
||||||
|
|
||||||
|
if (!silent)
|
||||||
|
MessageBox.Show("PANDA wurde erfolgreich deinstalliert.", "Deinstallation abgeschlossen", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||||
|
|
||||||
|
ScheduleSelfRemoval(ownPath, installDirectory);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
if (!silent)
|
||||||
|
MessageBox.Show("PANDA konnte nicht vollständig entfernt werden.\r\n\r\n" + exception.Message + "\r\n\r\nSchließe PANDA und versuche es erneut.", "Deinstallationsfehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
Environment.ExitCode = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DeleteIfExists(string path)
|
||||||
|
{
|
||||||
|
if (File.Exists(path))
|
||||||
|
File.Delete(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ScheduleSelfRemoval(string ownPath, string installDirectory)
|
||||||
|
{
|
||||||
|
string scriptPath = Path.Combine(Path.GetTempPath(), "panda-uninstall-" + Guid.NewGuid().ToString("N") + ".cmd");
|
||||||
|
var script = new StringBuilder();
|
||||||
|
script.AppendLine("@echo off");
|
||||||
|
script.AppendLine("ping 127.0.0.1 -n 2 > nul");
|
||||||
|
script.AppendLine("del /f /q \"" + ownPath + "\" > nul 2>&1");
|
||||||
|
script.AppendLine("rmdir \"" + installDirectory.TrimEnd(Path.DirectorySeparatorChar) + "\" > nul 2>&1");
|
||||||
|
script.AppendLine("del /f /q \"%~f0\" > nul 2>&1");
|
||||||
|
File.WriteAllText(scriptPath, script.ToString(), Encoding.ASCII);
|
||||||
|
Process.Start(new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "cmd.exe",
|
||||||
|
Arguments = "/c call \"" + scriptPath + "\"",
|
||||||
|
CreateNoWindow = true,
|
||||||
|
UseShellExecute = false,
|
||||||
|
WindowStyle = ProcessWindowStyle.Hidden
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$compiler = 'C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe'
|
||||||
|
$project = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
$program = Join-Path $project 'Program.cs'
|
||||||
|
$tests = Join-Path $project 'Tests.cs'
|
||||||
|
$installerSource = Join-Path $project 'Installer.cs'
|
||||||
|
$uninstallerSource = Join-Path $project 'Uninstaller.cs'
|
||||||
|
$icon = Join-Path $project 'PANDA.ico'
|
||||||
|
$testExe = Join-Path $project 'PANDA.Tests.exe'
|
||||||
|
$appExe = Join-Path $project 'PANDA-Portable.exe'
|
||||||
|
$uninstallerExe = Join-Path $project 'PANDA-Uninstall.Payload.exe'
|
||||||
|
$setupExe = Join-Path $project 'PANDA-Setup.exe'
|
||||||
|
$iconArgument = "/win32icon:$icon"
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $icon)) { throw 'PANDA.ico fehlt.' }
|
||||||
|
|
||||||
|
& $compiler /nologo /target:exe /main:Panda.Tests /out:$testExe /reference:System.dll /reference:System.Core.dll /reference:System.Drawing.dll /reference:System.Windows.Forms.dll $program $tests
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw 'Test-Build fehlgeschlagen.' }
|
||||||
|
|
||||||
|
& $testExe
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw 'Tests fehlgeschlagen.' }
|
||||||
|
|
||||||
|
& $compiler /nologo /target:winexe /main:Panda.Program /optimize+ /platform:anycpu /out:$appExe $iconArgument /reference:System.dll /reference:System.Core.dll /reference:System.Drawing.dll /reference:System.Windows.Forms.dll $program
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw 'Programm-Build fehlgeschlagen.' }
|
||||||
|
|
||||||
|
& $compiler /nologo /target:winexe /main:PandaUninstall.Program /optimize+ /platform:anycpu /out:$uninstallerExe $iconArgument /reference:System.dll /reference:System.Core.dll /reference:System.Windows.Forms.dll $uninstallerSource
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw 'Uninstaller-Build fehlgeschlagen.' }
|
||||||
|
|
||||||
|
$appResource = "/resource:$appExe,PANDA.Application.exe"
|
||||||
|
$uninstallerResource = "/resource:$uninstallerExe,PANDA.Uninstaller.exe"
|
||||||
|
& $compiler /nologo /target:winexe /main:PandaSetup.Program /optimize+ /platform:anycpu /out:$setupExe $iconArgument /reference:System.dll /reference:System.Core.dll /reference:System.Drawing.dll /reference:System.Windows.Forms.dll $appResource $uninstallerResource $installerSource
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw 'Installer-Build fehlgeschlagen.' }
|
||||||
|
|
||||||
|
$verifyProcess = Start-Process -FilePath $setupExe -ArgumentList '--verify' -WindowStyle Hidden -Wait -PassThru
|
||||||
|
if ($verifyProcess.ExitCode -ne 0) { throw 'Installer-Payload-Prüfung fehlgeschlagen.' }
|
||||||
|
|
||||||
|
Remove-Item -LiteralPath $testExe -Force
|
||||||
|
Remove-Item -LiteralPath $uninstallerExe -Force
|
||||||
|
Write-Host "Portable Einzeldatei erstellt: $appExe"
|
||||||
|
Write-Host "Installer mit Uninstaller erstellt: $setupExe"
|
||||||
Reference in New Issue
Block a user