Implemented the server form. Client <-> Server interaction actually works.

Fixed a few bugs in the CustomServer - wasn't configuring the listen socket correctly, doing read/write operations from the wrong socket, etc. Reworked how the CustomConnection class handles serialization.

Added Disconnected events to the sample's CustomConnection and CustomServer classes so the forms could reset state.
This commit is contained in:
antiduh
2014-06-26 18:00:50 +00:00
parent 81ed80a4d0
commit 5b3a92ee66
8 changed files with 626 additions and 17 deletions

View File

@@ -75,6 +75,7 @@
this.serverTextBox.Name = "serverTextBox";
this.serverTextBox.Size = new System.Drawing.Size(137, 20);
this.serverTextBox.TabIndex = 2;
this.serverTextBox.Text = "localhost";
//
// portNumeric
//

View File

@@ -40,14 +40,13 @@ namespace TestClient
InitializeComponent();
this.connectButton.Click += connectButton_Click;
this.disconnectButton.Click += disconnectButton_Click;
this.encryptButton.Click += encryptButton_Click;
this.signButton.Click += signButton_Click;
this.FormClosing += Form1_FormClosing;
// --- SSPI ---
this.cred = new ClientCredential( SecurityPackage.Negotiate );
@@ -64,6 +63,7 @@ namespace TestClient
this.connection = new CustomConnection();
this.connection.Received += connection_Received;
this.connection.Disconnected += connection_Disconnected;
// --- UI Fillout ---
this.usernameTextbox.Text = this.cred.Name;
@@ -71,6 +71,11 @@ namespace TestClient
UpdateButtons();
}
private void Form1_FormClosing( object sender, FormClosingEventArgs e )
{
this.connection.Stop();
}
private void connectButton_Click( object sender, EventArgs e )
{
if( string.IsNullOrWhiteSpace( this.serverTextBox.Text ) )
@@ -82,6 +87,8 @@ namespace TestClient
try
{
this.connection.StartClient( this.serverTextBox.Text, (int)this.portNumeric.Value );
this.initializing = true;
DoInit();
}
catch( SocketException socketExcept )
{
@@ -101,7 +108,6 @@ namespace TestClient
}
}
this.initializing = true;
}
}
@@ -174,6 +180,18 @@ namespace TestClient
} );
}
private void connection_Disconnected()
{
this.connected = false;
this.initializing = false;
this.lastServerToken = null;
this.Invoke( (Action)delegate()
{
UpdateButtons();
});
}
private void DoInit()
{
SecurityStatus status;

View File

@@ -27,6 +27,8 @@ namespace TestProtocol
public event ReceivedAction Received;
public event Action Disconnected;
public void StartClient( string server, int port )
{
if( this.running )
@@ -49,7 +51,7 @@ namespace TestProtocol
{
if( this.running == false )
{
throw new InvalidOperationException( "Already stopped" );
return;
}
this.socket.Close();
@@ -64,13 +66,19 @@ namespace TestProtocol
}
byte[] outBuffer = new byte[ message.Data.Length + 8 ];
int position = 0;
ByteWriter.WriteInt32_BE( (int)message.Operation, outBuffer, 0 );
ByteWriter.WriteInt32_BE( message.Data.Length, outBuffer, 4 );
ByteWriter.WriteInt32_BE( (int)message.Operation, outBuffer, position );
position += 4;
Array.Copy( message.Data, 0, outBuffer, 8, message.Data.Length );
ByteWriter.WriteInt32_BE( message.Data.Length, outBuffer, position );
position += 4;
Array.Copy( message.Data, 0, outBuffer, position, message.Data.Length );
this.socket.Send( outBuffer, 0, outBuffer.Length, SocketFlags.None );
Console.Out.WriteLine( "Client: Sent " + message.Operation );
}
private void ReceiveThreadEntry()
@@ -82,7 +90,20 @@ namespace TestProtocol
catch( Exception e )
{
MessageBox.Show( "The SspiConnection receive thread crashed:\r\n\r\n" + e.ToString() );
}
finally
{
this.running = false;
try
{
if( this.Disconnected != null )
{
this.Disconnected();
}
}
catch
{ }
}
}
@@ -133,6 +154,7 @@ namespace TestProtocol
}
}
Console.Out.WriteLine( "Client: Received " + operation );
if( this.Received != null )
{

View File

@@ -9,6 +9,7 @@ namespace TestProtocol
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
@@ -35,6 +36,8 @@ namespace TestProtocol
public event ReceivedAction Received;
public event Action Disconnected;
public void StartServer( int port )
{
if( this.running )
@@ -43,6 +46,8 @@ namespace TestProtocol
}
this.serverSocket = new Socket( SocketType.Stream, ProtocolType.Tcp );
this.serverSocket.Bind( new IPEndPoint( IPAddress.Any, port ) );
this.serverSocket.Listen( 1 );
this.running = true;
@@ -55,7 +60,7 @@ namespace TestProtocol
{
if( this.running == false )
{
throw new InvalidOperationException( "Already stopped" );
return;
}
this.serverSocket.Close();
@@ -82,7 +87,9 @@ namespace TestProtocol
Array.Copy( message.Data, 0, outBuffer, 8, message.Data.Length );
this.serverSocket.Send( outBuffer, 0, outBuffer.Length, SocketFlags.None );
this.readSocket.Send( outBuffer, 0, outBuffer.Length, SocketFlags.None );
Console.Out.WriteLine( "Server: Sent " + message.Operation );
}
private void ReceiveThreadEntry()
@@ -112,13 +119,25 @@ namespace TestProtocol
}
ReadLoop();
}
}
catch( Exception e )
{
MessageBox.Show( "The SspiConnection receive thread crashed:\r\n\r\n" + e.ToString() );
}
finally
{
this.running = false;
try
{
if( this.Disconnected != null )
{
this.Disconnected();
}
}
catch
{ }
}
}
@@ -138,7 +157,7 @@ namespace TestProtocol
// Read the operation.
this.serverSocket.Receive( readBuffer, 4, SocketFlags.None );
this.readSocket.Receive( readBuffer, 4, SocketFlags.None );
// Check if we popped out of a receive call after we were shut down.
if( this.running == false ) { break; }
@@ -146,11 +165,11 @@ namespace TestProtocol
operation = (ProtocolOp)ByteWriter.ReadInt32_BE( readBuffer, 0 );
// Read the length
this.serverSocket.Receive( readBuffer, 4, SocketFlags.None );
this.readSocket.Receive( readBuffer, 4, SocketFlags.None );
length = ByteWriter.ReadInt32_BE( readBuffer, 0 );
// Read the data
this.serverSocket.Receive( readBuffer, length, SocketFlags.None );
this.readSocket.Receive( readBuffer, length, SocketFlags.None );
}
catch( SocketException e )
@@ -169,6 +188,7 @@ namespace TestProtocol
}
}
Console.Out.WriteLine( "Server: Received " + operation );
if( this.Received != null )
{
@@ -180,7 +200,7 @@ namespace TestProtocol
{
this.Received( message );
}
catch( Exception e )
catch( Exception )
{ }
}

View File

@@ -28,12 +28,226 @@
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.label1 = new System.Windows.Forms.Label();
this.portNumeric = new System.Windows.Forms.NumericUpDown();
this.startButton = new System.Windows.Forms.Button();
this.stopButton = new System.Windows.Forms.Button();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.usernameTextbox = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.groupBox3 = new System.Windows.Forms.GroupBox();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.sendTextbox = new System.Windows.Forms.TextBox();
this.receivedTextbox = new System.Windows.Forms.TextBox();
this.encryptButton = new System.Windows.Forms.Button();
this.signButton = new System.Windows.Forms.Button();
((System.ComponentModel.ISupportInitialize)(this.portNumeric)).BeginInit();
this.groupBox1.SuspendLayout();
this.groupBox2.SuspendLayout();
this.groupBox3.SuspendLayout();
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 21);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(83, 13);
this.label1.TabIndex = 0;
this.label1.Text = "Port to listen on:";
//
// portNumeric
//
this.portNumeric.Location = new System.Drawing.Point(101, 19);
this.portNumeric.Maximum = new decimal(new int[] {
65536,
0,
0,
0});
this.portNumeric.Minimum = new decimal(new int[] {
1,
0,
0,
0});
this.portNumeric.Name = "portNumeric";
this.portNumeric.Size = new System.Drawing.Size(69, 20);
this.portNumeric.TabIndex = 1;
this.portNumeric.Value = new decimal(new int[] {
10000,
0,
0,
0});
//
// startButton
//
this.startButton.Location = new System.Drawing.Point(196, 16);
this.startButton.Name = "startButton";
this.startButton.Size = new System.Drawing.Size(75, 23);
this.startButton.TabIndex = 2;
this.startButton.Text = "Start server";
this.startButton.UseVisualStyleBackColor = true;
//
// stopButton
//
this.stopButton.Location = new System.Drawing.Point(277, 16);
this.stopButton.Name = "stopButton";
this.stopButton.Size = new System.Drawing.Size(75, 23);
this.stopButton.TabIndex = 3;
this.stopButton.Text = "Stop server";
this.stopButton.UseVisualStyleBackColor = true;
//
// groupBox1
//
this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.groupBox1.Controls.Add(this.label2);
this.groupBox1.Controls.Add(this.usernameTextbox);
this.groupBox1.Location = new System.Drawing.Point(400, 12);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(313, 65);
this.groupBox1.TabIndex = 4;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Server credentials";
//
// usernameTextbox
//
this.usernameTextbox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.usernameTextbox.Location = new System.Drawing.Point(74, 20);
this.usernameTextbox.Name = "usernameTextbox";
this.usernameTextbox.ReadOnly = true;
this.usernameTextbox.Size = new System.Drawing.Size(233, 20);
this.usernameTextbox.TabIndex = 0;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(7, 23);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(61, 13);
this.label2.TabIndex = 1;
this.label2.Text = "User name:";
//
// groupBox2
//
this.groupBox2.Controls.Add(this.signButton);
this.groupBox2.Controls.Add(this.encryptButton);
this.groupBox2.Controls.Add(this.sendTextbox);
this.groupBox2.Dock = System.Windows.Forms.DockStyle.Fill;
this.groupBox2.Location = new System.Drawing.Point(3, 3);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(344, 381);
this.groupBox2.TabIndex = 5;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "Send a message to the client";
//
// groupBox3
//
this.groupBox3.Controls.Add(this.receivedTextbox);
this.groupBox3.Dock = System.Windows.Forms.DockStyle.Fill;
this.groupBox3.Location = new System.Drawing.Point(353, 3);
this.groupBox3.Name = "groupBox3";
this.groupBox3.Size = new System.Drawing.Size(345, 381);
this.groupBox3.TabIndex = 6;
this.groupBox3.TabStop = false;
this.groupBox3.Text = "Received messages from the client";
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.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.tableLayoutPanel1.ColumnCount = 2;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.Controls.Add(this.groupBox2, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.groupBox3, 1, 0);
this.tableLayoutPanel1.Location = new System.Drawing.Point(12, 87);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 1;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(701, 387);
this.tableLayoutPanel1.TabIndex = 7;
//
// sendTextbox
//
this.sendTextbox.Location = new System.Drawing.Point(6, 19);
this.sendTextbox.Multiline = true;
this.sendTextbox.Name = "sendTextbox";
this.sendTextbox.Size = new System.Drawing.Size(331, 322);
this.sendTextbox.TabIndex = 0;
//
// receivedTextbox
//
this.receivedTextbox.Dock = System.Windows.Forms.DockStyle.Fill;
this.receivedTextbox.Location = new System.Drawing.Point(3, 16);
this.receivedTextbox.Multiline = true;
this.receivedTextbox.Name = "receivedTextbox";
this.receivedTextbox.Size = new System.Drawing.Size(339, 362);
this.receivedTextbox.TabIndex = 0;
//
// encryptButton
//
this.encryptButton.Location = new System.Drawing.Point(33, 349);
this.encryptButton.Name = "encryptButton";
this.encryptButton.Size = new System.Drawing.Size(120, 23);
this.encryptButton.TabIndex = 2;
this.encryptButton.Text = "Encrypt and send";
this.encryptButton.UseVisualStyleBackColor = true;
//
// signButton
//
this.signButton.Location = new System.Drawing.Point(174, 349);
this.signButton.Name = "signButton";
this.signButton.Size = new System.Drawing.Size(117, 23);
this.signButton.TabIndex = 3;
this.signButton.Text = "Sign and send";
this.signButton.UseVisualStyleBackColor = true;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Text = "Form1";
this.ClientSize = new System.Drawing.Size(725, 477);
this.Controls.Add(this.tableLayoutPanel1);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.stopButton);
this.Controls.Add(this.startButton);
this.Controls.Add(this.portNumeric);
this.Controls.Add(this.label1);
this.Name = "Form1";
this.Text = "Server - SSPI Test";
((System.ComponentModel.ISupportInitialize)(this.portNumeric)).EndInit();
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
this.groupBox2.ResumeLayout(false);
this.groupBox2.PerformLayout();
this.groupBox3.ResumeLayout(false);
this.groupBox3.PerformLayout();
this.tableLayoutPanel1.ResumeLayout(false);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label label1;
private System.Windows.Forms.NumericUpDown portNumeric;
private System.Windows.Forms.Button startButton;
private System.Windows.Forms.Button stopButton;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox usernameTextbox;
private System.Windows.Forms.GroupBox groupBox2;
private System.Windows.Forms.GroupBox groupBox3;
private System.Windows.Forms.Button signButton;
private System.Windows.Forms.Button encryptButton;
private System.Windows.Forms.TextBox sendTextbox;
private System.Windows.Forms.TextBox receivedTextbox;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
}
}

View File

@@ -7,14 +7,215 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using TestProtocol;
namespace TestServer
{
using NSspi;
using NSspi.Contexts;
using NSspi.Credentials;
using Message = TestProtocol.Message;
public partial class Form1 : Form
{
private ServerCredential serverCred;
private ServerContext serverContext;
private CustomServer server;
private bool running;
private bool initializing;
private bool connected;
public Form1()
{
InitializeComponent();
this.serverCred = new ServerCredential( SecurityPackage.Negotiate );
this.serverContext = new ServerContext(
serverCred,
ContextAttrib.AcceptIntegrity |
ContextAttrib.ReplayDetect |
ContextAttrib.SequenceDetect |
ContextAttrib.MutualAuth |
ContextAttrib.Delegate |
ContextAttrib.Confidentiality
);
this.server = new CustomServer();
this.server.Received += server_Received;
this.server.Disconnected += server_Disconnected;
this.FormClosing += Form1_FormClosing;
this.startButton.Click += startButton_Click;
this.stopButton.Click += stopButton_Click;
this.encryptButton.Click += encryptButton_Click;
this.signButton.Click += signButton_Click;
this.running = false;
this.initializing = false;
this.connected = false;
UpdateButtons();
}
private void Form1_FormClosing( object sender, FormClosingEventArgs e )
{
this.server.Stop();
}
private void startButton_Click( object sender, EventArgs e )
{
this.server.StartServer( (int)this.portNumeric.Value );
this.running = true;
this.initializing = true;
this.connected = false;
UpdateButtons();
}
private void stopButton_Click( object sender, EventArgs e )
{
this.server.Stop();
this.running = false;
this.initializing = false;
this.connected = false;
UpdateButtons();
}
private void encryptButton_Click( object sender, EventArgs e )
{
Message message;
byte[] plainText = Encoding.UTF8.GetBytes( this.sendTextbox.Text );
byte[] cipherText = this.serverContext.Encrypt( plainText );
message = new Message( ProtocolOp.EncryptedMessage, cipherText );
this.server.Send( message );
}
private void signButton_Click( object sender, EventArgs e )
{
// Not implemented.
}
private void UpdateButtons()
{
this.startButton.Enabled = this.running == false;
this.stopButton.Enabled = this.running;
this.encryptButton.Enabled = this.connected;
this.signButton.Enabled = this.connected;
}
private void server_Received( Message message )
{
if( message.Operation == ProtocolOp.ClientToken )
{
HandleInit( message );
}
else if( message.Operation == ProtocolOp.EncryptedMessage )
{
HandleEncrypted( message );
}
else if( message.Operation == ProtocolOp.SignedMessage )
{
HandleSigned( message );
}
else
{
HandleUnknown( message );
}
}
private void server_Disconnected()
{
this.running = false;
this.initializing = false;
this.connected = false;
UpdateButtons();
}
private void HandleInit( Message message )
{
byte[] nextToken;
SecurityStatus status;
if( initializing )
{
status = this.serverContext.AcceptToken( message.Data, out nextToken );
if( status == SecurityStatus.OK || status == SecurityStatus.ContinueNeeded )
{
if( nextToken != null )
{
this.server.Send( new Message( ProtocolOp.ServerToken, nextToken ) );
}
if( status == SecurityStatus.OK )
{
this.initializing = false;
this.connected = true;
this.Invoke( (Action)delegate() { UpdateButtons(); } );
}
}
else
{
this.Invoke( (Action)delegate()
{
MessageBox.Show( "Failed to accept token from client. Sspi error code: " + status );
} );
}
}
}
private void HandleEncrypted( Message message )
{
this.Invoke( (Action)delegate()
{
byte[] plainText = this.serverContext.Decrypt( message.Data );
string text = Encoding.UTF8.GetString( plainText );
this.receivedTextbox.Text += "Received encrypted message from client:\r\n" + text + "\r\n";
} );
}
private void HandleSigned( Message message )
{
// Not implemented yet.
/*
this.Invoke( (Action)delegate()
{
byte[] plainText = this.serverContext.Decrypt( message.Data );
string text = Encoding.UTF8.GetString( plainText );
this.receivedTextbox.Text += "Received encrypted message from client:\r\n" + text + "\r\n";
} );
*/
}
private void HandleUnknown( Message message )
{
this.Invoke( (Action)delegate()
{
MessageBox.Show( "Received unexpected message from server. Message type: " + message.Operation );
} );
}
}
}

120
TestServer/Form1.resx Normal file
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>

View File

@@ -52,6 +52,9 @@
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="Form1.resx">
<DependentUpon>Form1.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
@@ -74,6 +77,16 @@
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NSspi.csproj">
<Project>{4b4cd933-bf62-4f92-b8fa-6771758c5197}</Project>
<Name>NSspi</Name>
</ProjectReference>
<ProjectReference Include="..\TestProtocol\TestProtocol.csproj">
<Project>{9bfd94e1-d9fb-44d7-a6e7-8bac2620e535}</Project>
<Name>TestProtocol</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.