Background Operations in DicomObjects
DicomObjects.NET version
All functions in .NET (e.g SendImages) are synchronous methods and do not return until the operation has completed. So if for example you wish to send asynchronously, then it is necessary to create your own thread using the native .NET threading methods. Whilst this might seem a “move backwards” from the COM version, remember that the COM threading in DicomObjects was only to allow you to do these things despite the lack of native threading support in VB6 etc., so the fact that the .NET allows you to do them yourself, complete with all the full threading facilities that .NET offers is in fact a strength.
Events
All .NET version events fires on its own thread, so you don’t have to worry about concurrent requests coming at the same time as each will be handled by the same event on a different thread. Here is some of the DicomServer’s events
server = new DicomServer();
server.AssociationRequest += Server_AssociationRequest;
server.QueryReceived += Server_QueryReceived;
server.InstanceReceived += Server_InstanceReceived;
server.NormalisedReceived += Server_NormalisedReceived;
private void Server_NormalisedReceived(object Sender, DicomObjects.EventArguments.NormalisedReceivedArgs e)
{
// your code here to handle normalised request, like Printing
}
private void Server_InstanceReceived(object Sender, DicomObjects.EventArguments.InstanceReceivedArgs e)
{
// your code here to handle received Image (e.Instance)
}
private void Server_QueryReceived(object Sender, DicomObjects.EventArguments.QueryReceivedArgs e)
{
// your code here to handle incoming DICOM queries
// such as C-FIND/C-GET/C-MOVE and MWL queries
}
private void Server_AssociationRequest(object Sender, DicomObjects.EventArguments.AssociationRequestArgs e)
{
// your code here to handle incoming DICOM connection request
}
DicomObjects.COM version
It is often useful to perform operation in the background, in order to keep a responsive user interface, and this is possible in DicomObjects using an asynchronous DicomConnection object.
- Note: Asynchronous in this sense refers to how the object interacts with your application, and has no connection to DICOM Asynchronous communications (which no-one ever uses)
In order to manage asynchronous operations, they must be performed using a DicomConnection object, which must be created using the New(“DicomConnection”) method of a DicomServer, and not using New DicomConnection. The reason for this is simply to link to a static object to which the ActionComplete can be attached!
The capabilities of a DicomConnection object in DicomObjects are written in such a way that it is possible to write a full multi-threaded application from a single threaded environment. In order to achieve this however, it is necessary to follow some rules. The important point to remember is that a DicomConnection, even when in asynchronous mode can only have a single outstanding operation, so any further operations will cause the calling thread to wait until the previous operations have completed. This is best shown by an example, where Connection is a DicomConnection created as above
Connection.SendImages("CT.dcm")
Connection.SendStatus(0)
When this code is executed, the SendImages method will return immediately, and transfer of the images on the established association will commence. However, the SendStatus method cannot return until the operations initiated by the previous method have completed, and this may be a long time, during which the application would be unresponsive. To avoid this issue, each operation should be initiated only once the previous one has completed, and the best way of doing this is to use the ActionComplete event. Using this approach, only the very first operation is triggered from with the original initiating event (such as the user pressing the “send” button), and all other operations are called from the ActionComplete event. This is shown in the following vb6 code:
Main initiating routine
dim server as New DicomServer
dim cn as DicomConnection
Set cn = server.New("DicomConnection")
cn.Mode = doAsync
cn.SetDestination(ip, port, callingAET, calledAET)
'no operations on the Connection object here
The Server’s ActionComplete event:
Private Sub server_ActionComplete(ByVal Connection As DicomObjects8.DicomConnection, ByVal Action As String,
ByVal Tag As Variant, ByVal Success As Boolean, ByVal ErrorMessage As String)
If Action = "SetDestination" Then
Dim imgs As New DicomImages
imgs.ReadFile "CT.dcm"
Connection.SendImages imgs
End If
If Action = "SendImages" Then
Connection.SendStatus(0)
End If
End Sub
For the best asynchronous behaviour, SendImages should where possible be passed a string or array of strings, being the filenames of the files to be sent, as this causes the files to be loaded on the background thread, further reducing delays on the main thread.
Although not used in the above simplistic example, there will often be occasions where the routine initiating an asynchronous method needs to pass some data or context information on to the ActionComplete event, to ensure that subsequent operations are appropriate. The Tag property is designed for this purpose. Note that the value passed to the ActionComplete event is the value at the time that the initiating method is called, so Tag should be set before making any such calls.