I don't have much experience doing unit testing. From what I learned, code should be decoupled, and I should not strive to test private code, just public methods, setters, etc etc.
Now, I have grasped some basic testing concepts, but I have troubles applying more advanced stuff to this case... Dependency Injection, Inversion of Control, Mock Objects, etc - can't get my head around it just yet :(
Before I move on to code, here are the questions.
In a given class, what exactly should I try to test?
How can I accomplish those test tasks?
Is there something seriously wrong with class design, which prevents testing from being done properly (or is something just plain wrong even outside of testing context)?
What design patterns are useful to testing network code in general?
Also, I've been trying to obey "write tests first, then write code to make tests pass", that's why I wrote first two tests that simply instantiate class and run it, but then, when server was able to start and accept packets, I didn't know what to test next...
Okay, here goes the code snippet. (note: original code is split in several namespaces, that's why it may appear a little out of order)
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace MyProject1
{
/// <summary>
/// Packet buffer that is sent to/received from connection
/// </summary>
public class UDPPacketBuffer
{
/// <summary>
/// Buffer size constant
/// </summary>
public const int BUFFER_SIZE = 4096;
private byte[] _data;
/// <summary>
/// Byte array with buffered data
///
/// DataLength is automatically updated when Data is set
/// </summary>
/// <see cref="DataLength"/>
public byte[] Data { get { return _data; } set { _data = value; DataLength = value.Length; } }
/// <summary>
/// Integer with length of buffered data
/// </summary>
public int DataLength;
/// <summary>
/// Remote end point (IP Address and Port)
/// </summary>
public EndPoint RemoteEndPoint;
/// <summary>
/// Initializes <see cref="UDPPacketBuffer"/> class
/// </summary>
public UDPPacketBuffer()
{
// initialize byte array
this.Data = new byte[BUFFER_SIZE];
// this will be filled in by the caller (eg. udpSocket.BeginReceiveFrom)
RemoteEndPoint = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
}
/// <summary>
/// Returns <see cref="Data"/> as a byte array shortened to <see cref="DataLength"/> number of bytes
/// </summary>
public byte[] ByteContent
{
get
{
if (DataLength > 0)
{
byte[] content = new byte[DataLength];
for (int i = 0; i < DataLength; i++)
content[i] = Data[i];
return content;
}
else
{
return Data;
}
}
}
/// <summary>
/// Returns <see cref="ByteContent"/> converted to string
/// </summary>
public string StringContent { get { return Encoding.ASCII.GetString(ByteContent); } }
}
/// <summary>
/// UDP packet-related event arguments passed when invoking events
/// </summary>
/// <example>
/// This example shows how to use UDPPacketEventArgs class when event is invoked.
/// <code>
/// if (PacketSent != null)
/// PacketSent(this, new UDPPacketEventArgs(buffer, bytesSent));
/// </code>
/// </example>
public class UDPPacketEventArgs : EventArgs
{
/// <summary>
/// Instance of UDPPacketBuffer, holding current event-related buffer
/// </summary>
public UDPPacketBuffer buffer { get; private set; }
/// <summary>
/// Number of bytes sent to remote end point
/// </summary>
public int sent { get; private set; }
/// <summary>
/// Initializes <see cref="buffer"/> only. Used when receiving data.
/// </summary>
/// <param name="buffer">Buffer sent to or received from remote endpoint</param>
public UDPPacketEventArgs(UDPPacketBuffer buffer)
{
this.buffer = buffer;
}
/// <summary>
/// Initializes <see cref="buffer"/> and <see cref="sent"/> variables. Used when sending data.
/// </summary>
/// <param name="buffer">buffer that has been sent</param>
/// <param name="sent">number of bytes sent</param>
public UDPPacketEventArgs(UDPPacketBuffer buffer, int sent)
{
this.buffer = buffer;
this.sent = sent;
}
}
/// <summary>
/// Asynchronous UDP server
/// </summary>
public class AsyncUdp : ServerBase
{
private const int _defaultPort = 45112;
private int _udpPort;
/// <summary>
/// Port number on which server should listen
/// </summary>
public int udpPort { get { return _udpPort; } private set { _udpPort = value; } }
// should server listen for broadcasts?
private bool broadcast = false;
// server socket
private Socket udpSocket;
// the ReaderWriterLock is used solely for the purposes of shutdown (Stop()).
// since there are potentially many "reader" threads in the internal .NET IOCP
// thread pool, this is a cheaper synchronization primitive than using
// a Mutex object. This allows many UDP socket "reads" concurrently - when
// Stop() is called, it attempts to obtain a writer lock which will then
// wait until all outstanding operations are completed before shutting down.
// this avoids the problem of closing the socket with outstanding operations
// and trying to catch the inevitable ObjectDisposedException.
private ReaderWriterLock rwLock = new ReaderWriterLock();
// number of outstanding operations. This is a reference count
// which we use to ensure that the threads exit cleanly. Note that
// we need this because the threads will potentially still need to process
// data even after the socket is closed.
private int rwOperationCount = 0;
// the all important shutdownFlag. This is synchronized through the ReaderWriterLock.
private bool shutdownFlag = true;
/// <summary>
/// Returns server running state
/// </summary>
public bool IsRunning
{
get { return !shutdownFlag; }
}
/// <summary>
/// Initializes UDP server with arbitrary default port
/// </summary>
public AsyncUdp()
{
this.udpPort = _defaultPort;
}
/// <summary>
/// Initializes UDP server with specified port number
/// </summary>
/// <param name="port">Port number for server to listen on</param>
public AsyncUdp(int port)
{
this.udpPort = port;
}
/// <summary>
/// Initializes UDP server with specified port number and broadcast capability
/// </summary>
/// <param name="port">Port number for server to listen on</param>
/// <param name="broadcast">Server will have broadcasting enabled if set to true</param>
public AsyncUdp(int port, bool broadcast)
{
this.udpPort = port;
this.broadcast = broadcast;
}
/// <summary>
/// Raised when packet is received via UDP socket
/// </summary>
public event EventHandler PacketReceived;
/// <summary>
/// Raised when packet is sent via UDP socket
/// </summary>
public event EventHandler PacketSent;
/// <summary>
/// Starts UDP server
/// </summary>
public override void Start()
{
if (! IsRunning)
{
// create and bind the socket
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, udpPort);
udpSocket = new Socket(
AddressFamily.InterNetwork,
SocketType.Dgram,
ProtocolType.Udp);
udpSocket.EnableBroadcast = broadcast;
// we don't want to receive our own broadcasts, if broadcasting is enabled
if (broadcast)
udpSocket.MulticastLoopback = false;
udpSocket.Bind(ipep);
// we're not shutting down, we're starting up
shutdownFlag = false;
// kick off an async receive. The Start() method will return, the
// actual receives will occur asynchronously and will be caught in
// AsyncEndRecieve().
// I experimented with posting multiple AsyncBeginReceives() here in an attempt
// to "queue up" reads, however I found that it negatively impacted performance.
AsyncBeginReceive();
}
}
/// <summary>
/// Stops UDP server, if it is running
/// </summary>
public override void Stop()
{
if (IsRunning)
{
// wait indefinitely for a writer lock. Once this is called, the .NET runtime
// will deny any more reader locks, in effect blocking all other send/receive
// threads. Once we have the lock, we set shutdownFlag to inform the other
// threads that the socket is closed.
rwLock.AcquireWriterLock(-1);
shutdownFlag = true;
udpSocket.Close();
rwLock.ReleaseWriterLock();
// wait for any pending operations to complete on other
// threads before exiting.
while (rwOperationCount > 0)
Thread.Sleep(1);
}
}
/// <summary>
/// Dispose handler for UDP server. Stops the server first if it is still running
/// </summary>
public override void Dispose()
{
if (IsRunning == true)
this.Stop();
}
private void AsyncBeginReceive()
{
// this method actually kicks off the async read on the socket.
// we aquire a reader lock here to ensure that no other thread
// is trying to set shutdownFlag and close the socket.
rwLock.AcquireReaderLock(-1);
if (!shutdownFlag)
{
// increment the count of pending operations
Interlocked.Increment(ref rwOperationCount);
// allocate a packet buffer
UDPPacketBuffer buf = new UDPPacketBuffer();
try
{
// kick off an async read
udpSocket.BeginReceiveFrom(
buf.Data,
0,
UDPPacketBuffer.BUFFER_SIZE,
SocketFlags.None,
ref buf.RemoteEndPoint,
new AsyncCallback(AsyncEndReceive),
buf);
}
catch (SocketException)
{
// an error occurred, therefore the operation is void. Decrement the reference count.
Interlocked.Decrement(ref rwOperationCount);
}
}
// we're done with the socket for now, release the reader lock.
rwLock.ReleaseReaderLock();
}
private void AsyncEndReceive(IAsyncResult iar)
{
// Asynchronous receive operations will complete here through the call
// to AsyncBeginReceive
// aquire a reader lock
rwLock.AcquireReaderLock(-1);
if (!shutdownFlag)
{
// start another receive - this keeps the server going!
AsyncBeginReceive();
// get the buffer that was created in AsyncBeginReceive
// this is the received data
UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState;
try
{
// get the length of data actually read from the socket, store it with the
// buffer
buffer.DataLength = udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint);
// this operation is now complete, decrement the reference count
Interlocked.Decrement(ref rwOperationCount);
// we're done with the socket, release the reader lock
rwLock.ReleaseReaderLock();
// run event PacketReceived(), passing the buffer that
// has just been filled from the socket read.
if (PacketReceived != null)
PacketReceived(this, new UDPPacketEventArgs(buffer));
}
catch (SocketException)
{
// an error occurred, therefore the operation is void. Decrement the reference count.
Interlocked.Decrement(ref rwOperationCount);
// we're done with the socket for now, release the reader lock.
rwLock.ReleaseReaderLock();
}
}
else
{
// nothing bad happened, but we are done with the operation
// decrement the reference count and release the reader lock
Interlocked.Decrement(ref rwOperationCount);
rwLock.ReleaseReaderLock();
}
}
/// <summary>
/// Send packet to remote end point speified in <see cref="UDPPacketBuffer"/>
/// </summary>
/// <param name="buf">Packet to send</param>
public void AsyncBeginSend(UDPPacketBuffer buf)
{
// by now you should you get the idea - no further explanation necessary
rwLock.AcquireReaderLock(-1);
if (!shutdownFlag)
{
try
{
Interlocked.Increment(ref rwOperationCount);
udpSocket.BeginSendTo(
buf.Data,
0,
buf.DataLength,
SocketFlags.None,
buf.RemoteEndPoint,
new AsyncCallback(AsyncEndSend),
buf);
}
catch (SocketException)
{
throw new NotImplementedException();
}
}
rwLock.ReleaseReaderLock();
}
private void AsyncEndSend(IAsyncResult iar)
{
// by now you should you get the idea - no further explanation necessary
rwLock.AcquireReaderLock(-1);
if (!shutdownFlag)
{
UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState;
try
{
int bytesSent = udpSocket.EndSendTo(iar);
// note that in invocation of PacketSent event - we are passing the number
// of bytes sent in a separate parameter, since we can't use buffer.DataLength which
// is the number of bytes to send (or bytes received depending upon whether this
// buffer was part of a send or a receive).
if (PacketSent != null)
PacketSent(this, new UDPPacketEventArgs(buffer, bytesSent));
}
catch (SocketException)
{
throw new NotImplementedException();
}
}
Interlocked.Decrement(ref rwOperationCount);
rwLock.ReleaseReaderLock();
}
}
/// <summary>
/// Base class used for all network-oriented servers.
/// <para>Disposable. All methods are abstract, including Dispose().</para>
/// </summary>
/// <example>
/// This example shows how to inherit from ServerBase class.
/// <code>
/// public class SyncTcp : ServerBase {...}
/// </code>
/// </example>
abstract public class ServerBase : IDisposable
{
/// <summary>
/// Starts the server.
/// </summary>
abstract public void Start();
/// <summary>
/// Stops the server.
/// </summary>
abstract public void Stop();
#region IDisposable Members
/// <summary>
/// Cleans up after server.
/// <para>It usually calls Stop() if server is running.</para>
/// </summary>
public abstract void Dispose();
#endregion
}
}
"Test code" follows.
namespace MyProject1
{
class AsyncUdpTest
{
[Fact]
public void UdpServerInstance()
{
AsyncUdp udp = new AsyncUdp();
Assert.True(udp is AsyncUdp);
udp.Dispose();
}
[Fact]
public void StartStopUdpServer()
{
using (AsyncUdp udp = new AsyncUdp(5000))
{
udp.Start();
Thread.Sleep(5000);
}
}
string udpReceiveMessageSend = "This is a test message";
byte[] udpReceiveData = new byte[4096];
bool udpReceivePacketMatches = false;
[Fact]
public void UdpServerReceive()
{
using (AsyncUdp udp = new AsyncUdp(5000))
{
udp.Start();
udp.PacketReceived += new EventHandler(delegate(object sender, EventArgs e)
{
UDPPacketEventArgs ea = e as UDPPacketEventArgs;
if (this.udpReceiveMessageSend.Equals(ea.buffer.StringContent))
{
udpReceivePacketMatches = true;
}
});
// wait 20 ms for a socket to be bound etc
Thread.Sleep(20);
UdpClient sock = new UdpClient();
IPEndPoint iep = new IPEndPoint(IPAddress.Loopback, 5000);
this.udpReceiveData = Encoding.ASCII.GetBytes(this.udpReceiveMessageSend);
sock.Send(this.udpReceiveData, this.udpReceiveData.Length, iep);
sock.Close();
// wait 20 ms for an event to fire, it should be enough
Thread.Sleep(20);
Assert.True(udpReceivePacketMatches);
}
}
}
}
note: code is c#, testing framework xUnit
A big thanks to everyone who takes time to go through my question and answer it!
Should you test ? Absolutely. You need to engineer your code for testability to make this simple. Your first statement is largely correct. So, some further comments:
Unit testing is largely testing code alone against test data and not reliant on external systems/servers etc. Functional/integration testing then brings in your external servers/databases etc. You can use dependency injection to inject either that real external system reference, or a test (mocked) system implementing the same interface, and thus your code becomes easily testable.
So in the above you would probably want to inject the UDP data source into your receiver. Your data source would implement a particular interface, and a mocked (or simple test) source would provide different packets for testing (e.g. empty, containing valid data, containing invalid data). That would form the basis of your unit test.
Your integration (or functional? I never know what to call it) test would perhaps start up a test UDP data source in the same VM, for each test, and pump data via UDP to your receiver. So now you've tested the basic functionality in the face of different packets via your unit test, and you're testing the actual UDP client/server function via your integration test.
So now you've tested your packet transmission/reception, you can test further parts of your code. Your UDP receiver will plug into another component, and here you can use dependency injection to inject the UDP receiver into your upstream component, or a mocked/test receiver implementing the same interface (and so on).
(Note: given that UDP transmission is unreliable even intra-host you should be prepared to cater for that somehow, or accept that infrequently you'll have problems. But that's a UDP-specific issue).
I notices some design problems in your code, and I think that this problems also interfere with ability to test this code.
I don't really unserstand UDPPacketBuffer class purpose. This class isn't encapsulate anything. It contains read/write Data property and I noticed only one probably useful is StringContent.
If you assume pass through UDP some application level packets, maybe you should create appropriate abstractions for this packets. Also, using UDP you should create something that helps you gather all parts in one (because you can receive parts of your packets in different order).
Also, I don't understand why your UDPPacketBuffer contains IPEndPoint.
That's why you can't test this class, because there no obvious purpose for this class.
It's really hard to test class that sends and receives data over network. But I notices some problems with your AsyncUdp implementation.
2.1 There is no packets delivery guarantee. I mean, who responsible for reliable packet delivery?
2.2 Ther is no thread safe (due to lack of exception safety).
What happens if Start method would be called simultaneously from separate threads?
And, consider following code (From Stop method):
rwLock.AcquireWriterLock(-1);
shutdownFlag = true;
udpSocket.Close();
rwLock.ReleaseWriterLock();
What if updSocket.Close method raise an exception? In this case rwLock whould stay in acquired state.
And in AsyncBeginReceive: what if UDPPacketBuffer ctor throws an exception, or udpSocket.BeginReceiveFrom throws SecurityException or ArgumentOutOfRangeException.
Other functions also not thread safe due to unhandled exception.
In this case you may create some helper class, that can be used in using statemant.
Something like this:
class ReadLockHelper : IDisposable
{
public ReadLockHelper(ReaderWriterLockSlim rwLock)
{
rwLock.AcquireReadLock(-1);
this.rwLock = rwLock;
}
public void Dispose()
{
rwLock.ReleaseReadLock();
}
private ReaderWriterLockSlim rwLock;
}
And than use it in your methods:
using (var l = new ReadLockHelper(rwLock))
{
//all other stuff
}
And finally. You should use ReaderWriterLockSlim instead ReaderWriterLock.
Important note from MSDN:
The .NET Framework has two reader-writer locks, ReaderWriterLockSlim and ReaderWriterLock. ReaderWriterLockSlim is recommended for all new development. ReaderWriterLockSlim is similar to ReaderWriterLock, but it has simplified rules for recursion and for upgrading and downgrading lock state. ReaderWriterLockSlim avoids many cases of potential deadlock. In addition, the performance of ReaderWriterLockSlim is significantly better than ReaderWriterLock.
Related
I need to be able to temporarily disable logging for some scope. In my case there is a background task which periodically attempts to instantiate some device API for every available COM port in the system and sees if it fails. That API writes a lot of information to log in case of failure (exceptions, internal components Dispose calls, etc). As a result, the log is flooded with such unsuccessful attempts errors every second.
I came up with the solution that uses LogContext.PushProperty to identify the supressed log events. However, the following code won't log anything:
internal static class Program
{
public static void Main(String[] args)
{
void StartListeningSomething()
{
Task.Factory.StartNew(() =>
{
while (true)
{
Log.Information("Listening");
Thread.Sleep(500);
}
}, TaskCreationOptions.LongRunning);
}
Log.Logger = new LoggerConfiguration()
.Enrich.WithThreadId()
.Filter.ByExcluding(logEvent => logEvent.Properties.ContainsKey("SuppressLogging"))
.Enrich.FromLogContext()
.WriteTo.Console(new JsonFormatter())
.CreateLogger();
using (LogContext.PushProperty("SuppressLogging", true))
{
StartListeningSomething();
Console.ReadKey(); // Will ignore background thread log messages until key enter
}
// We want to start logging events after exiting using block
// But they won't be logged for listener thread at all
Console.ReadKey();
}
}
All log events inside listener task will be enriched with "SupressLogging" property even after popping it from the scope.
The only workaround I found (except the tedious passing customized ILogger throughout the entire API) consists of the following steps:
Assign some unique value to "SupressLogging" property
Add this value to internal static storage
When exiting scope, remove this value from storage (invalidate)
In Filter section of the logger configuration, check for property being attached and it's value being valid (contained in storage).
The following code uses custom IDisposable token to make it seem like usual PushProperty
internal static class Program
{
public static void Main(String[] args)
{
void StartListeningSomething()
{
Task.Factory.StartNew(() =>
{
while (true)
{
Log.Information("Listening");
Thread.Sleep(500);
}
}, TaskCreationOptions.LongRunning);
}
Log.Logger = new LoggerConfiguration()
.Enrich.WithThreadId()
.Filter.ByExcluding(logEvent => logEvent.IsSuppressed()) // Check if log event marked with supression property
.Enrich.FromLogContext()
.WriteTo.Console(new JsonFormatter())
.CreateLogger();
using (SerilogExtensions.SuppressLogging())
{
StartListeningSomething();
Console.ReadKey(); // Will ignore background thread log messages until some key is entered
}
// Will start logging events after exiting the using block
Console.ReadKey();
}
}
And the actual SerilogExtensions:
/// <summary>
/// Provides helper extensions to Serilog logging.
/// </summary>
public static class SerilogExtensions
{
private const String SuppressLoggingProperty = "SuppressLogging";
private static readonly HashSet<Guid> ActiveSuppressions = new HashSet<Guid>();
/// <summary>
/// Get disposable token to supress logging for context.
/// </summary>
/// <remarks>
/// Pushes "SuppressLogging" property with unique value to SerilogContext.
/// When disposed, disposes Serilog property push token and invalidates stored value so new log messages are no longer
/// supressed.
/// </remarks>
public static IDisposable SuppressLogging()
{
return new SuppressLoggingDisposableToken();
}
/// <summary>
/// Determines whether the given log event suppressed.
/// </summary>
/// <remarks>
/// Also removes "SuppressLogging" property if present.
/// </remarks>
public static Boolean IsSuppressed(this LogEvent logEvent)
{
Boolean containsProperty = logEvent.Properties.TryGetValue(SuppressLoggingProperty, out var val);
if (!containsProperty)
return false;
logEvent.RemovePropertyIfPresent(SuppressLoggingProperty); //No need for that in logs
if (val is ScalarValue scalar && scalar.Value is Guid id)
return ActiveSuppressions.Contains(id);
return false;
}
/// <summary>
/// Disposable wrapper around logging supression property push/pop and value generation/invalidation.
/// </summary>
private class SuppressLoggingDisposableToken : IDisposable
{
private readonly IDisposable _pushPropertyDisposable;
private readonly Guid _guid;
public SuppressLoggingDisposableToken()
{
_guid = Guid.NewGuid();
_pushPropertyDisposable = LogContext.PushProperty(SuppressLoggingProperty, _guid);
ActiveSuppressions.Add(_guid);
}
public void Dispose()
{
ActiveSuppressions.Remove(_guid);
_pushPropertyDisposable.Dispose();
}
}
}
The complete example project can be found on github.
I would like to leave this self-answered question here and also to ask more experienced Serilog users what their opinion of this problem is. May be there is some common approach to logging suppression I didn't find?
I would like to add to ArXen42 answer.
The proposed Hashset to keep track of the the activesuppression keys is not threadsafe and will create issues when using multiple threads.
A solution would be to use the ConcurrentDictionary<T,T2> instead of the HashSet<T> or the solution as stated below without keeping track of GUIDs to supress the logs.
/// Provides helper extensions to Serilog logging.
/// </summary>
public static class SerilogExtensions
{
private const string SuppressLoggingProperty
= "SuppressLogging";
/// <summary>
/// Get disposable token to supress logging for context.
/// </summary>
public static IDisposable SuppressLogging()
{
return LogContext.PushProperty(SuppressLoggingProperty, true);
}
/// <summary>
/// Determines whether the given log event suppressed.
/// </summary>
/// <remarks>
/// Also removes "SuppressLogging" property if present.
/// </remarks>
public static bool IsSuppressed(this LogEvent logEvent)
{
var containsProperty = logEvent.Properties
.TryGetValue(SuppressLoggingProperty, out var val);
if (!containsProperty)
return false;
// remove suppression property from logs
logEvent.RemovePropertyIfPresent(SuppressLoggingProperty);
if (val is ScalarValue scalar && scalar.Value is bool isSuppressed)
return isSuppressed;
return false;
}
}
im trying to use .Net Remoting to get a value of a variable that i use in a thread of an windows service.
TcpChannel tcpChannel = new TcpChannel(9998);
ChannelServices.RegisterChannel(tcpChannel, false);
Type commonInterfaceType = typeof(MyNameSpace.Core.Engine);
RemotingConfiguration.RegisterWellKnownServiceType(commonInterfaceType,
"CopyFilePercentage",
WellKnownObjectMode.SingleCall);
myEngine = Engine.EngineInstance;
myEngine.Start();
But it seams that every time that i use the Client to get that value, a new thread is created returning an empty string.
Any idea why is this happening or am I doing something wrong?
Thanks in advance,
Miguel de Sousa
WellKnownObjectMode.SingleCall creates a new instance of your class for each call. try WellKnownObjectMode.Singleton
EDIT
Maybe you should read about client activated objects. Turn your singleton object to a class factory and return a new instance of a real worker class(ofcourse inheriting from MarshalByRefObject) which will be used by the client.
so your client will be something like this
var worker = client.GetWorkerClass();
worker.GetSomeData();
and you will have one server object per connection (this may not be the correct terminology).
well i just used a Global Variable Class not really what I wanted but does the job.
/// <summary>
/// Contains global variables for project.
/// </summary>
public static class GlobalVar
{
/// <summary>
/// Global variable that is constant.
/// </summary>
public const string GlobalString = "Important Text";
/// <summary>
/// Static value protected by access routine.
/// </summary>
static int _globalValue;
/// <summary>
/// Access routine for global variable.
/// </summary>
public static int GlobalValue
{
get
{
return _globalValue;
}
set
{
_globalValue = value;
}
}
/// <summary>
/// Global static field.
/// </summary>
public static bool GlobalBoolean;
}
Currently I'm doing something like this:
public void StartListening()
{
if (!isListening)
{
Task.Factory.StartNew(ListenForClients);
isListening = true;
}
}
public void StopListening()
{
if (isListening)
{
tcpListener.Stop();
isListening = false;
}
}
Is there not a method or property within TcpListener to determine if a TcpListener has started listening (ie TcpListener.Start() was called)? Can't really access TcpListener.Server because if it hasn't started, it has not been instantiated yet either. Even if I could access it I'm not sure even that contains a Listening property.
Is this really the best way?
The TcpListener actually has a property called Active which does exactly what you want. However, the property is marked protected for some reason so you cannot access it unless you inherit from the TcpListener class.
You can get around this limitation by adding a simple wrapper to your project.
/// <summary>
/// Wrapper around TcpListener that exposes the Active property
/// </summary>
public class TcpListenerEx : TcpListener
{
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Net.Sockets.TcpListener"/> class with the specified local endpoint.
/// </summary>
/// <param name="localEP">An <see cref="T:System.Net.IPEndPoint"/> that represents the local endpoint to which to bind the listener <see cref="T:System.Net.Sockets.Socket"/>. </param><exception cref="T:System.ArgumentNullException"><paramref name="localEP"/> is null. </exception>
public TcpListenerEx(IPEndPoint localEP) : base(localEP)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Net.Sockets.TcpListener"/> class that listens for incoming connection attempts on the specified local IP address and port number.
/// </summary>
/// <param name="localaddr">An <see cref="T:System.Net.IPAddress"/> that represents the local IP address. </param><param name="port">The port on which to listen for incoming connection attempts. </param><exception cref="T:System.ArgumentNullException"><paramref name="localaddr"/> is null. </exception><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="port"/> is not between <see cref="F:System.Net.IPEndPoint.MinPort"/> and <see cref="F:System.Net.IPEndPoint.MaxPort"/>. </exception>
public TcpListenerEx(IPAddress localaddr, int port) : base(localaddr, port)
{
}
public new bool Active
{
get { return base.Active; }
}
}
Which you can use in place of any TcpListener object.
TcpListenerEx tcpListener = new TcpListenerEx(localaddr, port);
You can get this directly from the Socket. A Socket is always created when a TcpListener is instantiated.
if(tcpListener.Server.IsBound)
// The TcpListener has been bound to a port
// and is listening for new TCP connections
I have a NativeActivity derived activity that I wrote that is to use bookmarks as a trigger for a pick branch. Using something I found on MSDN I tried writing this to trigger the branch. The branch contains activities that fire service callbacks to remote clients via send activities. If I set a delay for the trigger, callbacks fire to the clients successfully. If I use my code activity, the pick branch activities don't fire.
public sealed class UpdateListener : NativeActivity<ClientUpdate>
{
[RequiredArgument]
public InArgument<string> BookmarkName { get; set; }
protected override void Execute(NativeActivityContext context)
{
context.CreateBookmark(BookmarkName.Get(context),
new BookmarkCallback(this.OnResumeBookmark));
}
protected override bool CanInduceIdle
{
get { return true; }
}
public void OnResumeBookmark(NativeActivityContext context, Bookmark bookmark, object obj )
{
Result.Set(context, (ClientUpdate)obj);
}
}
So it takes an arg to set the bookmark name for future bookmark references to execute the trigger. OnResumeBoookmark() takes in a ClientUpdate object that is passed by my application that is hosting the workflowapp. The activity is to return the object so the ClientUpdate can be passed to the workflow and have it sent to the remote clients via the send activity in the pick branch. In theory anyways.
For some reason it seems to be correct but feels wrong. I'm not sure if I should write the Activity in a different way to take care of what I need for my WF service.
I think your intentions would be a bit clearer if you created an extension (that implements IWorkflowInstanceExtension) to perform your action here.
For example:
public sealed class AsyncWorkExtension
: IWorkflowInstanceExtension
{
// only one extension per workflow
private WorkflowInstanceProxy _proxy;
private Bookmark _lastBookmark;
/// <summary>
/// Request the extension does some work for an activity
/// during which the activity will idle the workflow
/// </summary>
/// <param name="toResumeMe"></param>
public void DoWork(Bookmark toResumeMe)
{
_lastBookmark = toResumeMe;
// imagine I kick off some async op here
// when complete system calls WorkCompleted below
// NOTE: you CANNOT block here or you block the WF!
}
/// <summary>
/// Called by the system when long-running work is complete
/// </summary>
/// <param name="result"></param>
internal void WorkCompleted(object result)
{
//NOT good practice! example only
//this leaks resources search APM for details
_proxy.BeginResumeBookmark(_lastBookmark, result, null, null);
}
/// <summary>
/// When implemented, returns any additional extensions
/// the implementing class requires.
/// </summary>
/// <returns>
/// A collection of additional workflow extensions.
/// </returns>
IEnumerable<object> IWorkflowInstanceExtension
.GetAdditionalExtensions()
{
return new object[0];
}
/// <summary>
/// Sets the specified target
/// <see cref="WorkflowInstanceProxy"/>.
/// </summary>
/// <param name="instance">The target workflow instance to set.</param>
void IWorkflowInstanceExtension
.SetInstance(WorkflowInstanceProxy instance)
{
_proxy = instance;
}
}
Within the Activity, you'd use this thusly:
var ext = context.GetExtension<AsyncWorkExtension>();
var bookmark = context.CreateBookmark(BookmarkCallback);
ext.DoWork(bookmark);
return;
This way is much more explicit (instead of using the bookmark name to convey meaning to the "outside" world) and is much easier to extend if, say, you require to send out more information than a bookmark name.
Is there something actually resuming the bookmark here? If not the workflow will wait very patiently and nothing will happen.
I have a legacy VB6 component that I've imported into VS using tlbimp.exe to generate my interop assembly. The VB6 component defines an event that allows me to pass messages within VB6.
Public Event Message(ByVal iMsg As Variant, oCancel As Variant)
I would really like to be able to raise this even in my C# program, but its getting imported as an event, not a delegate or something else useful. So, I can only listen, but never fire. Does anyone know how to fire an event contained within VB6? The C# event looks like
[TypeLibType(16)]
[ComVisible(false)]
public interface __MyObj_Event
{
event __MyObj_MessageEventHandler Message;
}
I unfortunately cannot change the VB6 code. Thanks.
Actually, hope is not lost yet. It is possible to raise an event on a COM object from outside of the object's class. This functionality is actually provided by COM itself, although in an indirect manner.
In COM, events work on a publish/subscribe model. A COM object that has events (the "event source") publishes events, and one or more other COM objects subscribe to the event by attaching an event handler to the source object (the handlers are called "event sinks"). Normally, the source object raises an event by simply looping through all the event sinks and calling the appropriate handler method.
So how does this help you? It just so happens that COM lets you query an event source for a list of all the event sink objects currently subscribed to the source object's events. Once you have a list of event sink objects, you can simulate raising an event by invoking each of the sink object's event handlers.
Note: I'm over-simplifying the details and being liberal with some of the terminology, but that's the short (and somewhat politically incorrect) version of how events work in COM.
You can take advantage of this knowledge to raise events on a COM object from external code. In fact, it is possible to do all of this in C#, with the help of the COM interop support in the System.Runtime.Interop and System.Runtime.Interop.ComTypes namespaces.
EDIT
I wrote a utility class that will allow you to raise events on a COM object from .NET. It's pretty easy to use. Here is an example using the event interface from your question:
MyObj legacyComObject = new MyObj();
// The following code assumes other COM objects have already subscribed to the
// MyObj class's Message event at this point.
//
// NOTE: VB6 objects have two hidden interfaces for classes that raise events:
//
// _MyObj (with one underscore): The default interface.
// __MyObj (with two underscores): The event interface.
//
// We want the second interface, because it gives us a delegate
// that we can use to raise the event.
// The ComEventUtils.GetEventSinks<T> method is a convenience method
// that returns all the objects listening to events from the legacy COM object.
// set up the params for the event
string messageData = "Hello, world!";
bool cancel = false;
// raise the event by invoking the event delegate for each connected object...
foreach(__MyObj sink in ComEventUtils.GetEventSinks<__MyObj>(legacyComObject))
{
// raise the event via the event delegate
sink.Message(messageData, ref cancel);
if(cancel == true)
{
// do cancel processing (just an example)
break;
}
}
Below is the code for the ComEventUtils class (as well as helper class, SafeIntPtr, because I'm paranoid and wanted a nice way to deal with the IntPtrS needed by the COM-related code):
Disclaimer: I haven't thoroughly tested the code below. The code performs manual memory management in a few places, and therefore there is the possibility that it could introduce memory leaks into your code. Also, I didn't add error-handling to the code, because this is only an example. Use with care.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using COM = System.Runtime.InteropServices.ComTypes;
namespace YourNamespaceHere
{
/// <summary>
/// A utility class for dealing with COM events.
/// Needs error-handling and could potentially be refactored
/// into a regular class. Also, I haven't extensively tested this code;
/// there may be a memory leak somewhere due to the rather
/// low-level stuff going on in the class, but I think I covered everything.
/// </summary>
public static class ComEventUtils
{
/// <summary>
/// Get a list of all objects implementing an event sink interface T
/// that are listening for events on a specified COM object.
/// </summary>
/// <typeparam name="T">The event sink interface.</typeparam>
/// <param name="comObject">The COM object whose event sinks you want to retrieve.</param>
/// <returns>A List of objects that implement the given event sink interface and which
/// are actively listening for events from the specified COM object.</returns>
public static List<T> GetEventSinks<T>(object comObject)
{
List<T> sinks = new List<T>();
List<COM.IConnectionPoint> connectionPoints = GetConnectionPoints(comObject);
// Loop through the source object's connection points,
// find the objects that are listening for events at each connection point,
// and add the objects we are interested in to the list.
foreach(COM.IConnectionPoint connectionPoint in connectionPoints)
{
List<COM.CONNECTDATA> connections = GetConnectionData(connectionPoint);
foreach (COM.CONNECTDATA connection in connections)
{
object candidate = connection.pUnk;
// I tried to avoid relying on try/catch for this
// part, but candidate.GetType().GetInterfaces() kept
// returning an empty array.
try
{
sinks.Add((T)candidate);
}
catch { }
}
// Need to release the interface pointer in each CONNECTDATA instance
// because GetConnectionData implicitly AddRef's it.
foreach (COM.CONNECTDATA connection in connections)
{
Marshal.ReleaseComObject(connection.pUnk);
}
}
return sinks;
}
/// <summary>
/// Get all the event connection points for a given COM object.
/// </summary>
/// <param name="comObject">A COM object that raises events.</param>
/// <returns>A List of IConnectionPoint instances for the COM object.</returns>
private static List<COM.IConnectionPoint> GetConnectionPoints(object comObject)
{
COM.IConnectionPointContainer connectionPointContainer = (COM.IConnectionPointContainer)comObject;
COM.IEnumConnectionPoints enumConnectionPoints;
COM.IConnectionPoint[] oneConnectionPoint = new COM.IConnectionPoint[1];
List<COM.IConnectionPoint> connectionPoints = new List<COM.IConnectionPoint>();
connectionPointContainer.EnumConnectionPoints(out enumConnectionPoints);
enumConnectionPoints.Reset();
int fetchCount = 0;
SafeIntPtr pFetchCount = new SafeIntPtr();
do
{
if (0 != enumConnectionPoints.Next(1, oneConnectionPoint, pFetchCount.ToIntPtr()))
{
break;
}
fetchCount = pFetchCount.Value;
if (fetchCount > 0)
connectionPoints.Add(oneConnectionPoint[0]);
} while (fetchCount > 0);
pFetchCount.Dispose();
return connectionPoints;
}
/// <summary>
/// Returns a list of CONNECTDATA instances representing the current
/// event sink connections to the given IConnectionPoint.
/// </summary>
/// <param name="connectionPoint">The IConnectionPoint to return connection data for.</param>
/// <returns>A List of CONNECTDATA instances representing all the current event sink connections to the
/// given connection point.</returns>
private static List<COM.CONNECTDATA> GetConnectionData(COM.IConnectionPoint connectionPoint)
{
COM.IEnumConnections enumConnections;
COM.CONNECTDATA[] oneConnectData = new COM.CONNECTDATA[1];
List<COM.CONNECTDATA> connectDataObjects = new List<COM.CONNECTDATA>();
connectionPoint.EnumConnections(out enumConnections);
enumConnections.Reset();
int fetchCount = 0;
SafeIntPtr pFetchCount = new SafeIntPtr();
do
{
if (0 != enumConnections.Next(1, oneConnectData, pFetchCount.ToIntPtr()))
{
break;
}
fetchCount = pFetchCount.Value;
if (fetchCount > 0)
connectDataObjects.Add(oneConnectData[0]);
} while (fetchCount > 0);
pFetchCount.Dispose();
return connectDataObjects;
}
} //end class ComEventUtils
/// <summary>
/// A simple wrapper class around an IntPtr that
/// manages its own memory.
/// </summary>
public class SafeIntPtr : IDisposable
{
private bool _disposed = false;
private IntPtr _pInt = IntPtr.Zero;
/// <summary>
/// Allocates storage for an int and assigns it to this pointer.
/// The pointed-to value defaults to 0.
/// </summary>
public SafeIntPtr()
: this(0)
{
//
}
/// <summary>
/// Allocates storage for an int, assigns it to this pointer,
/// and initializes the pointed-to memory to known value.
/// <param name="value">The value this that this <tt>SafeIntPtr</tt> points to initially.</param>
/// </summary>
public SafeIntPtr(int value)
{
_pInt = Marshal.AllocHGlobal(sizeof(int));
this.Value = value;
}
/// <summary>
/// Gets or sets the value this pointer is pointing to.
/// </summary>
public int Value
{
get
{
if (_disposed)
throw new InvalidOperationException("This pointer has been disposed.");
return Marshal.ReadInt32(_pInt);
}
set
{
if (_disposed)
throw new InvalidOperationException("This pointer has been disposed.");
Marshal.WriteInt32(_pInt, Value);
}
}
/// <summary>
/// Returns an IntPtr representation of this SafeIntPtr.
/// </summary>
/// <returns></returns>
public IntPtr ToIntPtr()
{
return _pInt;
}
/// <summary>
/// Deallocates the memory for this pointer.
/// </summary>
public void Dispose()
{
if (!_disposed)
{
Marshal.FreeHGlobal(_pInt);
_disposed = true;
}
}
~SafeIntPtr()
{
if (!_disposed)
Dispose();
}
} //end class SafeIntPtr
} //end namespace YourNamespaceHere
In VB6 the event can only be raised from within the class (or Form as the case may be) declaring the Event. To force a event to be raised in VB6 you need to expose a method on the class to do this. If you don't have the source code the you are out of luck.
From the documentation
RaiseEvent eventname [(argumentlist)]
The required eventname is the name of
an event declared within the module
and follows Basic variable naming
conventions.
For example
Option Explicit
Private FText As String
Public Event OnChange(ByVal Text As String)
'This exposes the raising the event
Private Sub Change(ByVal Text As String)
RaiseEvent OnChange(Text)
End Sub
Public Property Get Text() As String
Text = FText
End Property
Public Property Let Text(ByVal Value As String)
FText = Value
Call Change(Value)
End Property
Sorry to be the bearer of bad news.