#include "ltdic.h"
L_INT LDicomNet::Initialize(pszPath, nMode, pCtxCreate)
L_TCHAR *pszPath; |
character string |
L_INT32 nMode; |
initialization mode |
pL_SSL_CTX_CREATE *pCtxCreate; |
pointer to a structure |
This function is to be used in conjunction with the LDicomNet::LDicomNet(*pszPath, nMode, bReserved) constructor in order to change security options from the defaults. This function is available in the Medical Imaging Suite toolkits.
Parameter | Description | |
pszPath | Character string that contains the location of the temporary files. This should be the same string that was used in the LDicomNet constructor. | |
nMode | Flag that indicates the security mode to use when initializing the network structure. This should be the same flag that was used in the LDicomNet constructor. Possible values are: | |
Value | Meaning | |
DICOM_SECURE_NONE | No security mode. | |
DICOM_SECURE_ISCL | Integrated Secure Communication Layer security mode. | |
DICOM_SECURE_TLS | Transport Layer Security security mode. | |
pCtxCreate | Pointer to the L_SSL_CTX_CREATE structure that is used to modify the security defaults. This structure is used only if the nMode flag is DICOM_SECURE_TLS. Pass NULL to get the default values. |
SUCCESS |
The function was successful. |
> 0 |
An error occurred. Refer to Return Codes.. |
This function is to be used in conjunction with the LDicomNet::LDicomNet(*pszPath, nMode, bReserved) constructor when changing security options from the defaults. Note that when using the LDicomNet::LDicomNet(*pszPath, nMode, bReserved) version of the constructor, in addition to calling LDicomNet::Startup it is also necessary to call LDicomNet::Initialize in order to prepare the LDicomNet object for use. Use the pCtxCreate parameter when the nMode flag is set to DICOM_SECURE_TLS.
Note that the following uses of the LDicomNet constructors are functionally equivalent:
1.
LDicomNet *pNet = new LDicomNet(pszPath, nMode);
2.
LDicomNet *pNet = new LDicomNet(pszPath, nMode, 0);
If (pNet)
pNet->Initialize(pszPath, nMode, NULL);
Required DLLs and Libraries
LTDIC For a listing of the exact DLLs and Libraries needed, based on the toolkit version, refer to Files To Be Included With Your Application |
Win32, x64
Functions: |
|
Topics: |
|
|
|
|
|
|
|
|
This is a basic, but complete example that shows a DICOM client sending a C-Echo-REQ to a server, and the server processing the request using TLS Security.
#define MAKE_IMAGE_PATH(pFileName) TEXT("C:\\Users\\Public\\Documents\\LEADTOOLS Images\\")pFileName
namespace LDicomNet_Initialize_Namespace
{
#ifndef CA_CERT_NAME
#define CA_CERT_NAME MAKE_IMAGE_PATH(TEXT("CA.pem"))
#endif
#ifndef SERVER_CERT_NAME
#define SERVER_CERT_NAME MAKE_IMAGE_PATH(TEXT("Server.pem"))
#endif
#ifndef CLIENT_CERT_NAME
#define CLIENT_CERT_NAME MAKE_IMAGE_PATH(TEXT("Client.pem"))
#endif
// Logs a message
// This implementation logs to the console, and the debug window
L_VOID LogMessage(TCHAR *szMsg)
{
wprintf(TEXT("\n"));
wprintf(szMsg);
OutputDebugStringW(TEXT("\n"));
OutputDebugStringW(szMsg);
}
L_VOID LogMessage(TCHAR *s, L_INT n)
{
TCHAR szLog[200] = {0};
wsprintf(szLog, TEXT("%s [%d]"), s, n);
LogMessage(szLog);
}
L_INT PrivateKeyPassword(L_TCHAR *pszPassword, L_INT nSize, L_INT nFlag)
{
UNREFERENCED_PARAMETER(nFlag);
LPCTSTR pszMyPassword= TEXT("test");
L_INT nRet = 0;
if ((L_INT)_tcslen(pszMyPassword) < nSize)
{
_tcsncpy_s(pszPassword, nSize, pszMyPassword, nSize);
nRet = (L_INT)_tcslen(pszMyPassword);
}
return nRet;
}
L_VOID SetupTlsContext(LDicomNet *pNet, L_TCHAR *pszCertName)
{
if (pNet != NULL)
{
L_SSL_CTX_CREATE ctxCreate;
memset(&ctxCreate, 0, sizeof(L_SSL_CTX_CREATE));
ctxCreate.uStructSize = sizeof(L_SSL_CTX_CREATE);
ctxCreate.uFlags = FLAG_SSL_CTX_CREATE_METHOD_TYPE | FLAG_SSL_CTX_CREATE_VERIFY_MODE |
FLAG_SSL_CTX_CREATE_VERIFY_DEPTH | FLAG_SSL_CTX_CREATE_OPTIONS | FLAG_SSL_CTX_CREATE_CAFILE;
ctxCreate.nMethodTypeSSL= TYPE_SSLV23_METHOD;
ctxCreate.pszCAfile = CA_CERT_NAME;
ctxCreate.uVerifyMode = L_SSL_VERIFY_PEER | L_SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
ctxCreate.nVerifyDepth = 2;
ctxCreate.nOptions = L_SSL_OP_NO_SSLv2|L_SSL_OP_ALL;
ctxCreate.nReserved1 = 0;
ctxCreate.nReserved2 = 0;
L_INT nRet = pNet->Initialize(NULL, DICOM_SECURE_TLS, &ctxCreate);
if (nRet == DICOM_SUCCESS)
{
L_TCHAR szMsg[200] = {0};
// Assign the server the certificate
// Note that SERVER_CERT_NAME contains both the password and an encrypted private key
// When loading the private key, the OnPrivateKeyPassword virtual function is called
// so that the encryption password "test" can be supplied
nRet = pNet->SetServerCertificateTLS (pszCertName, L_TLS_FILETYPE_PEM, NULL);
if (nRet == DICOM_SUCCESS)
wsprintf(szMsg, TEXT("%s loaded successfully"), pszCertName);
else
wsprintf(szMsg, TEXT("%s could not be loaded successfully -- error[%d]"), pszCertName, nRet);
LogMessage(szMsg);
}
}
}
L_VOID DumpTlsInformation(LDicomNet *pNet)
{
L_CIPHERSUITE ciphersuite;
ciphersuite = pNet->GetCiphersuiteTLS();
switch(ciphersuite)
{
case TLS_DHE_RSA_WITH_DES_CBC_SHA:
LogMessage( TEXT(" Secure connected, cipher is TLS_DHE_RSA_WITH_DES_CBC_SHA"));
break;
case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
LogMessage( TEXT(" Secure connected, cipher is TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA"));
break;
case TLS_DHE_RSA_AES256_SHA:
LogMessage( TEXT(" Secure connected, cipher is TLS_DHE_RSA_AES256_SHA"));
break;
}
L_UINT32 nEncryptionAlgorithm = pNet->GetEncryptionAlgorithmTLS(ciphersuite);
switch(nEncryptionAlgorithm)
{
case L_CRYPT_NONE:
LogMessage(TEXT(" Encryption Algorithm: L_CRYPT_NONE"));
break;
case L_CRYPT_DES:
LogMessage(TEXT(" Encryption Algorithm: L_CRYPT_DES"));
break;
case L_CRYPT_3DES:
LogMessage(TEXT(" Encryption Algorithm: L_CRYPT_3DES"));
break;
case L_CRYPT_RC4:
LogMessage(TEXT(" Encryption Algorithm: L_CRYPT_RC4"));
break;
case L_CRYPT_RC2:
LogMessage(TEXT(" Encryption Algorithm: L_CRYPT_RC2"));
break;
case L_CRYPT_IDEA:
LogMessage(TEXT(" Encryption Algorithm: L_CRYPT_IDEA"));
break;
case L_CRYPT_FORTEZZA:
LogMessage(TEXT(" Encryption Algorithm: L_CRYPT_FORTEZZA"));
break;
case L_CRYPT_AES:
LogMessage(TEXT(" Encryption Algorithm: L_CRYPT_AES"));
break;
}
L_UINT32 auth = pNet->GetAuthenticationAlgorithmTLS(ciphersuite);
switch(auth)
{
case L_MUTUALAUTH_NONE:
LogMessage(TEXT(" Authentication Algorithm: L_MUTUALAUTH_NONE"));
break;
case L_MUTUALAUTH_RSA:
LogMessage(TEXT(" Authentication Algorithm: L_MUTUALAUTH_RSA"));
break;
case L_MUTUALAUTH_DSS:
LogMessage(TEXT(" Authentication Algorithm: L_MUTUALAUTH_DSS"));
break;
case L_MUTUALAUTH_DH:
LogMessage(TEXT(" Authentication Algorithm: L_MUTUALAUTH_DH"));
break;
}
L_UINT32 integrity = pNet->GetIntegrityAlgorithmTLS(ciphersuite);
switch(integrity)
{
case L_MAC_NONE:
LogMessage(TEXT(" Message Authentication code type: L_MAC_NONE"));
break;
case L_MAC_SHA1:
LogMessage(TEXT(" Message Authentication code type: L_MAC_SHA1"));
break;
case L_MAC_MD5:
LogMessage(TEXT(" Message Authentication code type: L_MAC_MD5"));
break;
}
L_UINT32 keyExchange = pNet->GetKeyExchangeAlgorithmTLS(ciphersuite);
switch(keyExchange)
{
case L_KEYEXCHANGE_NONE:
LogMessage(TEXT(" Key Exchange Algorithm: L_KEYEXCHANGE_NONE"));
break;
case L_KEYEXCHANGE_RSA_SIGNED_DHE:
LogMessage(TEXT(" Key Exchange Algorithm: L_KEYEXCHANGE_RSA_SIGNED_DHE"));
break;
case L_KEYEXCHANGE_RSA:
LogMessage(TEXT(" Key Exchange Algorithm: L_KEYEXCHANGE_RSA"));
break;
case L_KEYEXCHANGE_DH:
LogMessage(TEXT(" Key Exchange Algorithm: L_KEYEXCHANGE_DH"));
break;
case L_KEYEXCHANGE_DH_DSS:
LogMessage(TEXT(" Key Exchange Algorithm: L_KEYEXCHANGE_DH_DSS"));
break;
case L_KEYEXCHANGE_FORTEZZA:
LogMessage(TEXT(" Key Exchange Algorithm: L_KEYEXCHANGE_FORTEZZA"));
break;
}
L_UINT32 uKeyLength = pNet->GetEncryptKeyLengthTLS(ciphersuite);
LogMessage(TEXT(" Encrypt Key Length"), uKeyLength);
L_UINT32 uMutualAuthKeyLength = pNet->GetMutualAuthKeyLengthTLS(ciphersuite);
LogMessage(TEXT(" Mutual authentication key length"), uMutualAuthKeyLength);
}
// *******************************************************************************************
// Client Class
//
// Class that is used to connect to the server
// *******************************************************************************************
class CMyClient : public LDicomNet
{
public:
CMyClient(L_INT32 nMode): LDicomNet(NULL, nMode)
{
m_waitEvent = CreateEvent( NULL, TRUE, TRUE, TEXT("ClientEvent"));
ResetEvent(m_waitEvent);
this->SetCipherToIndexTLS(0, TLS_DHE_RSA_WITH_DES_CBC_SHA);
L_CIPHERSUITE ciphersuite;
ciphersuite = this->GetCipherFromIndexTLS(0);
assert(ciphersuite == TLS_DHE_RSA_WITH_DES_CBC_SHA);
this->SetClientCertificateTLS( MAKE_IMAGE_PATH(TEXT("Client.pem")), L_TLS_FILETYPE_PEM, NULL);
}
~CMyClient(void)
{
CloseHandle(m_waitEvent);
}
// Client
L_VOID OnConnect (L_INT nError);
L_VOID OnSecureLinkReady (L_UINT32 nError);
L_VOID OnReceiveAssociateAccept (LDicomAssociate *pPDU);
L_VOID OnReceiveReleaseResponse ();
L_VOID OnReceiveCEchoResponse (L_UCHAR nPresentationID, L_UINT16 nMessageID, L_TCHAR *pszClass, L_UINT16 nStatus);
L_INT OnPrivateKeyPassword (L_TCHAR *pszPassword, L_INT nSize, L_INT nFlag);
L_BOOL Wait(DWORD timeout = 5000);
private:
HANDLE m_waitEvent;
};
// Continues dispatching messages until hEvent is signalled, our timeout
// Returns TRUE if hEvent is signalled
// Returns FALSE if timeout
L_BOOL MessageLoop (
HANDLE hEvent, // handles that need to be waited on
DWORD timeout // timeout in milliseconds
)
{
DWORD dwStart = GetTickCount();
MSG msg = {0};
volatile L_BOOL bRunForever = TRUE;
while (bRunForever)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0)
{
ResetEvent(hEvent);
return TRUE;
}
DWORD dwCurrent = GetTickCount();
if ((dwCurrent - dwStart) > timeout)
{
return FALSE;
}
}
return TRUE;
}
L_VOID CMyClient::OnConnect(L_INT nError)
{
L_TCHAR szMsg[200] = {0};
wsprintf(szMsg, TEXT("CMyClient::OnConnect: nError[%d]"), nError);
LogMessage(szMsg);
}
L_VOID CMyClient::OnSecureLinkReady(L_UINT32 nError)
{
UNREFERENCED_PARAMETER(nError);
SetEvent(m_waitEvent);
L_UINT32 uSecureMode = this->GetSecureMode();
switch(uSecureMode)
{
case DICOM_SECURE_NONE:
LogMessage(TEXT("CMyClient::OnSecureLinkReady: DICOM_SECURE_NONE"));
break;
case DICOM_SECURE_ISCL:
LogMessage(TEXT("CMyClient::OnSecureLinkReady: DICOM_SECURE_ISCL"));
break;
case DICOM_SECURE_TLS:
{
LogMessage(TEXT("CMyClient::OnSecureLinkReady: DICOM_SECURE_TLS"));
if (nError == DICOM_SUCCESS)
{
DumpTlsInformation(this);
}
else
{
L_TCHAR szMsg[200] = {0};
wsprintf(szMsg, TEXT( "ClientTLS: Secure link error %d"), nError);
LogMessage(szMsg);
}
}
break;
}
}
L_VOID CMyClient::OnReceiveAssociateAccept (LDicomAssociate *pPDU)
{
UNREFERENCED_PARAMETER(pPDU);
SetEvent(m_waitEvent);
LogMessage(TEXT("CMyClient::OnReceiveAssociateAccept"));
}
L_VOID CMyClient::OnReceiveCEchoResponse(L_UCHAR nPresentationID, L_UINT16 nMessageID, L_TCHAR *pszClass, L_UINT16 nStatus)
{
SetEvent(m_waitEvent);
L_TCHAR szMsg[200] = {0};
if (pszClass == NULL)
{
pszClass = TEXT("");
}
wsprintf(szMsg, TEXT("CMyClient::OnReceiveCEchoResponse: \n\tnPresentationID[%d], \n\tnMessageID[%d], \n\tpszClass[%s], \n\tnStatus[%d]"), nPresentationID, nMessageID, pszClass, nStatus);
LogMessage(szMsg);
}
L_INT CMyClient::OnPrivateKeyPassword(L_TCHAR *pszPassword, L_INT nSize, L_INT nFlag)
{
return PrivateKeyPassword(pszPassword, nSize, nFlag);
}
L_VOID CMyClient::OnReceiveReleaseResponse()
{
SetEvent(m_waitEvent);
LogMessage(TEXT("CMyClient::OnReceiveReleaseResponse"));
}
L_BOOL CMyClient::Wait(DWORD timeout)
{
L_BOOL bRet = MessageLoop(m_waitEvent, timeout);
return bRet;
}
// *******************************************************************************************
// Server Connection Class
//
// When a client connects, CMyServer creates a new instance of the CMyServerConnection class
// and accepts the connection.
// *******************************************************************************************
class CMyServerConnection : public LDicomNet
{
public:
CMyServerConnection(L_INT32 nMode): LDicomNet(NULL, nMode)
{
}
~CMyServerConnection(void)
{
}
// Server
L_VOID OnReceiveAssociateRequest (LDicomAssociate *pPDU);
L_VOID OnReceiveAssociateReject (L_UCHAR nResult, L_UCHAR nSource, L_UCHAR nReason);
L_VOID OnReceiveCEchoRequest (L_UCHAR nPresentationID, L_UINT16 nMessageID, L_TCHAR *pszClass);
L_VOID OnReceiveReleaseRequest ();
L_INT OnPrivateKeyPassword (L_TCHAR *pszPassword, L_INT nSize, L_INT nFlag);
};
#define SIZEINWORD(p) sizeof(p)/sizeof(L_TCHAR)
L_VOID CMyServerConnection::OnReceiveAssociateRequest(LDicomAssociate *pPDU)
{
LogMessage(TEXT("\tCMyServerConnection::OnReceiveAssociateRequest"));
//check the version, if not 1, reject it
if (pPDU->GetVersion() != 1)
{
LogMessage(TEXT("\tCMyServerConnection::SendAssociateReject"));
SendAssociateReject(
PDU_REJECT_RESULT_PERMANENT,
PDU_REJECT_SOURCE_USER,
PDU_REJECT_REASON_UNKNOWN
);
}
else
{
LDicomAssociate DicomAssociate(FALSE);
L_TCHAR clientAE[20] = {0};
pPDU->GetCalling(clientAE, 20);
if (lstrcmp(clientAE, TEXT("LEAD_CLIENT")) != 0)
{
LogMessage(TEXT("\tCMyServerConnection::SendAssociateReject"));
SendAssociateReject(
PDU_REJECT_RESULT_PERMANENT,
PDU_REJECT_SOURCE_USER,
PDU_REJECT_REASON_UNKNOWN
);
return;
}
//Copy presentation objects from received
//Reply that we only support the first Transfer Syntax from the received hPDU
L_TCHAR szTransfer[PDU_MAX_UID_SIZE+1] = {0};
L_TCHAR szAbstract[PDU_MAX_UID_SIZE+1] = {0};
L_INT iPresentationCount = pPDU->GetPresentationCount();
for (L_UCHAR i = 0; i<iPresentationCount; i++)
{
L_UCHAR nId = pPDU->GetPresentation(i);
pPDU->GetTransfer(nId, 0, szTransfer, PDU_MAX_UID_SIZE+1);
L_UCHAR nResult = PDU_ACCEPT_RESULT_SUCCESS;
pPDU->GetAbstract(nId, szAbstract, PDU_MAX_UID_SIZE+1);
DicomAssociate.AddPresentation( nId, nResult, szAbstract);
DicomAssociate.AddTransfer( nId, szTransfer);
}
LogMessage(TEXT("\tCMyServerConnection::SendAssociateAccept"));
SendAssociateAccept(&DicomAssociate);
}
}
L_VOID CMyServerConnection::OnReceiveAssociateReject(L_UCHAR nResult, L_UCHAR nSource, L_UCHAR nReason)
{
L_TCHAR szMsg[200] = {0};
wsprintf(szMsg, TEXT("\tOnReceiveAssociateReject\nResult[%d]\nSource[%d]\nReason[%d]"), nResult, nSource, nReason);
LogMessage(szMsg);
}
L_VOID CMyServerConnection::OnReceiveCEchoRequest(L_UCHAR nPresentationID, L_UINT16 nMessageID, L_TCHAR *pszClass)
{
LogMessage(TEXT("\tCMyServerConnection::OnReceiveCEchoRequest"));
LogMessage(TEXT("\tCMyServerConnection::SendCEchoResponse"));
SendCEchoResponse(nPresentationID, nMessageID, pszClass, COMMAND_STATUS_SUCCESS);
}
L_VOID CMyServerConnection::OnReceiveReleaseRequest()
{
LogMessage(TEXT("\tCMyServerConnection::OnReceiveReleaseRequest"));
LogMessage(TEXT("\tCMyServerConnection::SendReleaseResponse"));
SendReleaseResponse();
}
L_INT CMyServerConnection::OnPrivateKeyPassword(L_TCHAR *pszPassword, L_INT nSize, L_INT nFlag)
{
return PrivateKeyPassword(pszPassword, nSize, nFlag);
}
// *******************************************************************************************
// Server Class
//
// Listens for connections
// When a client connects, this class creates a CMyServerConnection and accepts the connection
// *******************************************************************************************
class CMyServer : public LDicomNet
{
public:
CMyServer(L_INT32 nMode): LDicomNet(NULL, nMode)
{
m_pServerConnection = NULL;
}
~CMyServer(void)
{
if (m_pServerConnection != NULL)
{
delete m_pServerConnection;
}
}
L_VOID OnAccept (L_INT nError);
L_VOID OnClose (L_INT nError, LDicomNet *pServerConnection);
L_INT OnPrivateKeyPassword (L_TCHAR *pszPassword, L_INT nSize, L_INT nFlag);
CMyServerConnection *m_pServerConnection;
};
L_VOID CMyServer::OnAccept(L_INT nError)
{
LogMessage(TEXT("\tCMyServer::OnAccept"));
if (nError != DICOM_SUCCESS)
{
return;
}
if (m_pServerConnection != NULL)
{
delete m_pServerConnection;
m_pServerConnection = NULL;
}
m_pServerConnection = new CMyServerConnection(DICOM_SECURE_TLS);
if (m_pServerConnection == NULL)
{
return;
}
SetupTlsContext(m_pServerConnection, SERVER_CERT_NAME);
nError = LDicomNet::Accept(m_pServerConnection);
if (nError != DICOM_SUCCESS)
{
delete m_pServerConnection;
return;
}
}
L_VOID CMyServer::OnClose(L_INT nError, LDicomNet *pServerConnection)
{
UNREFERENCED_PARAMETER(nError);
LogMessage(TEXT("\tCMyServer::OnClose"));
if (m_pServerConnection == pServerConnection)
{
m_pServerConnection = NULL;
}
delete (CMyServerConnection *)pServerConnection;
}
L_INT CMyServer::OnPrivateKeyPassword(L_TCHAR *pszPassword, L_INT nSize, L_INT nFlag)
{
return PrivateKeyPassword(pszPassword, nSize, nFlag);
}
// *******************************************************************************************
// Sample starts here
// *******************************************************************************************
#define WaitForProcessing() \
{ \
if (!client.Wait()) \
{ \
LogMessage(TEXT("Timeout: client.Connect")); \
nRet = DICOM_ERROR_NET_TIME_OUT; \
goto Cleanup; \
} \
}
L_INT LDicomNet_InitializeExample()
{
LogMessage(TEXT("\n\n *** Initialize ***"));
L_TCHAR *pszServerAddress = TEXT("127.0.0.1");
L_UINT uServerPort = 105;
L_INT nRet = DICOM_SUCCESS;
LDicomNet::StartUp();
CMyClient client(DICOM_SECURE_TLS);
CMyServer server(DICOM_SECURE_TLS);
SetupTlsContext(&client, CLIENT_CERT_NAME);
SetupTlsContext(&server, SERVER_CERT_NAME);
LogMessage(TEXT("\tCMyServer::Listen"));
nRet = server.Listen(pszServerAddress, uServerPort, 5);
LogMessage(TEXT("CMyClient::Connect"));
client.Connect(NULL, 0, pszServerAddress, uServerPort);
if (!client.Wait(2000))
{
if (!client.IsConnected())
{
LogMessage(TEXT("Timeout: client.Connect"));
nRet = DICOM_ERROR_NET_TIME_OUT;
goto Cleanup;
}
}
if (nRet == DICOM_SUCCESS)
{
//create the Associate Class as Request
LDicomAssociate dicomAssociateRequest(TRUE);
dicomAssociateRequest.Default();
// Send A-Associate-RQ message
dicomAssociateRequest.SetCalled(TEXT("LEAD_SERVER"));
dicomAssociateRequest.SetCalling(TEXT("LEAD_CLIENT"));
LogMessage(TEXT("CMyClient::SendAssociateRequest"));
nRet = client.SendAssociateRequest(&dicomAssociateRequest);
if (!client.Wait(5000))
{
LogMessage(TEXT("Timeout: client.Connect"));
nRet = DICOM_ERROR_NET_TIME_OUT;
goto Cleanup;
}
}
L_UCHAR nPresentationID = client.GetAssociate()->FindAbstract(UID_VERIFICATION_CLASS);
L_UINT16 uUniqueID = 99;
LogMessage(TEXT("CMyClient::SendCEchoRequest"));
client.SendCEchoRequest( nPresentationID, uUniqueID, UID_VERIFICATION_CLASS);
WaitForProcessing();
LogMessage(TEXT("CMyClient::SendReleaseRequest"));
client.SendReleaseRequest();
WaitForProcessing();
Cleanup:
LogMessage(TEXT("CMyClient::Close"));
client.Close();
client.Wait(1000);
LogMessage(TEXT("\tCMyServer::Close"));
server.Close();
LDicomNet::ShutDown();
return nRet;
}
}