-> PortScanner added

This commit is contained in:
Kevin Krüger
2022-01-18 09:14:22 +01:00
parent f7b63cb8e1
commit a07655faed
18 changed files with 1301 additions and 9 deletions

View File

@@ -7,6 +7,8 @@
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" Background="White">
<Grid>
</Grid>
<WindowsFormsHost>
<local:WF_PortScanner/>
</WindowsFormsHost>
</Grid>
</UserControl>

View File

@@ -17,7 +17,7 @@ using System.Windows.Shapes;
namespace Networking.Pages
{
/// <summary>
/// Interaktionslogik für Networking.xaml
/// Interaktionslogik für PortScanner.xaml
/// </summary>
public partial class PortScanner : UserControl, IPage
{
@@ -30,6 +30,6 @@ namespace Networking.Pages
public UserControl GetPage() => new PortScanner();
public string GetUnderCategory() => "PortScan";
public string GetUnderCategory() => "PortScanner";
}
}

View File

@@ -0,0 +1,281 @@
namespace Networking.Pages
{
partial class WF_PortScanner
{
/// <summary>
/// Erforderliche Designervariable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Verwendete Ressourcen bereinigen.
/// </summary>
/// <param name="disposing">True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Vom Komponenten-Designer generierter Code
/// <summary>
/// Erforderliche Methode für die Designerunterstützung.
/// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
/// </summary>
private void InitializeComponent()
{
this.hostnameTextBox = new System.Windows.Forms.TextBox();
this.label3 = new System.Windows.Forms.Label();
this.modeGroupBox = new System.Windows.Forms.GroupBox();
this.udpModeRadioButton = new System.Windows.Forms.RadioButton();
this.tcpModeRadioButton = new System.Windows.Forms.RadioButton();
this.portRangeCheckBox = new System.Windows.Forms.CheckBox();
this.portTextBoxMax = new System.Windows.Forms.TextBox();
this.dashLabel = new System.Windows.Forms.Label();
this.portTextBoxMin = new System.Windows.Forms.TextBox();
this.portLabel = new System.Windows.Forms.Label();
this.timeoutGroupBox = new System.Windows.Forms.GroupBox();
this.timeoutComboBox = new System.Windows.Forms.ComboBox();
this.checkPortButton = new System.Windows.Forms.Button();
this.listview1 = new System.Windows.Forms.ListView();
this.chPort = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.chStatus = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.progressBar1 = new System.Windows.Forms.ProgressBar();
this.cancelButton = new System.Windows.Forms.Button();
this.modeGroupBox.SuspendLayout();
this.timeoutGroupBox.SuspendLayout();
this.SuspendLayout();
//
// hostnameTextBox
//
this.hostnameTextBox.Location = new System.Drawing.Point(38, 24);
this.hostnameTextBox.Name = "hostnameTextBox";
this.hostnameTextBox.Size = new System.Drawing.Size(251, 20);
this.hostnameTextBox.TabIndex = 1;
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(5, 27);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(29, 13);
this.label3.TabIndex = 5;
this.label3.Text = "Host";
//
// modeGroupBox
//
this.modeGroupBox.Controls.Add(this.udpModeRadioButton);
this.modeGroupBox.Controls.Add(this.tcpModeRadioButton);
this.modeGroupBox.Location = new System.Drawing.Point(296, 19);
this.modeGroupBox.Name = "modeGroupBox";
this.modeGroupBox.Size = new System.Drawing.Size(111, 57);
this.modeGroupBox.TabIndex = 13;
this.modeGroupBox.TabStop = false;
this.modeGroupBox.Text = "Mode";
//
// udpModeRadioButton
//
this.udpModeRadioButton.AutoSize = true;
this.udpModeRadioButton.Location = new System.Drawing.Point(13, 35);
this.udpModeRadioButton.Name = "udpModeRadioButton";
this.udpModeRadioButton.Size = new System.Drawing.Size(48, 17);
this.udpModeRadioButton.TabIndex = 11;
this.udpModeRadioButton.Text = "UDP";
this.udpModeRadioButton.UseVisualStyleBackColor = true;
//
// tcpModeRadioButton
//
this.tcpModeRadioButton.AutoSize = true;
this.tcpModeRadioButton.Checked = true;
this.tcpModeRadioButton.Location = new System.Drawing.Point(13, 16);
this.tcpModeRadioButton.Name = "tcpModeRadioButton";
this.tcpModeRadioButton.Size = new System.Drawing.Size(46, 17);
this.tcpModeRadioButton.TabIndex = 10;
this.tcpModeRadioButton.TabStop = true;
this.tcpModeRadioButton.Text = "TCP";
this.tcpModeRadioButton.UseVisualStyleBackColor = true;
//
// portRangeCheckBox
//
this.portRangeCheckBox.AutoSize = true;
this.portRangeCheckBox.Location = new System.Drawing.Point(156, 57);
this.portRangeCheckBox.Name = "portRangeCheckBox";
this.portRangeCheckBox.Size = new System.Drawing.Size(80, 17);
this.portRangeCheckBox.TabIndex = 19;
this.portRangeCheckBox.Text = "Port Range";
this.portRangeCheckBox.UseVisualStyleBackColor = true;
this.portRangeCheckBox.CheckedChanged += new System.EventHandler(this.portRangeCheckBox_CheckedChanged);
//
// portTextBoxMax
//
this.portTextBoxMax.Enabled = false;
this.portTextBoxMax.Location = new System.Drawing.Point(98, 55);
this.portTextBoxMax.MaxLength = 5;
this.portTextBoxMax.Name = "portTextBoxMax";
this.portTextBoxMax.Size = new System.Drawing.Size(38, 20);
this.portTextBoxMax.TabIndex = 18;
//
// dashLabel
//
this.dashLabel.AutoSize = true;
this.dashLabel.Location = new System.Drawing.Point(82, 58);
this.dashLabel.Name = "dashLabel";
this.dashLabel.Size = new System.Drawing.Size(10, 13);
this.dashLabel.TabIndex = 17;
this.dashLabel.Text = "-";
//
// portTextBoxMin
//
this.portTextBoxMin.Location = new System.Drawing.Point(38, 55);
this.portTextBoxMin.MaxLength = 5;
this.portTextBoxMin.Name = "portTextBoxMin";
this.portTextBoxMin.Size = new System.Drawing.Size(38, 20);
this.portTextBoxMin.TabIndex = 16;
//
// portLabel
//
this.portLabel.AutoSize = true;
this.portLabel.Location = new System.Drawing.Point(3, 58);
this.portLabel.Name = "portLabel";
this.portLabel.Size = new System.Drawing.Size(31, 13);
this.portLabel.TabIndex = 15;
this.portLabel.Text = "Ports";
//
// timeoutGroupBox
//
this.timeoutGroupBox.Controls.Add(this.timeoutComboBox);
this.timeoutGroupBox.Location = new System.Drawing.Point(413, 24);
this.timeoutGroupBox.Name = "timeoutGroupBox";
this.timeoutGroupBox.Size = new System.Drawing.Size(110, 52);
this.timeoutGroupBox.TabIndex = 20;
this.timeoutGroupBox.TabStop = false;
this.timeoutGroupBox.Text = "Timeout";
//
// timeoutComboBox
//
this.timeoutComboBox.AccessibleRole = System.Windows.Forms.AccessibleRole.None;
this.timeoutComboBox.FormattingEnabled = true;
this.timeoutComboBox.Items.AddRange(new object[] {
"500 ms",
"1000 ms",
"1500 ms",
"2000 ms"});
this.timeoutComboBox.Location = new System.Drawing.Point(6, 21);
this.timeoutComboBox.Name = "timeoutComboBox";
this.timeoutComboBox.Size = new System.Drawing.Size(98, 21);
this.timeoutComboBox.TabIndex = 0;
//
// checkPortButton
//
this.checkPortButton.Location = new System.Drawing.Point(529, 21);
this.checkPortButton.Name = "checkPortButton";
this.checkPortButton.Size = new System.Drawing.Size(105, 31);
this.checkPortButton.TabIndex = 5;
this.checkPortButton.Text = "Scan";
this.checkPortButton.UseVisualStyleBackColor = true;
this.checkPortButton.Click += new System.EventHandler(this.checkPortButton_Click);
//
// listview1
//
this.listview1.Activation = System.Windows.Forms.ItemActivation.OneClick;
this.listview1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.listview1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.chPort,
this.chStatus});
this.listview1.FullRowSelect = true;
this.listview1.GridLines = true;
this.listview1.HideSelection = false;
this.listview1.HoverSelection = true;
this.listview1.Location = new System.Drawing.Point(0, 89);
this.listview1.MultiSelect = false;
this.listview1.Name = "listview1";
this.listview1.RightToLeft = System.Windows.Forms.RightToLeft.No;
this.listview1.Size = new System.Drawing.Size(800, 361);
this.listview1.TabIndex = 21;
this.listview1.UseCompatibleStateImageBehavior = false;
this.listview1.View = System.Windows.Forms.View.Details;
//
// chPort
//
this.chPort.Text = "Port";
this.chPort.Width = 191;
//
// chStatus
//
this.chStatus.Text = "Status";
this.chStatus.Width = 296;
//
// progressBar1
//
this.progressBar1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.progressBar1.Location = new System.Drawing.Point(0, 79);
this.progressBar1.Name = "progressBar1";
this.progressBar1.Size = new System.Drawing.Size(800, 10);
this.progressBar1.TabIndex = 22;
//
// cancelButton
//
this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancelButton.Location = new System.Drawing.Point(529, 52);
this.cancelButton.Name = "cancelButton";
this.cancelButton.Size = new System.Drawing.Size(105, 23);
this.cancelButton.TabIndex = 23;
this.cancelButton.Text = "Cancel";
this.cancelButton.UseVisualStyleBackColor = true;
this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click);
//
// WF_PortScanner
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.cancelButton);
this.Controls.Add(this.progressBar1);
this.Controls.Add(this.listview1);
this.Controls.Add(this.checkPortButton);
this.Controls.Add(this.timeoutGroupBox);
this.Controls.Add(this.portRangeCheckBox);
this.Controls.Add(this.portTextBoxMax);
this.Controls.Add(this.dashLabel);
this.Controls.Add(this.portTextBoxMin);
this.Controls.Add(this.portLabel);
this.Controls.Add(this.modeGroupBox);
this.Controls.Add(this.label3);
this.Controls.Add(this.hostnameTextBox);
this.Name = "WF_PortScanner";
this.Size = new System.Drawing.Size(800, 450);
this.modeGroupBox.ResumeLayout(false);
this.modeGroupBox.PerformLayout();
this.timeoutGroupBox.ResumeLayout(false);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox hostnameTextBox;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.GroupBox modeGroupBox;
private System.Windows.Forms.RadioButton udpModeRadioButton;
private System.Windows.Forms.RadioButton tcpModeRadioButton;
private System.Windows.Forms.CheckBox portRangeCheckBox;
private System.Windows.Forms.TextBox portTextBoxMax;
private System.Windows.Forms.Label dashLabel;
private System.Windows.Forms.TextBox portTextBoxMin;
private System.Windows.Forms.Label portLabel;
private System.Windows.Forms.GroupBox timeoutGroupBox;
private System.Windows.Forms.ComboBox timeoutComboBox;
private System.Windows.Forms.Button checkPortButton;
private System.Windows.Forms.ListView listview1;
private System.Windows.Forms.ColumnHeader chPort;
private System.Windows.Forms.ColumnHeader chStatus;
private System.Windows.Forms.ProgressBar progressBar1;
private System.Windows.Forms.Button cancelButton;
}
}

View File

@@ -0,0 +1,271 @@
using PortScanner;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Networking.Pages
{
public partial class WF_PortScanner : UserControl
{
// Delegate to report back with one open port
public delegate void ExecuteOnceCallback(int openPort);
// Delegate to report back with one open port (Async)
public delegate void ExecuteOnceAsyncCallback(int port, bool isOpen, bool isCancelled, bool isLast);
// The manager instance
IScannerManagerSingleton smc;
// Cancellation token source for the cancel button
private CancellationTokenSource cts;
// Current mode of operation
private ScannerManagerSingleton.ScanMode currentScanMode;
public WF_PortScanner()
{
InitializeComponent();
this.Load += WF_PortScanner_Load;
}
private void WF_PortScanner_Load(object sender, EventArgs e)
{
// Get the ScannerManagerSingleton instance
smc = ScannerManagerSingleton.Instance;
// Populate the timeout times list box
PopulateTimeoutListBox();
}
private void PopulateTimeoutListBox()
{
// Assign the list to the ComboBox's DataSource property
timeoutComboBox.DataSource = TimeoutListItem.CreateTimeoutListItems();
timeoutComboBox.DisplayMember = "DisplayMember";
timeoutComboBox.ValueMember = "ValueMember";
// Set default value
timeoutComboBox.SelectedValue = 2000;
}
// This method is used as a callback for portscanning - writes to the log box (text box)
public void PortResult(int port, bool isOpen, bool isCancelled, bool isLast)
{
string status;
// The operation has been cancelled by MainWindow
if (isCancelled)
{
status = "Operation cancelled." + Environment.NewLine;
}
// The port is open
else if (isOpen)
{
//status = String.Format("{0}, {1} port {2} is open.{3}", hostnameTextBox.Text, currentScanMode.ToString(), port, Environment.NewLine);
var item = new ListViewItem(new[] { port.ToString(), "Open" });
listview1.Items.Add(item);
}
// The port is closed
else
{
status = String.Format("{0}, {1} port {2} is closed.{3}", hostnameTextBox.Text, currentScanMode.ToString(), port, Environment.NewLine);
}
if (isLast || isCancelled)
ToggleInputs(true);
}
// Checks whether the timeout combo box has user input or not
private bool IsTimeoutComboBoxUserInput()
{
var inputText = timeoutComboBox.Text;
foreach (var displayMemberText in (List<TimeoutListItem>)timeoutComboBox.DataSource)
{
if (displayMemberText.DisplayMember == inputText)
{
// Select the one that's in the box's list to prevent some problems
// This will return because the user input IS in the combo box's DataSource
// However it is still user input and does not have a ValueMember. Exception
// will be thrown.
timeoutComboBox.SelectedItem = displayMemberText;
return false;
}
}
return true;
}
// Executed when the Check Port button is clicked
private void checkPortButton_Click(object sender, EventArgs e)
{
listview1.Items.Clear();
// Get user inputs
string hostname = hostnameTextBox.Text;
if (hostname == "")
{
MessageBox.Show("Please enter a valid hostname.",
"Input Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
hostnameTextBox.Focus();
return;
}
// Check port
int portMin = InputChecker.ParsePort(portTextBoxMin.Text);
if (portMin == -1)
{
MessageBox.Show((portRangeCheckBox.Checked ? "Lower limit of port range" : "Port") + " invalid.",
"Input Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
portTextBoxMin.Focus();
return;
}
// Get scan mode
ScannerManagerSingleton.ScanMode scanMode = ReadScanMode();
// If custom timeout time, verify correct user input
int timeout;
if (IsTimeoutComboBoxUserInput())
{
// If valid, proceed with that input as timeout
timeout = InputChecker.ParseTimeout(timeoutComboBox.Text);
if (timeout == -1)
{
MessageBox.Show("Timeout format: [time], [time]ms or [time] ms.\nTimeout must be between 250 ms and 20000 ms.",
"Timeout Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
return;
}
}
else
{
// Else, use the ValueMember of the selected Member
timeout = (int)timeoutComboBox.SelectedValue;
}
// Instantiate CTS
cts = new CancellationTokenSource();
// Simple one port check
if (!portRangeCheckBox.Checked)
{
// The callback for scan result
var callback = new ExecuteOnceAsyncCallback(PortResult);
// Send one check request and toggle user inputs
ToggleInputs(false);
smc.ExecuteOnceAsync(hostname, portMin, timeout, scanMode, callback, cts.Token);
}
// Port range check
else
{
// Verify input
int portMax = InputChecker.ParsePort(portTextBoxMax.Text);
if (portMax == -1)
{
MessageBox.Show("Upper limit of port range invalid.",
"Input Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
portTextBoxMax.Focus();
return;
}
if (portMax < portMin)
{
MessageBox.Show("Port range invalid.",
"Input Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
portTextBoxMax.Focus();
return;
}
// The callback for scan result
var callback = new ExecuteOnceAsyncCallback(PortResult);
// Set status box text
//var connectionText = String.Format("Connecting to {0}, port {1}...{2}", hostname, portMin,
// Environment.NewLine);
//statusTextBox.AppendText(connectionText);
//Logger.Info(connectionText);
// Toggle inputs and begin operation
ToggleInputs(false);
smc.ExecuteRangeAsync(hostname, portMin, portMax, timeout, scanMode,progressBar1, callback, cts.Token);
}
}
// Read scan mode radio button selection
private ScannerManagerSingleton.ScanMode ReadScanMode()
{
if (tcpModeRadioButton.Checked)
{
currentScanMode = ScannerManagerSingleton.ScanMode.TCP;
}
else
{
currentScanMode = ScannerManagerSingleton.ScanMode.UDP;
}
return currentScanMode;
}
private void portRangeCheckBox_CheckedChanged(object sender, EventArgs e)
{
// This enables or disables the max. port input box
if (portRangeCheckBox.Checked)
{
portTextBoxMax.Enabled = true;
}
else
{
portTextBoxMax.Enabled = false;
}
}
// Toggle all inputs
private void ToggleInputs(bool setting)
{
hostnameTextBox.Enabled = setting;
portTextBoxMin.Enabled = setting;
checkPortButton.Enabled = setting;
portTextBoxMax.Enabled = setting;
portRangeCheckBox.Enabled = setting;
// Re-disable the portMax text box
if (!portRangeCheckBox.Checked)
{
portTextBoxMax.Enabled = false;
}
// Set focus to hostnameTextBox
if (setting)
hostnameTextBox.Focus();
}
private void cancelButton_Click(object sender, EventArgs e)
{
// If cts is instantiated (i.e. the scanning operation is in progress, request cancellation
if (cts != null)
{
cts.Cancel();
progressBar1.Value = 0;
}
}
}
}

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>