diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index f19edc82..69a8d24d 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -52,3 +52,17 @@ jobs: uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} + build-win: + runs-on: windows-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 7 + - name: Build service manager + run: dotnet build .\ServiceManager\ServiceManager.csproj \ No newline at end of file diff --git a/.github/workflows/bump-version.yml b/.github/workflows/bump-version.yml index c0c1e32d..348def2e 100644 --- a/.github/workflows/bump-version.yml +++ b/.github/workflows/bump-version.yml @@ -102,16 +102,37 @@ jobs: with: path-to-binary: 'nuget-packages/' + - name: Build service manager + run: dotnet publish .\ServiceManager\ServiceManager.csproj -c Release + + - name: Sign service manager + env: + CERTIFICATE_HOST: ${{ secrets.CODE_SIGNING_CERT_HOST }} + CERTIFICATE_HOST_API_KEY: ${{ secrets.CODE_SIGNING_CERT_HOST_API_KEY }} + CERTIFICATE_SHA1_HASH: ${{ secrets.CODE_SIGNING_CERT_SHA1_HASH }} + CLIENT_CERTIFICATE: ${{ secrets.CODE_SIGNING_CLIENT_CERT }} + CLIENT_CERTIFICATE_PASSWORD: ${{ secrets.CODE_SIGNING_CLIENT_CERT_PASSWORD }} + uses: cognitedata/code-sign-action@v2 + with: + path-to-binary: .\ServiceManager\bin\Release\net4.8\win-x64\publish\ServiceManager.exe + - name: Push nuget packages run: dotnet nuget push .\nuget-packages\*.nupkg -s https://api.nuget.org/v3/index.json -k ${{ secrets.NUGET_API_KEY }} --skip-duplicate continue-on-error: false - - name: Create Release - uses: actions/create-release@master - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Create tag + uses: rickstaa/action-create-tag@v1 + id: "tag_create" + with: + tag: v${{ needs.build.outputs.version }} + tag_exists_error: true + message: Release v${{ needs.build.outputs.version }} + - name: Upload to github releases + uses: softprops/action-gh-release@v1 with: + files: | + ServiceManager/bin/Release/net4.8/win-x64/publish/ServiceManager.exe tag_name: v${{ needs.build.outputs.version }} - release_name: Release v${{ needs.build.outputs.version }} + name: Release v${{ needs.build.outputs.version }} draft: false prerelease: false diff --git a/ExtractorUtils.Test/unit/ChunkingTest.cs b/ExtractorUtils.Test/unit/ChunkingTest.cs index c6c0ba1e..6939c34e 100644 --- a/ExtractorUtils.Test/unit/ChunkingTest.cs +++ b/ExtractorUtils.Test/unit/ChunkingTest.cs @@ -70,8 +70,6 @@ await Assert.ThrowsAsync( Assert.Contains(1, completed); // may or may not contain 2 Assert.DoesNotContain(3, completed); - // may or may not contain 4 - Assert.DoesNotContain(5, completed); } [Theory] diff --git a/ServiceManager/Elements.cs b/ServiceManager/Elements.cs new file mode 100644 index 00000000..c522ba69 --- /dev/null +++ b/ServiceManager/Elements.cs @@ -0,0 +1,19 @@ +using System.ServiceProcess; + +namespace ServiceManager +{ + internal class ServiceListElement + { + public ServiceController Service { get; } + + public ServiceListElement(ServiceController service) + { + Service = service; + } + + public override string ToString() + { + return Service.DisplayName; + } + } +} diff --git a/ServiceManager/Form1.Designer.cs b/ServiceManager/Form1.Designer.cs new file mode 100644 index 00000000..6f5fb179 --- /dev/null +++ b/ServiceManager/Form1.Designer.cs @@ -0,0 +1,431 @@ +using System.Windows.Forms; + +namespace ServiceManager +{ + partial class Form1 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.panel1 = new System.Windows.Forms.Panel(); + this.serviceList = new System.Windows.Forms.ListBox(); + this.panel5 = new System.Windows.Forms.Panel(); + this.deleteServiceBtn = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.panel2 = new System.Windows.Forms.Panel(); + this.label2 = new System.Windows.Forms.Label(); + this.extractorsDropDown = new System.Windows.Forms.ComboBox(); + this.panel3 = new System.Windows.Forms.Panel(); + this.panel4 = new System.Windows.Forms.Panel(); + this.panel7 = new System.Windows.Forms.Panel(); + this.panel8 = new System.Windows.Forms.Panel(); + this.panel10 = new System.Windows.Forms.Panel(); + this.descriptionBox = new System.Windows.Forms.TextBox(); + this.label5 = new System.Windows.Forms.Label(); + this.panel11 = new System.Windows.Forms.Panel(); + this.nameBox = new System.Windows.Forms.TextBox(); + this.label6 = new System.Windows.Forms.Label(); + this.panel9 = new System.Windows.Forms.Panel(); + this.workingDirBox = new System.Windows.Forms.TextBox(); + this.label7 = new System.Windows.Forms.Label(); + this.panel12 = new System.Windows.Forms.Panel(); + this.selectWorkingDirBtn = new System.Windows.Forms.Button(); + this.label4 = new System.Windows.Forms.Label(); + this.panel13 = new System.Windows.Forms.Panel(); + this.addServiceBtn = new System.Windows.Forms.Button(); + this.panel6 = new System.Windows.Forms.Panel(); + this.serviceStatus = new System.Windows.Forms.TextBox(); + this.folderBrowserDialog1 = new System.Windows.Forms.FolderBrowserDialog(); + this.panel1.SuspendLayout(); + this.panel5.SuspendLayout(); + this.panel2.SuspendLayout(); + this.panel3.SuspendLayout(); + this.panel4.SuspendLayout(); + this.panel7.SuspendLayout(); + this.panel8.SuspendLayout(); + this.panel10.SuspendLayout(); + this.panel11.SuspendLayout(); + this.panel9.SuspendLayout(); + this.panel12.SuspendLayout(); + this.panel13.SuspendLayout(); + this.panel6.SuspendLayout(); + this.SuspendLayout(); + // + // panel1 + // + this.panel1.AutoSize = true; + this.panel1.Controls.Add(this.serviceList); + this.panel1.Controls.Add(this.panel5); + this.panel1.Controls.Add(this.label1); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(15, 102); + this.panel1.Name = "panel1"; + this.panel1.Padding = new System.Windows.Forms.Padding(6); + this.panel1.Size = new System.Drawing.Size(278, 505); + this.panel1.TabIndex = 0; + // + // serviceList + // + this.serviceList.Dock = System.Windows.Forms.DockStyle.Fill; + this.serviceList.FormattingEnabled = true; + this.serviceList.ItemHeight = 25; + this.serviceList.Location = new System.Drawing.Point(6, 47); + this.serviceList.Name = "serviceList"; + this.serviceList.Size = new System.Drawing.Size(266, 397); + this.serviceList.TabIndex = 0; + // + // panel5 + // + this.panel5.Controls.Add(this.deleteServiceBtn); + this.panel5.Dock = System.Windows.Forms.DockStyle.Bottom; + this.panel5.Location = new System.Drawing.Point(6, 444); + this.panel5.Name = "panel5"; + this.panel5.Padding = new System.Windows.Forms.Padding(12); + this.panel5.Size = new System.Drawing.Size(266, 55); + this.panel5.TabIndex = 2; + // + // deleteServiceBtn + // + this.deleteServiceBtn.AutoSize = true; + this.deleteServiceBtn.Location = new System.Drawing.Point(12, 12); + this.deleteServiceBtn.Name = "deleteServiceBtn"; + this.deleteServiceBtn.Size = new System.Drawing.Size(132, 35); + this.deleteServiceBtn.TabIndex = 1; + this.deleteServiceBtn.Text = "Delete Service"; + this.deleteServiceBtn.UseVisualStyleBackColor = true; + this.deleteServiceBtn.Click += new System.EventHandler(this.deleteServiceBtn_Click); + // + // label1 + // + this.label1.Dock = System.Windows.Forms.DockStyle.Top; + this.label1.Location = new System.Drawing.Point(6, 6); + this.label1.Margin = new System.Windows.Forms.Padding(3); + this.label1.Name = "label1"; + this.label1.Padding = new System.Windows.Forms.Padding(0, 0, 0, 3); + this.label1.Size = new System.Drawing.Size(266, 41); + this.label1.TabIndex = 1; + this.label1.Text = "Existing services"; + // + // panel2 + // + this.panel2.Controls.Add(this.label2); + this.panel2.Controls.Add(this.extractorsDropDown); + this.panel2.Dock = System.Windows.Forms.DockStyle.Top; + this.panel2.Location = new System.Drawing.Point(15, 15); + this.panel2.Name = "panel2"; + this.panel2.Padding = new System.Windows.Forms.Padding(6); + this.panel2.Size = new System.Drawing.Size(278, 87); + this.panel2.TabIndex = 2; + // + // label2 + // + this.label2.Dock = System.Windows.Forms.DockStyle.Fill; + this.label2.Location = new System.Drawing.Point(6, 6); + this.label2.Margin = new System.Windows.Forms.Padding(3); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(266, 42); + this.label2.TabIndex = 2; + this.label2.Text = "Extractors"; + // + // extractorsDropDown + // + this.extractorsDropDown.Dock = System.Windows.Forms.DockStyle.Bottom; + this.extractorsDropDown.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.extractorsDropDown.FormattingEnabled = true; + this.extractorsDropDown.Location = new System.Drawing.Point(6, 48); + this.extractorsDropDown.Name = "extractorsDropDown"; + this.extractorsDropDown.Size = new System.Drawing.Size(266, 33); + this.extractorsDropDown.TabIndex = 0; + this.extractorsDropDown.SelectedIndexChanged += new System.EventHandler(this.extractorsDropDown_SelectedIndexChanged); + // + // panel3 + // + this.panel3.Controls.Add(this.panel1); + this.panel3.Controls.Add(this.panel2); + this.panel3.Dock = System.Windows.Forms.DockStyle.Left; + this.panel3.Location = new System.Drawing.Point(0, 0); + this.panel3.Name = "panel3"; + this.panel3.Padding = new System.Windows.Forms.Padding(15, 15, 0, 15); + this.panel3.Size = new System.Drawing.Size(293, 622); + this.panel3.TabIndex = 3; + // + // panel4 + // + this.panel4.Controls.Add(this.panel7); + this.panel4.Controls.Add(this.panel13); + this.panel4.Controls.Add(this.panel6); + this.panel4.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel4.Location = new System.Drawing.Point(293, 0); + this.panel4.Name = "panel4"; + this.panel4.Padding = new System.Windows.Forms.Padding(15); + this.panel4.Size = new System.Drawing.Size(631, 622); + this.panel4.TabIndex = 4; + // + // panel7 + // + this.panel7.Controls.Add(this.panel8); + this.panel7.Controls.Add(this.label4); + this.panel7.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel7.Location = new System.Drawing.Point(15, 102); + this.panel7.Name = "panel7"; + this.panel7.Padding = new System.Windows.Forms.Padding(6); + this.panel7.Size = new System.Drawing.Size(601, 444); + this.panel7.TabIndex = 2; + // + // panel8 + // + this.panel8.Controls.Add(this.panel10); + this.panel8.Controls.Add(this.panel11); + this.panel8.Controls.Add(this.panel9); + this.panel8.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel8.Location = new System.Drawing.Point(6, 47); + this.panel8.Name = "panel8"; + this.panel8.Size = new System.Drawing.Size(589, 391); + this.panel8.TabIndex = 2; + // + // panel10 + // + this.panel10.Controls.Add(this.descriptionBox); + this.panel10.Controls.Add(this.label5); + this.panel10.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel10.Location = new System.Drawing.Point(0, 36); + this.panel10.Name = "panel10"; + this.panel10.Padding = new System.Windows.Forms.Padding(0, 6, 0, 6); + this.panel10.Size = new System.Drawing.Size(589, 312); + this.panel10.TabIndex = 1; + // + // descriptionBox + // + this.descriptionBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.descriptionBox.Location = new System.Drawing.Point(177, 6); + this.descriptionBox.Multiline = true; + this.descriptionBox.Name = "descriptionBox"; + this.descriptionBox.Size = new System.Drawing.Size(412, 300); + this.descriptionBox.TabIndex = 1; + // + // label5 + // + this.label5.Dock = System.Windows.Forms.DockStyle.Left; + this.label5.Location = new System.Drawing.Point(0, 6); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(177, 300); + this.label5.TabIndex = 0; + this.label5.Text = "Description:"; + // + // panel11 + // + this.panel11.Controls.Add(this.nameBox); + this.panel11.Controls.Add(this.label6); + this.panel11.Dock = System.Windows.Forms.DockStyle.Top; + this.panel11.Location = new System.Drawing.Point(0, 0); + this.panel11.Name = "panel11"; + this.panel11.Padding = new System.Windows.Forms.Padding(0, 0, 0, 6); + this.panel11.Size = new System.Drawing.Size(589, 36); + this.panel11.TabIndex = 2; + // + // nameBox + // + this.nameBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.nameBox.Location = new System.Drawing.Point(177, 0); + this.nameBox.Name = "nameBox"; + this.nameBox.Size = new System.Drawing.Size(412, 31); + this.nameBox.TabIndex = 2; + // + // label6 + // + this.label6.Dock = System.Windows.Forms.DockStyle.Left; + this.label6.Location = new System.Drawing.Point(0, 0); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(177, 30); + this.label6.TabIndex = 1; + this.label6.Text = "Name:"; + // + // panel9 + // + this.panel9.Controls.Add(this.workingDirBox); + this.panel9.Controls.Add(this.label7); + this.panel9.Controls.Add(this.panel12); + this.panel9.Dock = System.Windows.Forms.DockStyle.Bottom; + this.panel9.Location = new System.Drawing.Point(0, 348); + this.panel9.Name = "panel9"; + this.panel9.Padding = new System.Windows.Forms.Padding(0, 6, 0, 0); + this.panel9.Size = new System.Drawing.Size(589, 43); + this.panel9.TabIndex = 0; + // + // workingDirBox + // + this.workingDirBox.Dock = System.Windows.Forms.DockStyle.Top; + this.workingDirBox.Location = new System.Drawing.Point(177, 6); + this.workingDirBox.Name = "workingDirBox"; + this.workingDirBox.Size = new System.Drawing.Size(330, 31); + this.workingDirBox.TabIndex = 2; + // + // label7 + // + this.label7.Dock = System.Windows.Forms.DockStyle.Left; + this.label7.Location = new System.Drawing.Point(0, 6); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(177, 37); + this.label7.TabIndex = 1; + this.label7.Text = "Working Dir:"; + // + // panel12 + // + this.panel12.Controls.Add(this.selectWorkingDirBtn); + this.panel12.Dock = System.Windows.Forms.DockStyle.Right; + this.panel12.Location = new System.Drawing.Point(507, 6); + this.panel12.Name = "panel12"; + this.panel12.Size = new System.Drawing.Size(82, 37); + this.panel12.TabIndex = 3; + // + // selectWorkingDirBtn + // + this.selectWorkingDirBtn.AutoSize = true; + this.selectWorkingDirBtn.Location = new System.Drawing.Point(6, -2); + this.selectWorkingDirBtn.Name = "selectWorkingDirBtn"; + this.selectWorkingDirBtn.Size = new System.Drawing.Size(68, 35); + this.selectWorkingDirBtn.TabIndex = 0; + this.selectWorkingDirBtn.Text = "Select"; + this.selectWorkingDirBtn.UseVisualStyleBackColor = true; + this.selectWorkingDirBtn.Click += new System.EventHandler(this.selectWorkingDirBtn_Click); + // + // label4 + // + this.label4.Dock = System.Windows.Forms.DockStyle.Top; + this.label4.Location = new System.Drawing.Point(6, 6); + this.label4.Margin = new System.Windows.Forms.Padding(3); + this.label4.Name = "label4"; + this.label4.Padding = new System.Windows.Forms.Padding(0, 0, 0, 3); + this.label4.Size = new System.Drawing.Size(589, 41); + this.label4.TabIndex = 1; + this.label4.Text = "Create new service:"; + // + // panel13 + // + this.panel13.Controls.Add(this.addServiceBtn); + this.panel13.Dock = System.Windows.Forms.DockStyle.Bottom; + this.panel13.Location = new System.Drawing.Point(15, 546); + this.panel13.Name = "panel13"; + this.panel13.Size = new System.Drawing.Size(601, 61); + this.panel13.TabIndex = 2; + // + // addServiceBtn + // + this.addServiceBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.addServiceBtn.AutoSize = true; + this.addServiceBtn.Location = new System.Drawing.Point(455, 12); + this.addServiceBtn.Name = "addServiceBtn"; + this.addServiceBtn.Size = new System.Drawing.Size(132, 35); + this.addServiceBtn.TabIndex = 2; + this.addServiceBtn.Text = "Create Service"; + this.addServiceBtn.UseVisualStyleBackColor = true; + this.addServiceBtn.Click += new System.EventHandler(this.addServiceBtn_Click); + // + // panel6 + // + this.panel6.Controls.Add(this.serviceStatus); + this.panel6.Dock = System.Windows.Forms.DockStyle.Top; + this.panel6.Location = new System.Drawing.Point(15, 15); + this.panel6.Name = "panel6"; + this.panel6.Padding = new System.Windows.Forms.Padding(6); + this.panel6.Size = new System.Drawing.Size(601, 87); + this.panel6.TabIndex = 1; + // + // serviceStatus + // + this.serviceStatus.Dock = System.Windows.Forms.DockStyle.Fill; + this.serviceStatus.Location = new System.Drawing.Point(6, 6); + this.serviceStatus.Multiline = true; + this.serviceStatus.Name = "serviceStatus"; + this.serviceStatus.ReadOnly = true; + this.serviceStatus.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.serviceStatus.Size = new System.Drawing.Size(589, 75); + this.serviceStatus.TabIndex = 1; + // + // Form1 + // + this.ClientSize = new System.Drawing.Size(924, 622); + this.Controls.Add(this.panel4); + this.Controls.Add(this.panel3); + this.Name = "Form1"; + this.panel1.ResumeLayout(false); + this.panel5.ResumeLayout(false); + this.panel5.PerformLayout(); + this.panel2.ResumeLayout(false); + this.panel3.ResumeLayout(false); + this.panel3.PerformLayout(); + this.panel4.ResumeLayout(false); + this.panel7.ResumeLayout(false); + this.panel8.ResumeLayout(false); + this.panel10.ResumeLayout(false); + this.panel10.PerformLayout(); + this.panel11.ResumeLayout(false); + this.panel11.PerformLayout(); + this.panel9.ResumeLayout(false); + this.panel9.PerformLayout(); + this.panel12.ResumeLayout(false); + this.panel12.PerformLayout(); + this.panel13.ResumeLayout(false); + this.panel13.PerformLayout(); + this.panel6.ResumeLayout(false); + this.panel6.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private Panel panel1; + private ListBox serviceList; + private Label label1; + private Button deleteServiceBtn; + private Panel panel2; + private Label label2; + private Panel panel3; + private Panel panel5; + private Panel panel4; + private Panel panel6; + private ComboBox extractorsDropDown; + private Panel panel7; + private Label label4; + private Panel panel8; + private Panel panel10; + private Label label5; + private Panel panel11; + private Panel panel9; + private TextBox descriptionBox; + private TextBox nameBox; + private Label label6; + private TextBox workingDirBox; + private Label label7; + private Panel panel12; + private Button selectWorkingDirBtn; + private TextBox serviceStatus; + private Panel panel13; + private Button addServiceBtn; + private FolderBrowserDialog folderBrowserDialog1; + } +} \ No newline at end of file diff --git a/ServiceManager/Form1.cs b/ServiceManager/Form1.cs new file mode 100644 index 00000000..6c3b6b91 --- /dev/null +++ b/ServiceManager/Form1.cs @@ -0,0 +1,215 @@ +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.ServiceProcess; +using System.Windows.Forms; + +namespace ServiceManager +{ + public partial class Form1 : Form + { + private List _knownExtractors; + + public Form1() + { + InitializeComponent(); + _knownExtractors = FindWellKnownExtractors().ToList(); + PopulateExtractorsDropdown(); + Icon = Icon.ExtractAssociatedIcon(AppDomain.CurrentDomain.FriendlyName); + } + + private void PopulateExtractorsDropdown() + { + extractorsDropDown.Items.Clear(); + foreach (var ext in _knownExtractors) + { + extractorsDropDown.Items.Add(ext); + } + + if (extractorsDropDown.Items.Count > 0) + { + extractorsDropDown.SelectedIndex = 0; + } + } + + private IEnumerable FindWellKnownExtractors() + { + var configs = KnownExtractorConfigs.KnownExtractors; + foreach (var config in configs) + { + var reg = Registry.LocalMachine.OpenSubKey($"Software\\WOW6432Node\\Cognite\\{config.RegistryName}"); + if (reg == null) + { + continue; + } + var installFolder = reg.GetValue("InstallFolder"); + if (installFolder == null) continue; + var path = Path.Combine(installFolder.ToString(), config.WellKnownExePath); + if (!File.Exists(path)) continue; + + config.FullExePath = path; + + yield return config; + } + } + + private void ReloadServiceList() + { + serviceList.Items.Clear(); + + var selected = extractorsDropDown.SelectedItem as KnownExtractor; + + if (selected == null) + { + serviceList.Items.Clear(); + return; + } + + var services = ServiceController + .GetServices() + .Where(s => s.ServiceName.StartsWith(selected.ServicePrefix)) + .ToList(); + + foreach (var service in services) + { + serviceList.Items.Add(new ServiceListElement(service)); + } + } + + private void extractorsDropDown_SelectedIndexChanged(object sender, EventArgs e) + { + ReloadServiceList(); + } + + private void Error(string message) + { + serviceStatus.ForeColor = Color.Red; + serviceStatus.Text = message; + } + + private void Error(IEnumerable errors) + { + serviceStatus.ForeColor = Color.Red; + serviceStatus.Lines = errors.ToArray(); + } + + private void Error(params string[] errors) + { + Error(errors as IEnumerable); + } + + private void Ok(string message) + { + serviceStatus.ForeColor = Color.Green; + serviceStatus.Text = message; + } + + private void addServiceBtn_Click(object sender, EventArgs e) + { + var name = nameBox.Text; + var description = descriptionBox.Text; + var workingDir = workingDirBox.Text; + + var errors = new List(); + + var selected = extractorsDropDown.SelectedItem as KnownExtractor; + if (selected == null) + { + Error("No extractor selected"); + return; + } + + if (string.IsNullOrEmpty(name)) errors.Add("Missing service name"); + if (string.IsNullOrEmpty(workingDir)) errors.Add("Missing service working directory"); + else if (!Directory.Exists(workingDir)) errors.Add("Working directory does not exist"); + + + if (errors.Any()) + { + Error(errors); + return; + } + + var services = serviceList.Items + .OfType() + .ToList(); + + int index = 0; + foreach (var svc in services) + { + try + { + var idx = svc.Service.ServiceName.Replace(selected.ServicePrefix, ""); + Error(idx); + var cIndex = Convert.ToInt32(idx); + + if (cIndex > index) + { + index = cIndex; + } + } + catch { } + } + + var serviceName = $"{selected.ServicePrefix}{index + 1}"; + + string result = RunCommand.Run( + $"/C sc create {serviceName} binPath=\"\\\"{selected.FullExePath}\\\" {selected.ServiceCommand} {selected.WorkingDirFlag} \\\"{workingDir}\\\"\" DisplayName=\"{name}\""); + + if (result.Contains("SUCCESS")) + { + RunCommand.Run($"/C sc description {serviceName} \"{description}\""); + result = result.Replace("[SC] ", ""); + Ok(result); + } + else + { + Error("Failed to create service:", result); + } + + ReloadServiceList(); + } + + private void selectWorkingDirBtn_Click(object sender, EventArgs e) + { + if (folderBrowserDialog1.ShowDialog() == DialogResult.OK) + { + workingDirBox.Text = folderBrowserDialog1.SelectedPath; + } + } + + private void deleteServiceBtn_Click(object sender, EventArgs e) + { + var selected = serviceList.SelectedItem as ServiceListElement; + + if (selected == null) + { + Error("No service selected"); + return; + } + + DialogResult userCheck = MessageBox.Show("Are you sure you want to delete this service", + "Confirm Delete", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); + + if (userCheck == DialogResult.Yes) + { + string result = RunCommand.Run($"/C sc delete {selected.Service.ServiceName}"); + + var userResult = result.Replace("[SC] ", ""); + if (result.Contains("SUCCESS")) + { + Ok(userResult); + } + else + { + Error("Failed to delete service:", userResult); + } + } + + ReloadServiceList(); + } + } +} \ No newline at end of file diff --git a/ServiceManager/Form1.resx b/ServiceManager/Form1.resx new file mode 100644 index 00000000..73c89a55 --- /dev/null +++ b/ServiceManager/Form1.resx @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/ServiceManager/Icon/black16x16.ico b/ServiceManager/Icon/black16x16.ico new file mode 100644 index 00000000..8686b8a9 Binary files /dev/null and b/ServiceManager/Icon/black16x16.ico differ diff --git a/ServiceManager/KnownExtractorConfigs.cs b/ServiceManager/KnownExtractorConfigs.cs new file mode 100644 index 00000000..92212949 --- /dev/null +++ b/ServiceManager/KnownExtractorConfigs.cs @@ -0,0 +1,62 @@ +namespace ServiceManager +{ + internal abstract class KnownExtractor + { + public abstract string WellKnownExePath { get; } + public abstract string Name { get; } + public abstract string RegistryName { get; } + public abstract string ServicePrefix { get; } + public abstract string ServiceCommand { get; } + public abstract string WorkingDirFlag { get; } + + public string FullExePath { get; set; } + + public override string ToString() + { + return Name; + } + } + + internal class OpcUaExtractor : KnownExtractor + { + public override string WellKnownExePath => "OpcUaExtractor\\bin\\OpcUaExtractor.exe"; + public override string Name => "Cognite OPC UA Extractor"; + public override string RegistryName => "OpcUaExtractor"; + public override string ServicePrefix => "opcuaext"; + public override string ServiceCommand => "-s"; + public override string WorkingDirFlag => "-w"; + } + + internal class OpcClassicExtractor : KnownExtractor + { + public override string WellKnownExePath => "OpcClassicExtractor\\bin\\OpcClassicExtractor.exe"; + public override string Name => "Cognite OPC Classic Extractor"; + public override string RegistryName => "OpcClassicExtractor"; + public override string ServicePrefix => "opcclassicext"; + public override string ServiceCommand => "-s"; + public override string WorkingDirFlag => "-w"; + } + + internal class PiExtractor : KnownExtractor + { + public override string WellKnownExePath => "PiExtractor\\bin\\PiExtractor.exe"; + public override string Name => "Cognite PI Extractor"; + public override string RegistryName => "PiExtractor"; + public override string ServicePrefix => "piextractor"; + public override string ServiceCommand => "-s"; + public override string WorkingDirFlag => "-w"; + } + + + + + internal static class KnownExtractorConfigs + { + public static KnownExtractor[] KnownExtractors { get; } = new[] + { + (KnownExtractor)new PiExtractor(), + new OpcUaExtractor(), + new OpcClassicExtractor(), + }; + } +} diff --git a/ServiceManager/Program.cs b/ServiceManager/Program.cs new file mode 100644 index 00000000..101adf21 --- /dev/null +++ b/ServiceManager/Program.cs @@ -0,0 +1,23 @@ +using System; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace ServiceManager +{ + internal static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + // ApplicationConfiguration.Initialize(); + using (var form = new Form1()) { + Application.Run(form); + } + } + } +} \ No newline at end of file diff --git a/ServiceManager/RunCommand.cs b/ServiceManager/RunCommand.cs new file mode 100644 index 00000000..a02b4161 --- /dev/null +++ b/ServiceManager/RunCommand.cs @@ -0,0 +1,46 @@ +using System; +using System.Diagnostics; +using System.IO; + +namespace ServiceManager +{ + internal class RunCommand + { + /// + /// Runs a command in cmd.exe - timeout will occur after 1 minute + /// + /// + /// stdout of the command + public static string Run(string argument) + { + ProcessStartInfo pinfo = new ProcessStartInfo(); + pinfo.FileName = "cmd.exe"; + pinfo.Arguments = argument; + pinfo.UseShellExecute = false; + pinfo.RedirectStandardOutput = true; + pinfo.CreateNoWindow = true; + string result = string.Empty; + + try + { + using (Process exeProcess = Process.Start(pinfo)) + { + if (exeProcess == null) return $"Failed to run command: {argument}"; + + using (StreamReader reader = exeProcess.StandardOutput) + { + result = reader.ReadToEnd(); + } + // Wait 1 minute for the command to finish + exeProcess.WaitForExit(1000 * 60 * 1); + } + } + catch (Exception ex) + { + return ex.ToString(); + } + return result; + + } + } +} diff --git a/ServiceManager/ServiceManager.csproj b/ServiceManager/ServiceManager.csproj new file mode 100644 index 00000000..ce86b9ad --- /dev/null +++ b/ServiceManager/ServiceManager.csproj @@ -0,0 +1,18 @@ + + + + WinExe + net4.8 + true + app.manifest + win-x64 + + + + + + + + Icon\black16x16.ico + + \ No newline at end of file diff --git a/ServiceManager/ServiceManager.csproj.user b/ServiceManager/ServiceManager.csproj.user new file mode 100644 index 00000000..3a34caac --- /dev/null +++ b/ServiceManager/ServiceManager.csproj.user @@ -0,0 +1,8 @@ + + + + + Form + + + \ No newline at end of file diff --git a/ServiceManager/app.manifest b/ServiceManager/app.manifest new file mode 100644 index 00000000..66bc7752 --- /dev/null +++ b/ServiceManager/app.manifest @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/dotnet-extractor-utils.sln b/dotnet-extractor-utils.sln index 0593ad7b..101e76ad 100644 --- a/dotnet-extractor-utils.sln +++ b/dotnet-extractor-utils.sln @@ -41,6 +41,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "schema", "schema", "{B9F57E schema\retry_config.schema.json = schema\retry_config.schema.json EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceManager", "ServiceManager\ServiceManager.csproj", "{2C841261-9614-473A-9D9A-C323D4282F35}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -179,6 +181,18 @@ Global {96149187-BA12-4F47-A4D2-B3378C026E07}.Release|x64.Build.0 = Release|Any CPU {96149187-BA12-4F47-A4D2-B3378C026E07}.Release|x86.ActiveCfg = Release|Any CPU {96149187-BA12-4F47-A4D2-B3378C026E07}.Release|x86.Build.0 = Release|Any CPU + {2C841261-9614-473A-9D9A-C323D4282F35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2C841261-9614-473A-9D9A-C323D4282F35}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2C841261-9614-473A-9D9A-C323D4282F35}.Debug|x64.ActiveCfg = Debug|Any CPU + {2C841261-9614-473A-9D9A-C323D4282F35}.Debug|x64.Build.0 = Debug|Any CPU + {2C841261-9614-473A-9D9A-C323D4282F35}.Debug|x86.ActiveCfg = Debug|Any CPU + {2C841261-9614-473A-9D9A-C323D4282F35}.Debug|x86.Build.0 = Debug|Any CPU + {2C841261-9614-473A-9D9A-C323D4282F35}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2C841261-9614-473A-9D9A-C323D4282F35}.Release|Any CPU.Build.0 = Release|Any CPU + {2C841261-9614-473A-9D9A-C323D4282F35}.Release|x64.ActiveCfg = Release|Any CPU + {2C841261-9614-473A-9D9A-C323D4282F35}.Release|x64.Build.0 = Release|Any CPU + {2C841261-9614-473A-9D9A-C323D4282F35}.Release|x86.ActiveCfg = Release|Any CPU + {2C841261-9614-473A-9D9A-C323D4282F35}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/version b/version index 3500250a..57807d6d 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.21.0 +1.22.0