// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
//
// GenericUCPDlg.cpp : implementation file
//

#include "stdafx.h"
#include "GenericUCP.h"
#include "GenericUCPDlg.h"
#include "deviceprop.h"
#include "SCPDDisplay.h"
#include "util.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
   CAboutDlg();
   // Dialog Data
   //{{AFX_DATA(CAboutDlg)
   enum { IDD = IDD_ABOUTBOX };
   //}}AFX_DATA
   //{{AFX_VIRTUAL(CAboutDlg)
   protected:
   virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
   //}}AFX_VIRTUAL

// Implementation
protected:
   //{{AFX_MSG(CAboutDlg)
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
   //{{AFX_DATA_INIT(CAboutDlg)
   //}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
   CDialog::DoDataExchange(pDX);
   //{{AFX_DATA_MAP(CAboutDlg)
   //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
   //{{AFX_MSG_MAP(CAboutDlg)
   // No message handlers
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()

// App command to run the dialog
void CGenericUCPDlg::OnAppAbout()
{
   CAboutDlg aboutDlg;
   aboutDlg.DoModal();
}

/////////////////////////////////////////////////////////////////////////////
// CGenericUCPDlg dialog

//+---------------------------------------------------------------------------
//
//  Member:   CGenericUCPDlg
//
//  Purpose:    Constructor
//
//  Arguments:
//      pParent         [in]        Parent Window
//
//  Returns:    None
//
//  Notes:
//     We create the callback objects for device finder and service objects.
//

CGenericUCPDlg::CGenericUCPDlg(CWnd* pParent)
   : CDialog(CGenericUCPDlg::IDD, pParent)
{
    m_pDeviceFinder = NULL;
   // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
   m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

   // Create a device finder callback object
   m_pDeviceFinderCallback = NULL;
   m_pDeviceFinderCallback = CDeviceFinderCallback::Create();
   if (m_pDeviceFinderCallback == NULL)
   {
      MessageBox(
         L"Error: DeviceFinderCallback object creation failed"
         );
   }
   else
   {
      ((CDeviceFinderCallback*)m_pDeviceFinderCallback)->SetDialogPointer(
                                                            this
                                                            );
   }

   // Create a service callback object
   m_pServiceCallback = NULL;
   m_pServiceCallback = CServiceCallback::Create();
   if (m_pServiceCallback == NULL)
   {
      MessageBox(L"Error: ServiceCallback object creation failed");
   }
   else
   {
      ((CServiceCallback*)m_pServiceCallback)->SetDialogPointer(this);
   }

   // Initialize close handle
   m_hCloseEvent = ::CreateEvent(NULL,   // default security attributes
                                 TRUE,   // manual reset event object
                                 FALSE,  // initial state is unsignaled
                                 NULL);  // unnamed object

   if (!m_hCloseEvent)
   {
      MessageBox(L"Error: Failed to create Close event");
   }

}

//+---------------------------------------------------------------------------
//
//  Member:		~CGenericUCPDlg
//
//  Purpose:    Destructor
//
//  Arguments:
//		None
//
//  Returns:    None
//
//  Notes:
//     We stop the async find if it is proceeding, and release the callback
//     objects
//

CGenericUCPDlg::~CGenericUCPDlg()
{
   // Destructor
  if(m_hCloseEvent)
   {
      CloseHandle(m_hCloseEvent);
   }
   ReleaseObj(m_pServiceCallback);
   m_pServiceCallback = NULL;
   ReleaseObj(m_pDeviceFinderCallback);
   m_pDeviceFinderCallback = NULL;
   StopAsyncFindIfStarted();
   ReleaseObj(m_pDeviceFinder);
   m_pDeviceFinder = NULL;
}

// MFC Function
void CGenericUCPDlg::DoDataExchange(CDataExchange* pDX)
{
   CDialog::DoDataExchange(pDX);
   //{{AFX_DATA_MAP(CGenericUCPDlg)
   DDX_Control(pDX, IDC_VARIABLENAME, m_VariableName);
   DDX_Control(pDX, IDC_ACTIONNAME, m_ActionName);
   DDX_Control(pDX, IDC_STATUS, m_StatusText);
   DDX_Control(pDX, IDC_EVENTS, m_EventText);
   DDX_Control(pDX, IDC_VIEWSCPD, m_ViewSCPD);
   DDX_Control(pDX, IDC_SCPDURL, m_SCPDURL);
   DDX_Control(pDX, IDC_PRESENTATION, m_Presentation);
   DDX_Control(pDX, IDC_QUERYVARIABLE, m_QueryVariable);
   DDX_Control(pDX, IDC_INVOKEACTION, m_InvokeAction);
   DDX_Control(pDX, IDC_SUBSCRIBE, m_Subscribe);
   DDX_Control(pDX, IDC_DISCOVERY, m_StartDiscovery);
   DDX_Control(pDX, IDC_DEVPROP, m_DeviceProperties);
   DDX_Control(pDX, IDC_COMBOSERVICE, m_ServiceCombo);
   DDX_Control(pDX, IDC_COMBOFIND, m_FindCombo);
   DDX_Control(pDX, IDC_COMBODEVICE, m_DeviceCombo);
   DDX_Control(pDX, IDC_CLOSE, m_CloseApp);
   DDX_Control(pDX, IDC_ACTIONOUTARGUMENT, m_ActionOutArgument);
   DDX_Control(pDX, IDC_ACTIONINARGUMENT, m_ActionInArgument);
   DDX_Control(pDX, IDC_FINDBYTYPE, m_FindByType);
   DDX_Control(pDX, IDC_FINDBYUDN, m_FindByUDN);
   DDX_Control(pDX, IDC_ASYNCFIND, m_AsyncFind);
   DDX_Control(pDX, IDC_ASYNCH, m_AsynchControl);
   DDX_Control(pDX, IDC_DELAYSUBSCRIPTION, m_DelaySubscription);
   //}}AFX_DATA_MAP
}

//Message Map
BEGIN_MESSAGE_MAP(CGenericUCPDlg, CDialog)
   //{{AFX_MSG_MAP(CGenericUCPDlg)
   ON_WM_SYSCOMMAND()
   ON_WM_PAINT()
   ON_WM_QUERYDRAGICON()
   ON_BN_CLICKED(IDC_VIEWSCPD, OnViewSCPD)
   ON_BN_CLICKED(IDC_FINDBYTYPE, OnFindByType)
   ON_BN_CLICKED(IDC_DISCOVERY, OnDiscoveryClicked)
   ON_BN_CLICKED(IDC_FINDBYUDN, OnFindbyUDN)
   ON_BN_CLICKED(IDC_ASYNCFIND, OnAsyncFind)
   ON_CBN_SELENDOK(IDC_COMBODEVICE, OnSelEndOkComboDevice)
   ON_CBN_SELENDOK(IDC_COMBOSERVICE, OnSelEndOkComboService)
   ON_BN_CLICKED(IDC_DEVPROP, OnDevpropClick)
   ON_BN_CLICKED(IDC_QUERYVARIABLE, OnQueryVariable)
   ON_BN_CLICKED(IDC_INVOKEACTION, OnInvokeAction)
   ON_BN_CLICKED(IDC_SUBSCRIBE, OnSubscribe)
   ON_BN_CLICKED(IDC_CLOSE, OnCloseClick)
   ON_BN_CLICKED(IDC_ASYNCH, OnCheckBoxClick)
   ON_BN_CLICKED(IDC_DELAYSUBSCRIPTION, OnCheckBoxClick)
   ON_WM_CLOSE()
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CGenericUCPDlg message handlers

//+---------------------------------------------------------------------------
//
//  Member:		OnInitDialog
//
//  Purpose:    Initialization of the dialog box.
//
//  Arguments:
//				None
//
//  Returns:    TRUE
//
//  Notes:
//     Default to AsyncFind search and set the flag for m_fAsyncFindRunning to
//     FALSE
//

BOOL CGenericUCPDlg::OnInitDialog()
{
   CDialog::OnInitDialog();

   // Add "About..." menu item to system menu.

   // IDM_ABOUTBOX must be in the system command range.
   ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
   ASSERT(IDM_ABOUTBOX < 0xF000);

   CMenu* pSysMenu = GetSystemMenu(FALSE);
   if (pSysMenu != NULL)
   {
      CString strAboutMenu;
      (void) strAboutMenu.LoadString(IDS_ABOUTBOX);
      if (!strAboutMenu.IsEmpty())
      {
         pSysMenu->AppendMenu(MF_SEPARATOR);
         pSysMenu->AppendMenu(
                              MF_STRING,
                              IDM_ABOUTBOX,
                              strAboutMenu
                              );
      }
   }

   // Set the icon for this dialog.  The framework does this automatically
   //  when the application's main window is not a dialog
   SetIcon(m_hIcon, TRUE);			// Set big icon
   SetIcon(m_hIcon, FALSE);		// Set small icon
   m_pDeviceFinder = NULL;
   m_fAsyncFindRunning = FALSE; // Async Find is not running
   m_DeviceCombo.SetCurSel(-1);
   m_ServiceCombo.SetCurSel(-1);
   m_AsynchControl.SetCheck(BST_CHECKED);       // Set the check button of the asynchronous control box
   m_DelaySubscription.SetCheck(BST_CHECKED);   // Set the check button of the delay subscription box
   m_Subscribe.EnableWindow(FALSE);             // Subscribe is disabled at the beginning
   m_ViewSCPD.EnableWindow(TRUE);               // Enable View SCPD button

   // Grow the size of the array five at a time
   m_astrDevType.SetSize(5,5);
   m_AsyncFind.SetCheck(TRUE);

   // Initialize the Device Types.
   InitializeDevTypeArray();

   // Tell the user to click on Start Discovery Button
   m_StatusText.SetWindowText(
         L"Choose the type of device discovery and proceed"
         );

   return TRUE;
}

//MFC function
BOOL CGenericUCPDlg::PreTranslateMessage(_In_ MSG *pMsg)
{
   if (pMsg->message==WM_KEYDOWN && pMsg->wParam==VK_RETURN)
   {
      DWORD def_id=GetDefID();
      if (def_id!=0)
      {
         CWnd *wnd=FromHandle(pMsg->hwnd);
         // you may implement other ways of testing, e.g.
         //  comparing to array of CWnd*, comparing to array of IDs etc.
         WCHAR class_name[16];
         if (GetClassName(wnd->GetSafeHwnd(),class_name,sizeof(class_name)/sizeof(WCHAR))!=0)
         {
            if (_wcsnicmp(class_name,L"edit",5)==0)
            {
               return TRUE;
               // discard the message!
            }
         }
      }
   }
   // be a good citizen - call the base class
   return CDialog::PreTranslateMessage(pMsg);
}

// MFC generated function
void CGenericUCPDlg::OnSysCommand(_In_ UINT nID, _In_ LPARAM lParam)
{
   if ((nID & 0xFFF0) == IDM_ABOUTBOX)
   {
      CAboutDlg dlgAbout;
      dlgAbout.DoModal();
   }
   else
   {
      CDialog::OnSysCommand(nID, lParam);
   }
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CGenericUCPDlg::OnPaint()
{
   if (IsIconic())
   {
      CPaintDC dc(this); // device context for painting

      SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

      // Center icon in client rectangle
      int cxIcon = GetSystemMetrics(SM_CXICON);
      int cyIcon = GetSystemMetrics(SM_CYICON);
      CRect rect;
      GetClientRect(&rect);
      int x = (rect.Width() - cxIcon + 1) / 2;
      int y = (rect.Height() - cyIcon + 1) / 2;

      // Draw the icon
      dc.DrawIcon(x, y, m_hIcon);
   }
   else
   {
      CDialog::OnPaint();
   }
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CGenericUCPDlg::OnQueryDragIcon()
{
   return (HCURSOR) m_hIcon;
}

//+---------------------------------------------------------------------------
//
//  Member:		ClearDeviceCombo
//
//  Purpose:    Clears the device combo and releases the device pointer stored
//				in the list
//
//  Arguments:
//				None
//
//  Returns:    None
//
//  Notes:
//

void CGenericUCPDlg::ClearDeviceCombo(void)
{
   IUPnPDevice *pDevice = NULL;

   // We are going to release the device pointers we have stored in ItemDataPtr of each item
   int nCount = m_DeviceCombo.GetCount();

   for (int i=0; i < nCount; i++){
      pDevice = (IUPnPDevice *) m_DeviceCombo.GetItemDataPtr(i);
      ReleaseObj(pDevice);
   }
   m_DeviceCombo.ResetContent();
}

//+---------------------------------------------------------------------------
//
//  Member:		ClearServiceCombo
//
//  Purpose:    Clears the service combo and releases the service pointer stored
//				in the list
//
//  Arguments:
//				None
//
//  Returns:    None
//
//  Notes:
//

void CGenericUCPDlg::ClearServiceCombo(void)
{
   IUPnPService *pService = NULL;

   // We are going to release the service pointers we have stored in ItemDataPtr of each item
   int nCount = m_ServiceCombo.GetCount();

   for (int i=0; i < nCount; i++){
      pService = (IUPnPService *) m_ServiceCombo.GetItemDataPtr(i);
      ReleaseObj(pService);
   }
   m_ServiceCombo.ResetContent();
   m_EventText.SetWindowText(L""); // Clear  the event text
}

//+---------------------------------------------------------------------------
//
//  Member:		ClearAllDataStructures
//
//  Purpose:    Clears various combo boxes, sets the text of the labels
//
//  Arguments:
//		fClearFindCombo	[in]	Flag to clear the findCombo box.
//
//  Returns:    None
//
//  Notes:
//

void CGenericUCPDlg::ClearAllDataStructures(_In_ BOOL fClearFindCombo)
{
   // We are going to clear all the combo boxes and all the COM interfaces.
   if (fClearFindCombo)
   {
      // Clear the find combo box
      m_FindCombo.ResetContent();
      m_astrDevType.RemoveAll();
   }

   ClearDeviceCombo();
   ClearServiceCombo();

   m_StatusText.SetWindowText(L"");
   m_EventText.SetWindowText(L"");
}

//+---------------------------------------------------------------------------
//
//  Member:		OnFindByType
//
//  Purpose:    Initialize the find combo box with the device types
//		from devType.txt and stop async find if necessary
//
//  Arguments:
//		None
//
//  Returns:    None
//
//  Notes:	Called when FindByType radio button is clicked
//

void CGenericUCPDlg::OnFindByType()
{
   // We are going to do a FindByType
   // Hence we are going to load the FindCombo with the list of the types
   // from devType.txt.

   StopAsyncFindIfStarted();
   ClearAllDataStructures(TRUE);
   InitializeDevTypeArray();
}

//+---------------------------------------------------------------------------
//
//  Member:		OnFindByUDN
//
//  Purpose:    Initialize the find combo box with the UDNs
//				from udn.txt and stop async find if necessary
//
//  Arguments:
//				None
//
//  Returns:    None
//
//  Notes:		Called when FindByUDN radio button is clicked
//
void CGenericUCPDlg::OnFindbyUDN()
{
   // We are going to do a FindByUDN
   // Hence we are going to load the FindCombo with the list of the types
   // from UDN.txt.
   StopAsyncFindIfStarted();
   ClearAllDataStructures(TRUE);
   InitializeUDNList();
}

//+---------------------------------------------------------------------------
//
//  Member:		OnAsyncFind
//
//  Purpose:    Initialize the find combo box with the device types
//              from devType.txt and stop async find if necessary
//
//  Arguments:
//		None
//
//  Returns:    None
//
//  Notes:		Called when AsyncFind radio button is clicked
//
void CGenericUCPDlg::OnAsyncFind()
{
   // We are going to do AsyncFind
   // Hence we are going to load the FindCombo with the list of the types
   // from devType.txt.
   StopAsyncFindIfStarted();
   ClearAllDataStructures(TRUE);
   InitializeDevTypeArray();
}

//+---------------------------------------------------------------------------
//
//  Member:		OnDiscoveryClicked
//
//  Purpose:    Start the discovery of the type of search requested
//
//  Arguments:
//				None
//
//  Returns:    None
//
//  Notes:		Called when "Start Discovery" button is clicked
//

void CGenericUCPDlg::OnDiscoveryClicked()
{
   // Disable the StartDiscovey Button if the find is a synchronous find.
   if (m_FindByType.GetCheck() || m_FindByUDN.GetCheck())
   {
      m_StartDiscovery.EnableWindow(FALSE);
   }

   HRESULT hr = S_OK;
   CString strWindowString;

   StopAsyncFindIfStarted(); // If AsyncFind is in progress, stop it
   ClearAllDataStructures(FALSE); // Don't clear the find combo box

   m_StatusText.SetWindowText(L"Discovery Clicked");
   m_EventText.SetWindowText(L"");

   // Check if we chosen an item in the list
   m_FindCombo.GetWindowText(strWindowString);
   if(strWindowString.IsEmpty() && m_FindCombo.GetCurSel()==-1)
   {
      MessageBox(L"Select one of the items from the list");

      if (m_FindByType.GetCheck() || m_FindByUDN.GetCheck())
      {
         m_StartDiscovery.EnableWindow(TRUE);
      }

      return;
   }

   // We have to get the search parameters
   CString strSearchParameter;
   if(m_FindByType.GetCheck() || m_AsyncFind.GetCheck())
   {
      if(m_FindCombo.GetCurSel()==-1)
      {
         strSearchParameter=strWindowString; // Get it from the edit box
      }
      else
      {
         strSearchParameter=m_astrDevType[m_FindCombo.GetCurSel()];
      }
   }
   else
   {
      if(m_FindByUDN.GetCheck())
      {
         strSearchParameter=strWindowString;
      }
   }

   ReleaseObj(m_pDeviceFinder);
   m_pDeviceFinder = NULL;

   // Instantiate the device finder object
   hr = CoCreateInstance( CLSID_UPnPDeviceFinder,
                          NULL,
                          CLSCTX_SERVER,
                          IID_IUPnPDeviceFinder,
                          (LPVOID *)&m_pDeviceFinder);

   if(hr!=S_OK)
   {
      m_StatusText.SetWindowText(
      L"CoCreateInstance failed for device finder object"
      );
      PrintErrorText(hr);

      if (m_FindByType.GetCheck() || m_FindByUDN.GetCheck())
      {
         m_StartDiscovery.EnableWindow(TRUE);
      }

      return;
   }

   BSTR bstrSearchType = NULL;
   bstrSearchType = strSearchParameter.AllocSysString();
   if (bstrSearchType != NULL)
   {
      if(m_FindByType.GetCheck())
      {
         // We have to process the FindByType.
         ProcessFindByType(bstrSearchType);
         m_StartDiscovery.EnableWindow(TRUE);
      }
      else if (m_FindByUDN.GetCheck())
      {
         // We have to process the FindByUDN.
         ProcessFindByUDN(bstrSearchType);
         m_StartDiscovery.EnableWindow(TRUE);
      }
      else if (m_AsyncFind.GetCheck())
      {
         // We have to process the AsyncFind.
         ProcessAsyncFind(bstrSearchType);
         SysFreeString(bstrSearchType);

         // We should not release the device finder object.
         // Hence we are going to handle the exit here
         return;
      }
      m_StatusText.SetWindowText(L"End of discovery process");

      if(m_DeviceCombo.GetCount()>0)
      {
         m_DeviceCombo.SetCurSel(0);
         OnSelEndOkComboDevice();
      }

      SysFreeString(bstrSearchType);

      // We are not releasing the device finder interface in the case of
      // async find
      ReleaseObj(m_pDeviceFinder);

      m_pDeviceFinder = NULL;
   }

}

//+---------------------------------------------------------------------------
//
//  Member:		ProcessFindByType
//
//  Purpose:    Helper function for processing the FindByType search
//
//  Arguments:
//		bstrSearchType	[in]	Search type identifier
//
//  Returns:    None
//
//  Notes:
//

void CGenericUCPDlg::ProcessFindByType(_In_ BSTR bstrSearchType)
{
   // We have to process FindByType search
   HRESULT hr = S_OK;
   IUPnPDevices* pDevices = NULL;
   IUnknown* punkEnum = NULL;
   IEnumUnknown *pEU = NULL;

   m_StatusText.SetWindowText(L"FindByType selected");

   ASSERT(m_pDeviceFinder);
   if (m_pDeviceFinder == NULL)
   {
      m_StatusText.SetWindowText(L"Error: Missing DeviceFinder");
      return;
   }

   hr = m_pDeviceFinder->FindByType(bstrSearchType, 0,  &pDevices);
   if (SUCCEEDED(hr))
   {
      // We have to enumerate the devices
      long lCount;
      hr = pDevices->get_Count(&lCount);
      if (SUCCEEDED(hr))
      {
         if (lCount != 0)
         {
            hr = pDevices->get__NewEnum(&punkEnum);
            if (SUCCEEDED(hr))
            {
               hr = punkEnum->QueryInterface(IID_IEnumUnknown, (VOID **) &pEU);
               if (SUCCEEDED(hr))
               {
                  for (long lIndex = 0; lIndex<lCount; lIndex++)
                  {
                     IUnknown* punkDevice = NULL;
                     IUPnPDevice* pDevice=NULL;
                     hr = pEU->Next(1, &punkDevice, NULL);
                     if (SUCCEEDED(hr))
                     {
                        // Get a IUPnPDevice pointer to the device just got
                        hr = punkDevice->QueryInterface(
                                             IID_IUPnPDevice,
                                             (VOID **)&pDevice
                                             );
                        if (SUCCEEDED(hr))
                        {
                           // Add the found device to the device list
                           // Get the friendly name of the device
                           BSTR bstrFriendlyName = NULL;
                           hr = pDevice->get_FriendlyName(&bstrFriendlyName);
                           if (SUCCEEDED(hr))
                           {
                              WCHAR wszFriendlyName[DATA_BUFSIZE];
                              _snwprintf_s(
                                      wszFriendlyName,
                                      DATA_BUFSIZE,
                                      _TRUNCATE,
                                      L"%ls",
                                      bstrFriendlyName
                                      );
                              m_DeviceCombo.AddString(wszFriendlyName);
                              m_DeviceCombo.SetItemDataPtr(
                                               (int)lIndex,
                                                pDevice
                                               );
                              SysFreeString(bstrFriendlyName);
                           }
                           else
                           {
                              m_StatusText.SetWindowText(
                                L"Error: FriendlyName failed"
                                );
                              PrintErrorText(hr);
                              ReleaseObj(pDevice);
                              ReleaseObj(punkDevice);
                              break;
                           }
                        }
                        else
                        {
                           m_StatusText.SetWindowText(
                           L"Error: QueryInterface for IUPnPDevice failed"
                               );
                           PrintErrorText(hr);
                           ReleaseObj(punkDevice);
                           break;
                        }
                        ReleaseObj(punkDevice);
                     }
                     else
                     {
                        m_StatusText.SetWindowText(
                                    L"Error: Traversing the device list using Next"
                                    L" method failed"
                                    );
                        PrintErrorText(hr);
                        break; // Get out of the for loop
                     }
                  } // For loop
                  ReleaseObj(pEU);
               }
               else
               {
                  m_StatusText.SetWindowText(
                           L"Error: QueryInterface for IID_IEnumUnknown failed"
                           );
                  PrintErrorText(hr);
               }
               ReleaseObj(punkEnum);
            }
            else
            {
               m_StatusText.SetWindowText(
                     L"get__NewEnum in IUPnPDevices failed"
                     );
               PrintErrorText(hr);
            }
         }
         else
         {
            MessageBox(L"Found no devices");
         }
      }
      else
      {
         m_StatusText.SetWindowText(L"get_Count in IUPnPDevices failed");
         PrintErrorText(hr);
      }
      ReleaseObj(pDevices);
   }
   else
   {
      m_StatusText.SetWindowText(L"FindByType failed");
      PrintErrorText(hr);
   }
}

//+---------------------------------------------------------------------------
//
//  Member:		ProcessFindByUDN
//
//  Purpose:    Helper function for processing the FindByUDN search
//
//  Arguments:
//				bstrSearchType	[in]	Search type identifier
//
//  Returns:    None
//
//  Notes:
//

void CGenericUCPDlg::ProcessFindByUDN(_In_ BSTR bstrSearchType)
{
   // FindByUDN selected
   HRESULT hr= S_OK;

   m_StatusText.SetWindowText(L"FindByUDN selected");

   ASSERT(m_pDeviceFinder);
   if (m_pDeviceFinder == NULL)
   {
      m_StatusText.SetWindowText(L"Error: Missing DeviceFinder");
      return;
   }

   IUPnPDevice* pDevice = NULL;
   hr = m_pDeviceFinder->FindByUDN(bstrSearchType, &pDevice);
   if(hr == S_OK)
   {
      // Found a device with given udn
      // Add the found device to the device list
      // Get the friendly name of the device
      BSTR bstrFriendlyName = NULL;
      hr = pDevice->get_FriendlyName(&bstrFriendlyName);
      if(SUCCEEDED(hr))
      {
         WCHAR wszFriendlyName[DATA_BUFSIZE];
         _snwprintf_s(wszFriendlyName, DATA_BUFSIZE, _TRUNCATE, L"%ls", bstrFriendlyName);
         m_DeviceCombo.AddString(wszFriendlyName);
         m_DeviceCombo.SetItemDataPtr(0, pDevice);	// Add as the first element
         SysFreeString(bstrFriendlyName);
      }
      else
      {
         m_StatusText.SetWindowText(L"Error: FriendlyName failed");
         PrintErrorText(hr);
      }
   }
   else
   {
      MessageBox(L"Found no device with given udn");
   }
}

//+---------------------------------------------------------------------------
//
//  Member:		ProcessAsyncFind
//
//  Purpose:    Helper function for processing the AsyncFind search
//
//  Arguments:
//				bstrSearchType	[in]	Search type identifier
//
//  Returns:    None
//
//  Notes:
//
void CGenericUCPDlg::ProcessAsyncFind(_In_ BSTR bstrSearchType)
{
   HRESULT hr = S_OK;

   m_StatusText.SetWindowText(L"AsyncFind selected");

   ASSERT(m_pDeviceFinder);
   if (m_pDeviceFinder == NULL)
   {
      m_StatusText.SetWindowText(L"Error: Missing DeviceFinder");
      return;
   }

   // We have to start the AsyncFind.
   if (m_pDeviceFinderCallback!=NULL)
   {
      hr = m_pDeviceFinder->CreateAsyncFind(
                             bstrSearchType,
                             NULL,
                             m_pDeviceFinderCallback,
                             &m_lAsyncFindHandle
                             );
      if (SUCCEEDED(hr))
      {
         hr = m_pDeviceFinder->StartAsyncFind(m_lAsyncFindHandle);
         if (SUCCEEDED(hr))
         {
            m_fAsyncFindRunning = TRUE;
         }
         else
         {
            PrintErrorText(hr);
            m_StatusText.SetWindowText(
                       L"Error: StartAsyncFind failed"
                       );
            hr = m_pDeviceFinder->CancelAsyncFind(m_lAsyncFindHandle);
            if (FAILED(hr))
            {
               m_StatusText.SetWindowText(L"CancelAsyncFind failed");
               PrintErrorText(hr);
            }
         }
      }
      else
      {
         PrintErrorText(hr);
         m_StatusText.SetWindowText(L"Error: CreateAsyncFind failed");
      }
   }
   else
   {
      m_StatusText.SetWindowText(
                   L"DeviceFinderCallback object is not available"
                   );
   }
}

//+---------------------------------------------------------------------------
//
//  Member:	InitializeDevTypeArray
//
//  Purpose:    Helper function for initializing the find combo box
//		with list of device types
//
//  Arguments:
//		None
//
//  Returns:    None
//
//  Notes:	Called when FindByType or AsyncFind is clicked
//
void CGenericUCPDlg::InitializeDevTypeArray()
{
   // First remove all the stored elements in the devType array
   m_astrDevType.RemoveAll();

   // Also remove the items in the FindCombo
   m_FindCombo.ResetContent();

   // Check whether the file devtype.txt exists
   if (!fFileExists(L"devtype.txt"))
   {
      // devtype.txt file doesn't exist
      m_StatusText.SetWindowText(L"Warning: devType.txt doesn't exist");
      return;
   }

   // We can start reading the file
   CStdioFile fileDevType;
   if (!fileDevType.Open(L"devtype.txt", CFile::modeRead|CFile::typeText))
   {
      MessageBox(L"Error: Can't open the devtype.txt file");
      return;
   }

   CString strLine;
   while (fileDevType.ReadString(strLine))
   {
      // We have got one line at a time
      if (!strLine.IsEmpty())
      {
         // We are going to split the string first
         int iSpacePos;
         iSpacePos = strLine.Find(' '); // Find the first space character
         if (iSpacePos == -1) //space character not found
         {
            MessageBox(
                 L"Error: Malformed line in devType.txt, no space "
                 L"type and description"
                 );
            fileDevType.Close();
            return;
         }

         CString strDeviceType = strLine.Left(iSpacePos);
         CString strDeviceDesc = strLine.Right(
                                         strLine.GetLength() - iSpacePos - 1
                                         );

         // We are going to add this to the szaDevType Array
         m_astrDevType.Add(strDeviceType);
         //Also add the device description to the findCombo
         m_FindCombo.AddString(strDeviceDesc);
      }
   }

   fileDevType.Close();
   m_FindCombo.SetCurSel(0); // Select the first item in the list
}

//+---------------------------------------------------------------------------
//
//  Member:		fFileExists
//
//  Purpose:    Find whether the given file exists
//
//  Arguments:
//	ptszFileName [in]	File name of the file to check
//
//  Returns:    TRUE or FALSE
//
//  Notes:
//
BOOL CGenericUCPDlg::fFileExists(_In_ LPTSTR ptszFileName)
{
   DWORD   dwValue;
   dwValue = GetFileAttributes(ptszFileName);

   if (dwValue == -1 )
   {
   // Return false if we are not able to get the file attributes
      return FALSE;
   }
   else
   {
   return TRUE;
   }
}

//+---------------------------------------------------------------------------
//
//  Member:		InitializeUDNList
//
//  Purpose:    Helper function for initializing the find combo box
//				with list of UDNs
//
//  Arguments:
//				None
//
//  Returns:    None
//
//  Notes:		Called when FindByUDN is clicked
//
void CGenericUCPDlg::InitializeUDNList()
{
   // Also remove the items in the FindCombo
   m_FindCombo.ResetContent();

   // Check whether the file udn.txt exists
   if (!fFileExists(L"udn.txt"))
   {
      // udn.txt file doesn't exist
      m_StatusText.SetWindowText(L"Warning: udn.txt doesn't exist");
      return;
   }

   // We can start reading the file

   CStdioFile fileDevType;
   if (!fileDevType.Open(L"udn.txt", CFile::modeRead|CFile::typeText))
   {
      MessageBox(L"Error: Can't open the udn.txt file");
      return;
   }

   CString strLine;
   while (fileDevType.ReadString(strLine))
   {
      // We have got one line at a time
      if (!strLine.IsEmpty())
      {
         m_FindCombo.AddString(strLine);
      }
   }

   fileDevType.Close();
   m_FindCombo.SetCurSel(0); // Select the first item in the list

}

//+---------------------------------------------------------------------------
//
//  Member:		StopAsyncFindIfStarted
//
//  Purpose:    Helper function for stopping the async find if proceeding
//
//  Arguments:
//				None
//
//  Returns:    None
//
//  Notes:
//
void CGenericUCPDlg::StopAsyncFindIfStarted()
{
   HRESULT hr = S_OK;

   // This will stop the async find if it is in progress
   if (m_fAsyncFindRunning == TRUE)
   {
      ASSERT(m_pDeviceFinder);
      if (m_pDeviceFinder == NULL)
      {
         m_StatusText.SetWindowText(L"Error: Missing DeviceFinder");
      }
      else
      {
         hr = m_pDeviceFinder->CancelAsyncFind(m_lAsyncFindHandle);
         if (FAILED(hr))
         {
            m_StatusText.SetWindowText(L"CancelAsyncFind failed");
            MessageBox(L"Cancel Async Find Failed.", L"Error");
         }
      }

      m_fAsyncFindRunning = FALSE;
   }
}

//+---------------------------------------------------------------------------
//
//  Member:		AddDevice
//
//  Purpose:    Helper function for adding devices to the device combo box
//
//  Arguments:
//		pDevice	[in]	COM interface pointer of the device being added
//
//  Returns:    None
//
//  Notes:
//     This is called by the devicefinder callback object (DeviceAdded func)
//
void CGenericUCPDlg::AddDevice(_In_ IUPnPDevice *pDevice)
{
   //We are going to add a device
   HRESULT hr=S_OK;
   BSTR bstrFriendlyName = NULL;

   hr = pDevice->get_FriendlyName(&bstrFriendlyName);
   if (SUCCEEDED(hr))
   {
      pDevice->AddRef(); // Add Ref for the device
      WCHAR wszFriendlyName[DATA_BUFSIZE];
      WCHAR wszStatusString[DATA_BUFSIZE];
      _snwprintf_s(wszFriendlyName, DATA_BUFSIZE, _TRUNCATE, L"%ls", bstrFriendlyName);
      _snwprintf_s(
               wszStatusString,
               DATA_BUFSIZE,
               _TRUNCATE,
               L"Added Device %ls",
               wszFriendlyName
               );

      m_StatusText.SetWindowText(wszStatusString);

      int nCount = m_DeviceCombo.GetCount(); // Find the end of the list

      m_DeviceCombo.AddString(wszFriendlyName);

      // Explicitly AddRef for passing off the ownership to the combolist item.
      pDevice->AddRef();

      // Add the item at the end of the combolist
      m_DeviceCombo.SetItemDataPtr(nCount, pDevice);
      pDevice->Release(); // Release the current ownership of the pointer

      if (nCount==0)
      {
         m_DeviceCombo.SetCurSel(0);
         OnSelEndOkComboDevice();
      }

      SysFreeString(bstrFriendlyName);
   }
   else
   {
      PrintErrorText(hr);
      m_StatusText.SetWindowText(
                      L"Error: FriendlyName failed for device added failed"
                      );
   }
}

//+---------------------------------------------------------------------------
//
//  Member:	RemoveDevice
//
//  Purpose:    Helper function for removing device from the device combo box
//
//  Arguments:
//		bstrUDN	[in]	UDN of the device being deleted
//
//  Returns:    None
//
//  Notes:
//     This is called by the devicefinder callback object (DeviceRemoved func)
//
void CGenericUCPDlg::RemoveDevice(_In_ BSTR bstrUDN)
{
   //We are going to remove the device
   HRESULT hr=S_OK;
   IUPnPDevice *pDevice = NULL;
   BSTR bstrTemp = NULL;
   BOOL bFound = FALSE;

   // We are going to release the device pointers we have stored in ItemDataPtr of each item
   int nCount = m_DeviceCombo.GetCount();

   for (int i=0; !bFound && i < nCount; i++)
   {
      pDevice = (IUPnPDevice *) m_DeviceCombo.GetItemDataPtr(i);

      pDevice->AddRef();

      // Check if the device pointer is the one we want
      hr=pDevice->get_UniqueDeviceName(&bstrTemp);

      if(SUCCEEDED(hr))
      {
         if(wcscmp(bstrTemp, bstrUDN)==0)
         {
            // This is the removed device
            ReleaseObj(pDevice); // Remove the reference that was taken when the device was added to the combo
            m_DeviceCombo.DeleteString(i);

            bFound = TRUE;
         }

         SysFreeString(bstrTemp);
         bstrTemp = NULL;
      }
      else
      {
         PrintErrorText(hr);
         m_StatusText.SetWindowText(L"Error: GetUniqueDeviceName failed for the device in the list");
      }

      ReleaseObj(pDevice);
      pDevice = NULL;
   }
}

//+---------------------------------------------------------------------------
//
//  Member:		OnSelEndOkComboDevice
//
//  Purpose:    To populate the service list for the device selected
//
//  Arguments:
//				None
//
//  Returns:    None
//
//  Notes:
//
void CGenericUCPDlg::OnSelEndOkComboDevice()
{
   // Current Device is changed
   TRACE(L"SelendokDevice\n");

   HRESULT hr = S_OK;
   IUPnPDevice *pDevice=NULL;
   IUPnPServices* pServices = NULL;
   IUnknown *punkEnum = NULL;
   IEnumUnknown *pEU = NULL;
   IUPnPServiceEnumProperty *pServiceEnum = NULL;
   int iSelectedItem;
   long lIndex;
   m_StatusText.SetWindowText(L"");
   m_EventText.SetWindowText(L"");
   iSelectedItem = m_DeviceCombo.GetCurSel();
   if(iSelectedItem!=CB_ERR){
      pDevice=(IUPnPDevice *) m_DeviceCombo.GetItemDataPtr(iSelectedItem);
      pDevice->AddRef();
      ClearServiceCombo();
      hr = pDevice->get_Services(&pServices);
      if(hr==S_OK){
         long lCount;
         hr = pServices->get_Count(&lCount);
         if(SUCCEEDED(hr)){
            if(lCount!=0){
               hr = pServices->QueryInterface(IID_IUPnPServiceEnumProperty, (VOID **) &pServiceEnum); // Set enum property
               if(SUCCEEDED(hr))
               {
                  if (BST_CHECKED == m_DelaySubscription.GetCheck())
                  {
                     // Delayed SCPD download and event subscription
                     hr = pServiceEnum->SetServiceEnumProperty(UPNP_SERVICE_DELAY_SCPD_AND_SUBSCRIPTION);
                     if (FAILED(hr))
                     {
                        PrintErrorText(hr);
                        m_StatusText.SetWindowText(L"SetServiceEnumProperty in IUPnPServiceEnumProperty failed");
                     }
                     else
                     {
                        // Enable the button because it will be used to do subscriptions
                        m_Subscribe.EnableWindow(TRUE);
                     }
                  }
                  else
                  {
                     // Opt-out of delayed SCPD download - Reset the service enum property
                     hr = pServiceEnum->SetServiceEnumProperty(0);
                     if (FAILED(hr))
                     {
                        PrintErrorText(hr);
                        m_StatusText.SetWindowText(L"SetServiceEnumProperty in IUPnPServiceEnumProperty failed");
                     }
                     else
                     {
                        //Disable button since this will not delay the subscription
                        m_Subscribe.EnableWindow(FALSE);
                     }
                  }
                  ReleaseObj(pServiceEnum);
               }
               else
               {
                  PrintErrorText(hr);
                  m_StatusText.SetWindowText(L"Error: QueryInterface for IID_IUPnPServiceEnumProperty failed");
               }
               if (SUCCEEDED(hr)){
                  // We have to get a IEnumUnknown pointer
                  hr = pServices->get__NewEnum(&punkEnum);
                  if(SUCCEEDED(hr)){
                     hr = punkEnum->QueryInterface(IID_IEnumUnknown, (VOID **) &pEU);
                     if(SUCCEEDED(hr)){
                        for(lIndex = 0; lIndex<lCount; lIndex++){
                           IUnknown *punkService = NULL;
                           IUPnPService *pService=NULL;
                           hr = pEU->Next(1, &punkService, NULL);
                           if(SUCCEEDED(hr)){
                              // Get a IUPnPService pointer to the service just got
                              hr = punkService->QueryInterface(IID_IUPnPService, (VOID **)&pService);
                              if(SUCCEEDED(hr)){
                                 BSTR bstrServiceId = NULL;
                                 hr = pService->get_Id(&bstrServiceId);
                                 if(SUCCEEDED(hr)){
                                    WCHAR wszServiceId[DATA_BUFSIZE];
                                    _snwprintf_s(wszServiceId, DATA_BUFSIZE, _TRUNCATE, L"%ls", bstrServiceId);
                                    m_ServiceCombo.AddString(wszServiceId);
                                    m_ServiceCombo.SetItemDataPtr((int)lIndex, pService);
                                    SysFreeString(bstrServiceId);
                                 }
                                 else{
                                    PrintErrorText(hr);
                                    m_StatusText.SetWindowText(L"Error: get_Id failed");
                                    ReleaseObj(pService);
                                    ReleaseObj(punkService);
                                    break;
                                 }
                              }
                              else{
                                m_StatusText.SetWindowText(L"Error: QueryInterface for IUPnPService failed");
                                ReleaseObj(punkService);
                                break;
                              }
                              ReleaseObj(punkService);
                           }
                           else{
                              m_StatusText.SetWindowText(L"Error: Traversing the service list using Next method failed");
                              break; // Get out of the for loop
                           }

                        }//For loop
                        if(m_ServiceCombo.GetCount()>0){
                           m_ServiceCombo.SetCurSel(0);
                           OnSelEndOkComboService();
                        }
                        ReleaseObj(pEU);
                     }
                     else{
                        PrintErrorText(hr);
                        m_StatusText.SetWindowText(L"Error: QueryInterface for IID_IEnumUnknown failed");
                     }
                     ReleaseObj(punkEnum);
                  }
                  else{
                     PrintErrorText(hr);
                     m_StatusText.SetWindowText(L"get__NewEnum in IUPnPServices failed");
                  }
               }
            }
            else{
               MessageBox(L"Found no services");
            }
         }
         else{
            PrintErrorText(hr);
            m_StatusText.SetWindowText(L"get_Count in IUPnPServices failed");
         }
         ReleaseObj(pServices);
      }
      else{
         PrintErrorText(hr);
         m_StatusText.SetWindowText(L"Error: Getting services for the current device failed");
      }
   ReleaseObj(pDevice); // Release the pDevice which is the currently selected device
   }
}

//+---------------------------------------------------------------------------
//
//  Member:		OnSelEndOkComboService
//
//  Purpose:    To add the callback for the service selected
//
//  Arguments:
//				None
//
//  Returns:    None
//
//  Notes:
//
void CGenericUCPDlg::OnSelEndOkComboService()
{
   if (BST_UNCHECKED == m_DelaySubscription.GetCheck())
   {
      OnSubscribe();
   }
}

//+---------------------------------------------------------------------------
//
//  Member:	OnDevpropClick
//
//  Purpose:    Show the properties of the selected device
//
//  Arguments:
//		None
//
//  Returns:    None
//
//  Notes:
//      This starts up the modal CDeviceProp dialog and displays the properties
//
void CGenericUCPDlg::OnDevpropClick()
{
   int iSelectedDevice;
   IUPnPDevice* pDevice = NULL;

   // We have to show the properties of the current device
   iSelectedDevice = m_DeviceCombo.GetCurSel();
   if (iSelectedDevice != CB_ERR)
   {
      pDevice =
            (IUPnPDevice*)m_DeviceCombo.GetItemDataPtr(iSelectedDevice);
      CDeviceProp dlg(pDevice);
      dlg.DoModal();
   }
   else
   {
      MessageBox(L"No device selected");
   }

}

//+---------------------------------------------------------------------------
//
//  Member:		OnSubscribe
//
//  Purpose:    Subscribe for the selected service in the service list
//
//  Arguments:
//				None
//
//  Returns:    None
//
//  Notes:
//
void CGenericUCPDlg::OnSubscribe()
{
   HRESULT hr = S_OK;
   IUPnPService* pService = NULL;

   int iSelectedItem ;

   iSelectedItem = m_ServiceCombo.GetCurSel();

   if (iSelectedItem!=CB_ERR)
   {
      pService=(IUPnPService*)m_ServiceCombo.GetItemDataPtr(
                                                    m_ServiceCombo.GetCurSel()
                                                    );
      pService->AddRef();
      m_EventText.SetWindowText(L"");

      // Synchronous service subscription
      if (BST_UNCHECKED == m_AsynchControl.GetCheck())
      {
         hr = pService->AddCallback(m_pServiceCallback);
      }

      // Asynchronous service subscription
      else if (BST_CHECKED == m_AsynchControl.GetCheck())
      {
         IUPnPServiceAsync * pServiceAsync = NULL;

         // Get a IUPnPServiceAsync pointer to the service
         hr = pService->QueryInterface(IID_IUPnPServiceAsync, (VOID **)&pServiceAsync);

         if(FAILED(hr))
         {
            PrintErrorText(hr);
            m_StatusText.SetWindowText(L"Error: QueryInterface for IUPnPServiceAsync failed.");
         }
         else
         {
            CUPnPAsyncResult* pAsyncResult = NULL;
            HANDLE  searchEvents[2]; // Array of events to wait for. This array is passed as a argument to CoWaitForMultipleHandles().
            DWORD   dwSignalled = 0;
            DWORD   dwWaitTime = INFINITE;
            DWORD   dwEventCount = 2;
            MSG msg;

            pAsyncResult = new CUPnPAsyncResult();
            pAsyncResult->AddRef();
            hr = pAsyncResult->Init();

            if(SUCCEEDED(hr))
            {
               searchEvents[0] = pAsyncResult->GetHandle();
               searchEvents[1] = m_hCloseEvent;

               hr = pServiceAsync->BeginSubscribeToEvents(m_pServiceCallback, pAsyncResult, &m_ullRequestID);

               if(SUCCEEDED(hr))
               {
                  while(TRUE)
                  {
                     // Wait for multiple objects.
                     DWORD dwWaitResult = ::MsgWaitForMultipleObjectsEx( dwEventCount,
                                                                     searchEvents,
                                                                     dwWaitTime,
                                                                     QS_ALLINPUT,
                                                                     MWMO_INPUTAVAILABLE);

                     if(WAIT_FAILED == dwWaitResult)
                     {
                        DWORD dwError = GetLastError();
                        hr = HRESULT_FROM_WIN32(dwError);
                        m_StatusText.SetWindowText(
                                                 L"Wait Failed."
                                                 );
                        break;
                     }
                     else if(WAIT_TIMEOUT == dwWaitResult)
                     {
                        hr = pServiceAsync->CancelAsyncOperation(m_ullRequestID);
                        break;
                     }
                     else if(WAIT_OBJECT_0 == dwWaitResult)
                     {
                        if(0 == dwSignalled)    // Subscriube to events completion event triggerred..
                        {
                           hr = pServiceAsync->EndSubscribeToEvents( m_ullRequestID );
                        }
                        break;
                     }
                     else if(WAIT_OBJECT_0 + 1 == dwWaitResult)
                     {
                        // Received Close Event. Exit and prepare to close
                        hr = pServiceAsync->CancelAsyncOperation(m_ullRequestID);
                        break;
                     }

                     // Process any MSG
                     if (::PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE))
                     {
                        ::TranslateMessage(&msg);
                        ::DispatchMessage(&msg);
                        continue;
                     }
                  }
               }
               else
               {
                  PrintErrorText(hr);
               }
            }
            ReleaseObj(pServiceAsync);
         }

      }
      if (FAILED(hr))
      {
         PrintErrorText(hr);
         m_StatusText.SetWindowText(L"Error: AddCallback failed");
      }
      ReleaseObj(pService);
   }
}

//+---------------------------------------------------------------------------
//
//  Member:		OnViewSCPD
//
//  Purpose:    Displays the SCPD document in a Messagebox
//
//  Arguments:
//				None
//
//  Returns:    None
//
//  Notes:
//
void CGenericUCPDlg::OnViewSCPD()
{
   HRESULT hr = S_OK;
   int iCurrentDevice, iCurrentService;

   VARIANT varValue;
   VariantInit(&varValue);
   IUPnPService* pService = NULL;
   IUPnPServiceDocumentAccess * pServiceAccess = NULL;
   // We are going to query the variable which is present in the variable
   // edit box
   m_ActionOutArgument.SetWindowText(L"");

   iCurrentDevice = m_DeviceCombo.GetCurSel();

   if (iCurrentDevice != CB_ERR)
   {
      iCurrentService = m_ServiceCombo.GetCurSel();
      if (iCurrentService != CB_ERR)
      {
         pService=(IUPnPService*)m_ServiceCombo.GetItemDataPtr(
                                       iCurrentService
                                       );
         pService->AddRef();

         hr = pService->QueryInterface(IID_IUPnPServiceDocumentAccess, (VOID **)&pServiceAccess);
         if(FAILED(hr))
         {
            m_StatusText.SetWindowText(L"Error: QueryInterface for IUPnPServiceDocumentAccess failed.");
         }
         else
         {
            //Use the IUPnPServiceDocumentAccess interface methods to retrieve the SCPD URL and text

            BSTR bstrDocURL = NULL;
            BSTR bstrDocument = NULL;
            WCHAR wszMessage[DATA_BUFSIZE];

            hr = pServiceAccess->GetDocumentURL(&bstrDocURL);

            if (S_OK == hr)
            {
               hr = pServiceAccess->GetDocument(&bstrDocument);

               _snwprintf_s(
                           wszMessage,
                           DATA_BUFSIZE,
                           _TRUNCATE,
                           L"%ls",
                           bstrDocURL
                           );
               m_SCPDURL.SetWindowText(wszMessage);

               m_StatusText.SetWindowText(
                           L"Document URL retrieved..."
                              L"Downloading Document."
                           );

               SysFreeString(bstrDocURL);
            }

            if (S_OK == hr)
            {
                CSCPDDisplay dlg(bstrDocument);
                dlg.DoModal();

                m_StatusText.SetWindowText(
                           L"Document Retrieval Completed"
                           );

                SysFreeString(bstrDocument);
            }

            ReleaseObj(pServiceAccess);
         }

         ReleaseObj(pService);
      }
   }

}

//+---------------------------------------------------------------------------
//
//  Member:		OnQueryVariable
//
//  Purpose:    Determines how to query the variable for the selected service in the service list
//
//  Arguments:
//				None
//
//  Returns:    None
//
//  Notes:
//
void CGenericUCPDlg::OnQueryVariable()
{
   CString strVariable;
   int iCurrentDevice, iCurrentService;

   // We are going to query the variable which is present in the variable
   // edit box
   m_ActionOutArgument.SetWindowText(L"");

   iCurrentDevice = m_DeviceCombo.GetCurSel();

   if (iCurrentDevice != CB_ERR)
   {
      iCurrentService = m_ServiceCombo.GetCurSel();
      if (iCurrentService != CB_ERR)
      {
         m_VariableName.GetWindowText(strVariable);
         if (!strVariable.IsEmpty())
         {
            BSTR bstrVariableName = NULL;
            bstrVariableName=strVariable.AllocSysString();
            if (bstrVariableName!=NULL)
            {

               if (BST_CHECKED == m_AsynchControl.GetCheck())
               {
                  OnQueryVariableAsync(bstrVariableName, iCurrentService);
               }
               else
               {
                  OnQueryVariableSync(bstrVariableName, iCurrentService);
               }

               SysFreeString(bstrVariableName);
            }
            else
            {
               MessageBox(L"Error: Could not allocate the memory for BSTR");
            }
         }
         else
         {
            MessageBox(L"Please specify a query variable");
         }
      }
      else
      {
         MessageBox(L"No service selected");
      }
   }
   else
   {
      MessageBox(L"No device selected");
   }
}

//+---------------------------------------------------------------------------
//
//  Member:		OnQueryVariableSync
//
//  Purpose:    Query the variable for the selected service in the service list using the synchronous methods
//
//  Arguments:
//                  bstrVariableName      [in]    Variable name as obtained from the field
//                  iCurrentService         [in]    Denotes which service was selected on the service list
//
//  Returns:    None
//
//  Notes:		Displays the result in the action out edit box.
//
void CGenericUCPDlg::OnQueryVariableAsync(
   _In_ BSTR bstrVariableName,
   _In_ int iCurrentService
)
{
   HRESULT hr=S_OK;
   WCHAR wszMessage[DATA_BUFSIZE];

   VARIANT varValue;
   VariantInit(&varValue);
   IUPnPService* pService = NULL;
   IUPnPServiceAsync * pServiceAsync = NULL;

   pService=(IUPnPService*)m_ServiceCombo.GetItemDataPtr(
                                        iCurrentService
                                        );
   pService->AddRef();

   // Get a IUPnPServiceAsync pointer to the service just got
   hr = pService->QueryInterface(IID_IUPnPServiceAsync, (VOID **)&pServiceAsync);
   if(FAILED(hr))
   {
      m_StatusText.SetWindowText(L"Error: QueryInterface for IUPnPServiceAsync failed.");
   }
   else
   {

      CUPnPAsyncResult * pAsyncResult = NULL;
      HANDLE  searchEvents[2]; // Array of events to wait for. This array is passed as a argument to CoWaitForMultipleHandles().
      DWORD   dwSignalled = 0;
      DWORD   dwWaitTime = INFINITE;
      DWORD   dwEventCount = 2;
      MSG msg;

      pAsyncResult = new CUPnPAsyncResult();
      pAsyncResult->AddRef();
      hr = pAsyncResult->Init();

      if(SUCCEEDED(hr))
      {
         searchEvents[0] = pAsyncResult->GetHandle();
         searchEvents[1] = m_hCloseEvent;

         hr = pServiceAsync->BeginQueryStateVariable(bstrVariableName, pAsyncResult, &m_ullRequestID);
         m_StatusText.SetWindowText(
                                   L"State var value being queried..."
                                      L"Waiting for completion callback."
                                   );

         if(SUCCEEDED(hr))
         {
            while(TRUE)
            {
               // Wait for multiple objects. We should wait for windows UI calls too.
               DWORD dwWaitResult = ::MsgWaitForMultipleObjectsEx( dwEventCount,
                                                                    searchEvents,
                                                                    dwWaitTime,
                                                                    QS_ALLINPUT,
                                                                    MWMO_INPUTAVAILABLE);

               if(WAIT_FAILED == dwWaitResult)
               {
                  DWORD dwError = GetLastError();
                  hr = HRESULT_FROM_WIN32(dwError);
                  m_StatusText.SetWindowText(
                                            L"Wait Failed."
                                            );
                  break;
               }
           else if(WAIT_TIMEOUT == dwWaitResult)
               {
                  m_StatusText.SetWindowText(
                                         L"Timeout period elapsed."
                                            L" Cancelling Query operation now."
                                         );

                  hr = pServiceAsync->CancelAsyncOperation(m_ullRequestID);
                  break;
               }
               else if(WAIT_OBJECT_0 == dwWaitResult)
               {
                  if(0 == dwSignalled)    // Invoke action completion event triggerred..
                  {
                     m_StatusText.SetWindowText(
                                             L"Action successfully invoked."
                                                L" Fetching value now."
                                             );

                     hr = pServiceAsync->EndQueryStateVariable( m_ullRequestID, &varValue  );
                     if (SUCCEEDED(hr))
                     {
                        hr = VariantChangeType(
                                              &varValue,
                                              &varValue,
                                              VARIANT_ALPHABOOL,
                                              VT_BSTR
                                              );

                        if (SUCCEEDED(hr))
                        {
                           _snwprintf_s(
                                       wszMessage,
                                       DATA_BUFSIZE,
                                       _TRUNCATE,
                                       L"%ls",
                                       varValue.bstrVal
                                       );
                           m_ActionOutArgument.SetWindowText(wszMessage);
                        }
                        else
                        {
                           PrintErrorText(hr);
                        }
                     }
                     else
                     {
                        PrintErrorText(hr);
                     }

                  }
                  break;

               }
               else if (WAIT_OBJECT_0 + 1 == dwWaitResult)
               {
                  // Received Close Event. Exit and prepare to close
                  hr = pServiceAsync->CancelAsyncOperation(m_ullRequestID);
                  break;
               }

               // Process any MSG
               if (::PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE))
               {
                  ::TranslateMessage(&msg);
                  ::DispatchMessage(&msg);
                  continue;
               }
            }
         }
         else
         {
            PrintErrorText(hr);
         }
      }
      ReleaseObj(pServiceAsync);
   }

   ReleaseObj(pService);
   VariantClear (&varValue);

}

//+---------------------------------------------------------------------------
//
//  Member:		OnQueryVariableSync
//
//  Purpose:    Query the variable for the selected service in the service list using the synchronous methods
//
//  Arguments:
//                  bstrVariableName      [in]    Variable name as obtained from the field
//                  iCurrentService         [in]    Denotes which service was selected on the service list
//
//  Returns:    None
//
//  Notes:		Displays the result in the action out edit box.
//
void CGenericUCPDlg::OnQueryVariableSync(
   _In_ BSTR bstrVariableName,
   _In_ int iCurrentService
   )
{
   HRESULT hr=S_OK;
   WCHAR wszMessage[DATA_BUFSIZE];

   VARIANT varValue;
   VariantInit(&varValue);
   IUPnPService* pService;
   pService=(IUPnPService*)m_ServiceCombo.GetItemDataPtr(
                                                 iCurrentService
                                                 );
   pService->AddRef();
   hr = pService->QueryStateVariable(bstrVariableName, &varValue);

   if (SUCCEEDED(hr))
   {
      hr = VariantChangeType(
            &varValue,
            &varValue,
            VARIANT_ALPHABOOL,
            VT_BSTR
            );

      if (SUCCEEDED(hr))
      {
         _snwprintf_s(
              wszMessage,
              DATA_BUFSIZE,
              _TRUNCATE,
              L"%ls",
              varValue.bstrVal
              );
         m_ActionOutArgument.SetWindowText(wszMessage);
      }
      else
      {
         PrintErrorText(hr);
      }
   }
   else
       {
          PrintErrorText(hr);
   }

   ReleaseObj(pService);
   VariantClear (&varValue);

}

//+---------------------------------------------------------------------------
//
//  Member:		OnInvokeAction
//
//  Purpose:    Determines how to invoke the action for the selected service in the service list
//
//  Arguments:
//				None
//
//  Returns:    None
//
//  Notes:		Displays the result in the action out edit box.
//
void CGenericUCPDlg::OnInvokeAction()
{
   CString strAction;
   CString strArg;
   WCHAR wszString[DATA_BUFSIZE] = {0};
   WCHAR wszTempString[DATA_BUFSIZE] = {0};
   int iCurrentDevice, iCurrentService;
   int i, iCount;

   // We are going to query the variable which is present in the variable edit box
   m_ActionOutArgument.SetWindowText(L"");

   iCurrentDevice=m_DeviceCombo.GetCurSel();
   if(iCurrentDevice!=CB_ERR)
   {
      iCurrentService=m_ServiceCombo.GetCurSel();
      if(iCurrentService!=CB_ERR)
      {
         m_ActionName.GetWindowText(strAction);
         if(!strAction.IsEmpty())
         {
            BSTR bstrActionName = NULL;
            bstrActionName=strAction.AllocSysString();
            if(bstrActionName!=NULL)
            {
               m_ActionInArgument.GetWindowText(
                                    wszString,
                                    DATA_BUFSIZE - 1
                                    );

               // Make a copy of the read string so that the main string
               // is not modified
               SafeStrCopy(wszTempString, wszString, DATA_BUFSIZE);

               int iArgs = iNumberOfInArguments(wszTempString); // Get the number of arguments in the inArguments edit box

               VARIANT  vaActionArgs, vaArray;
               VARIANT**          ppVars = NULL;
               SAFEARRAY          *psaArgs = NULL;
               long               lPos;

               //Initialize all the variants
               VariantInit(&vaActionArgs);
               VariantInit(&vaArray);

               if(!(FAILED(HrCreateSafeArray(VT_VARIANT, iArgs, &psaArgs))||
                     FAILED(HrCreateArgVariants(iArgs, &ppVars))))
               {
                  VariantSetArray(psaArgs, vaArray);
                  VariantSetVar(&vaArray, vaActionArgs);
                  vaArray.pparray=&psaArgs;

                  // Get the in arguments
                  // Put the arguments as strings

                  WCHAR *token = NULL;
                  WCHAR *context = NULL;
                  iCount=0; // Initialize the counter
                  token = wcstok_s(wszString, L" ", &context);
                  while((NULL != token) && (iCount < iArgs) && (NULL != ppVars))
                  {
                     ASSERT(iCount<iArgs); // Make sure
                     strArg=token;
                     ppVars[iCount]->vt=VT_BSTR;
                     ppVars[iCount]->bstrVal=strArg.AllocSysString(); // Converting the argument into a BSTR
                     token = wcstok_s(NULL, L" ", &context); // We have the space character as the separator
                     iCount++;
                  }

                  for(i=0; i<iArgs; i++)
                  {
                     lPos=i+1;
                     if (NULL != ppVars)
                     {
                         SafeArrayPutElement(psaArgs, &lPos, ppVars[i]);
                     }
                  }

                  if (BST_CHECKED == m_AsynchControl.GetCheck())
                  {
                     OnInvokeAsyncAction(bstrActionName, iCurrentService, vaActionArgs);
                  }
                  else
                  {
                     OnInvokeSyncAction(bstrActionName, iCurrentService, vaActionArgs);
                  }

                  if(NULL != ppVars)
                     HrDestroyArgVariants(iArgs, &ppVars);
                  if(NULL != psaArgs)
                  {
                     SafeArrayDestroy(psaArgs);
                  }
               }

               SysFreeString(bstrActionName);
            }
            else
            {
               MessageBox(L"Error: Could not allocate the memory for BSTR");
            }
         }
         else
         {
            MessageBox(L"Please specify an action name");
         }
      }
      else
      {
         MessageBox(L"No service selected");
      }
   }
   else
   {
      MessageBox(L"No device selected");
   }
}

//+---------------------------------------------------------------------------
//
//  Member:		OnInvokeAsyncAction
//
//  Purpose:    Asynchronously invoke the action for the selected service in the service list
//
//  Arguments:
//                  bstrVariableName      [in]    Variable name as obtained from the field
//                  iCurrentService         [in]    Denotes which service was selected on the service list
//                  vaActionArgs             [in]    Contains all of the input arguments for InvokeAction
//
//  Returns:    None
//
//  Notes:		Displays the result in the action out edit box.
//
void CGenericUCPDlg::OnInvokeAsyncAction(
   _In_ BSTR bstrActionName,
   _In_ int iCurrentService,
   _In_ VARIANT vaActionArgs
   )
{
   HRESULT hr=S_OK;
   WCHAR wszString[DATA_BUFSIZE];
   WCHAR wszTempString[DATA_BUFSIZE];
   long lIndex;
   VARIANT vaOutArgs, vaRet, vaOutElement;
   long    lLBound = 0L, lUBound = 0L; // For out arguments

   //Initialize all the variants
   VariantInit(&vaOutArgs);
   VariantInit(&vaRet);
   VariantInit(&vaOutElement);

   IUPnPService * pService = NULL;
   IUPnPServiceAsync * pServiceAsync = NULL;

   pService = (IUPnPService *)m_ServiceCombo.GetItemDataPtr(iCurrentService);
   pService->AddRef();

   // Get a IUPnPServiceAsync pointer to the service just got
   hr = pService->QueryInterface(IID_IUPnPServiceAsync, (VOID **)&pServiceAsync);
   if(FAILED(hr))
   {
      m_StatusText.SetWindowText(L"Error: QueryInterface for IUPnPServiceAsync failed.");
   }

   if(SUCCEEDED(hr))
   {

      CUPnPAsyncResult * pAsyncResult = NULL;
      HANDLE  searchEvents[2]; // Array of events to wait for. This array is passed as a argument to CoWaitForMultipleHandles().
      DWORD   dwSignalled = 0;
      DWORD   dwWaitTime = INFINITE;
      DWORD   dwEventCount = 2;
      MSG msg;

      pAsyncResult = new CUPnPAsyncResult();
      pAsyncResult->AddRef();
      hr = pAsyncResult->Init();

      if(SUCCEEDED(hr))
         {
         searchEvents[0] = pAsyncResult->GetHandle();
         searchEvents[1] = m_hCloseEvent;

         hr = pServiceAsync->BeginInvokeAction(bstrActionName,
                                               vaActionArgs,
                                               pAsyncResult,
                                               &m_ullRequestID);

         m_StatusText.SetWindowText(
                              L"Action being invoked..."
                               L"Waiting for action completion callback."
                              );

         if(SUCCEEDED(hr))
         {
            while(TRUE)
            {
               // Wait for multiple objects. We should wait for windows UI calls too.
               DWORD dwWaitResult = ::MsgWaitForMultipleObjectsEx( dwEventCount,
                                                                   searchEvents,
                                                                   dwWaitTime,
                                                                   QS_ALLINPUT,
                                                                   MWMO_INPUTAVAILABLE);

               if(WAIT_FAILED == dwWaitResult)
               {
                  DWORD dwError = GetLastError();
                  hr = HRESULT_FROM_WIN32(dwError);
                  m_StatusText.SetWindowText(
                                           L"Wait Failed."
                                           );
                  break;
               }
               else if(WAIT_TIMEOUT == dwWaitResult)
               {
                  m_StatusText.SetWindowText(
                                           L"Timeout period elapsed."
                                              L" Cancelling invoke action operation now."
                                           );
                  hr = pServiceAsync->CancelAsyncOperation(m_ullRequestID);
                  break;
               }
               else if(WAIT_OBJECT_0 == dwWaitResult)
               {
                  if(0 == dwSignalled)    // Invoke action completion event triggerred..
                  {
                     m_StatusText.SetWindowText(
                                               L"Action successfully invoked."
                                                  L" Fetching output arguments now."
                                               );

                     hr = pServiceAsync->EndInvokeAction( m_ullRequestID, &vaOutArgs, &vaRet  );

                     if(SUCCEEDED(hr))
                     {

                        // We have to print the return value and out arguments
                        *wszString=0; // Empty the string
                        *wszTempString=0; // Empty the string

                        if(vaRet.vt!=VT_EMPTY)
                        {
                           hr=VariantChangeType(&vaRet, &vaRet, VARIANT_ALPHABOOL, VT_BSTR);
                           if(SUCCEEDED(hr))
                           {
                              _snwprintf_s(
                                    wszTempString,
                                    DATA_BUFSIZE,
                                    _TRUNCATE,
                                    L"%ls ",
                                    vaRet.bstrVal
                                    );
                              wcsncat_s(
                                    wszString,
                                    DATA_BUFSIZE,
                                    wszTempString,
                                    DATA_BUFSIZE - 1
                                    );
                           }
                           else
                           {
                              PrintErrorText(hr);
                              m_StatusText.SetWindowText(
                                   L"Error getting the return value for"
                                      L" the action invoked"
                                   );
                           }
                        }

                        // We have to process the out arguments now
                        hr = HrGetSafeArrayBounds(
                                vaOutArgs.parray,
                                &lLBound,
                                &lUBound
                                );
                        if(SUCCEEDED(hr))
                        {
                           // We have got the bounds of the arguments
                           for (lIndex = lLBound;
                                lIndex<=lUBound;
                                ++lIndex
                                )
                           {
                              VariantClear(&vaOutElement);
                              hr = HrGetVariantElement(
                                        vaOutArgs.parray,
                                        lIndex,
                                        &vaOutElement
                                        );
                              if(SUCCEEDED(hr))
                              {
                                 hr = VariantChangeType(
                                         &vaOutElement,
                                         &vaOutElement,
                                         VARIANT_ALPHABOOL,
                                         VT_BSTR
                                         );
                                 if (SUCCEEDED(hr))
                                 {
                                    _snwprintf_s(
                                        wszTempString,
                                        DATA_BUFSIZE,
                                        _TRUNCATE,
                                        L"%ls ",
                                        vaOutElement.bstrVal
                                        );
                                     wcsncat_s(
                                         wszString,
                                         DATA_BUFSIZE,
                                         wszTempString,
                                         (DATA_BUFSIZE -
                                          wcslen(wszString) - 1)
                                         );
                                 }
                                 else
                                 {
                                    PrintErrorText(hr);
                                    m_StatusText.SetWindowText(
                                        L"Error changing the out"
                                           L" argument type for the"
                                           L" action invoked"
                                          );
                                 }
                              }
                              else
                              {
                                 m_StatusText.SetWindowText(L"Error getting the out argument for the action invoked");
                                 PrintErrorText(hr);
                              }
                           } // For loop

                           VariantClear(&vaOutElement);
                        }

                        m_ActionOutArgument.SetWindowText(wszString); // Print the return value and out arguments.
                     }
                     else
                     {
                        PrintErrorText(hr);
                     }

                  }

                  break;

               }
               else if (WAIT_OBJECT_0 + 1 == dwWaitResult)
               {
                  // Received Close Event. Exit and prepare to close
                  hr = pServiceAsync->CancelAsyncOperation(m_ullRequestID);
                  break;
               }

               // Process any MSG
               if (::PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE))
               {
                  ::TranslateMessage(&msg);
                  ::DispatchMessage(&msg);
                  continue;
               }

            }
         }
         else
         {
            PrintErrorText(hr);
         }
      }
      ReleaseObj(pServiceAsync);
   }
   else
   {
      PrintErrorText(hr);
   }

   ReleaseObj(pService);

   VariantClear(&vaOutArgs);
   VariantClear(&vaRet);
   VariantClear(&vaOutElement);
}

//+---------------------------------------------------------------------------
//
//  Member:		OnInvokeSyncAction
//
//  Purpose:    Synchronously invoke the action for the selected service in the service list
//
//  Arguments:
//                  bstrVariableName      [in]    Variable name as obtained from the field
//                  iCurrentService         [in]    Denotes which service was selected on the service list
//                  vaActionArgs             [in]    Contains all of the input arguments for InvokeAction
//
//  Returns:    None
//
//  Notes:		Displays the result in the action out edit box.
//

void CGenericUCPDlg::OnInvokeSyncAction(
   _In_ BSTR bstrActionName,
   _In_ int iCurrentService,
   _In_ VARIANT vaActionArgs
   )
{
   HRESULT hr=S_OK;
   WCHAR wszString[DATA_BUFSIZE];
   WCHAR wszTempString[DATA_BUFSIZE];
   long lIndex;
   VARIANT vaOutArgs, vaRet, vaOutElement;
   long    lLBound = 0L, lUBound = 0L; // For out arguments

   //Initialize all the variants
   VariantInit(&vaOutArgs);
   VariantInit(&vaRet);
   VariantInit(&vaOutElement);

   IUPnPService *pService=NULL;
   pService = (IUPnPService *)m_ServiceCombo.GetItemDataPtr(iCurrentService);
   pService->AddRef();
   hr = pService->InvokeAction( bstrActionName,
                                vaActionArgs,
                                &vaOutArgs,
                                &vaRet);

   if(SUCCEEDED(hr))
   {
      // We have to print the return value and out arguments
      *wszString=0; // Empty the string
      *wszTempString=0; // Empty the string

      if(vaRet.vt!=VT_EMPTY)
      {
         hr=VariantChangeType(&vaRet, &vaRet, VARIANT_ALPHABOOL, VT_BSTR);
         if(SUCCEEDED(hr))
         {
            _snwprintf_s(
                   wszTempString,
                   DATA_BUFSIZE,
                   _TRUNCATE,
                   L"%ls ",
                   vaRet.bstrVal
                   );
            wcsncat_s(
                   wszString,
                   DATA_BUFSIZE,
                   wszTempString,
                   DATA_BUFSIZE - 1
                   );
         }
         else
         {
            PrintErrorText(hr);
            m_StatusText.SetWindowText(
               L"Error getting the return value for"
                  L" the action invoked"
               );
         }
      }

      // We have to process the out arguments now
      hr = HrGetSafeArrayBounds(
            vaOutArgs.parray,
            &lLBound,
            &lUBound
            );
      if(SUCCEEDED(hr))
      {
         // We have got the bounds of the arguments
         for (lIndex = lLBound;
               lIndex<=lUBound;
               ++lIndex
               )
         {
            VariantClear(&vaOutElement);
            hr = HrGetVariantElement(
                    vaOutArgs.parray,
                    lIndex,
                    &vaOutElement
                    );
            if(SUCCEEDED(hr))
            {
               hr = VariantChangeType(
                     &vaOutElement,
                     &vaOutElement,
                     VARIANT_ALPHABOOL,
                     VT_BSTR
                     );
               if (SUCCEEDED(hr))
               {
                  _snwprintf_s(
                       wszTempString,
                       DATA_BUFSIZE,
                       _TRUNCATE,
                       L"%ls ",
                       vaOutElement.bstrVal
                       );
                  wcsncat_s(
                        wszString,
                        DATA_BUFSIZE,
                        wszTempString,
                        (DATA_BUFSIZE -
                         wcslen(wszString) - 1)
                     );
               }
               else
               {
                  PrintErrorText(hr);
                  m_StatusText.SetWindowText(
                    L"Error changing the out"
                       L" argument type for the"
                       L" action invoked"
                       );
               }
            }
            else
            {
               m_StatusText.SetWindowText(L"Error getting the out argument for the action invoked");
               PrintErrorText(hr);
            }
         } // For loop

         VariantClear(&vaOutElement);
      }

      m_ActionOutArgument.SetWindowText(wszString); // Print the return value and out arguments.
   }
   else
   {
      PrintErrorText(hr);
   }
   ReleaseObj(pService);

   VariantClear(&vaOutArgs);
   VariantClear(&vaRet);
   VariantClear(&vaOutElement);
}

//+---------------------------------------------------------------------------
//
//  Member:		OnCloseClick
//
//  Purpose:    Close the dialog
//
//  Arguments:
//				None
//
//  Returns:    None
//
//  Notes:
//

void CGenericUCPDlg::OnCloseClick()
{
   OnClose();
   EndDialog(1);
   return;
}

//+---------------------------------------------------------------------------
//
//  Member:		OnCheckBoxClick
//
//  Purpose:    A Checkbox has been clicked so if an async find is running, it will be stopped
//
//  Arguments:
//				None
//
//  Returns:    None
//
//  Notes:
//

void CGenericUCPDlg::OnCheckBoxClick()
{
   StopAsyncFindIfStarted();
}

//+---------------------------------------------------------------------------
//
//  Member:		iNumberOfInArguments
//
//  Purpose:    Calculate the number of arguments in action box
//
//  Arguments:
//				tszString	[in]	Arguments string
//
//  Returns:    None
//
//  Notes:
//

int CGenericUCPDlg::iNumberOfInArguments(_In_ LPWSTR tszString)
{
   int iCount = 0;
   WCHAR *token = NULL;
   WCHAR *context = NULL;
   token = wcstok_s(tszString, L" ", &context);
   while(token!=NULL){
      iCount++;
      token = wcstok_s(NULL, L" ", &context);
   }
   return iCount;
}

///////////////////////////////////////////////////////////////////
//   CDeviceFinderCallback
///////////////////////////////////////////////////////////////////

CDeviceFinderCallback::CDeviceFinderCallback()
{
    m_cRef = 0;
}

CDeviceFinderCallback::~CDeviceFinderCallback()
{
}

/////////////////////////////////////////////////////////////////////////////
// IUnknown Functions

HRESULT __stdcall CDeviceFinderCallback::QueryInterface(_In_ const IID& iid, _Out_ void** ppv)
{
    if (iid == IID_IUPnPDeviceFinderCallback)
    {
        *ppv = static_cast<IUPnPDeviceFinderCallback*>(this);
    }
    else if (iid == IID_IUnknown)
    {
        *ppv = static_cast<IUnknown*>(this);
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    static_cast<CDeviceFinderCallback*>(*ppv)->AddRef();

    return S_OK;

}

ULONG __stdcall CDeviceFinderCallback::AddRef()
{
    return InterlockedIncrement(&m_cRef);
}

ULONG __stdcall CDeviceFinderCallback::Release()
{
    int iCount = InterlockedDecrement(&m_cRef);
    if (iCount == 0)
    {
        delete this;
    }

    return iCount;
}

//+---------------------------------------------------------------------------
//
//  Member:		Create
//
//  Purpose:    Create the Device Finder callback object
//
//  Arguments:
//				None
//
//  Returns:    None
//
//  Notes:		This is a static function
//

CDeviceFinderCallback* CDeviceFinderCallback::Create()
{
   CDeviceFinderCallback * pCallback = new CDeviceFinderCallback();
   pCallback->AddRef();
   return pCallback;
}

//+---------------------------------------------------------------------------
//
//  Member:		SetDialogPointer
//
//  Purpose:    Set the CGenericUCPDlg Dialog Pointer to call back
//
//  Arguments:
//				None
//
//  Returns:    None
//
//  Notes:
//

void CDeviceFinderCallback::SetDialogPointer(CGenericUCPDlg* pDialog){
   m_pGenericUCPDlg=pDialog;
}

//+---------------------------------------------------------------------------
//
//  Member:		DeviceAdded
//
//  Purpose:    Called when a device is added
//
//  Arguments:
//				lFindData	       [in]	AsyncFindHandle
//				pDevice		[in]	COM interface pointer of the device being added
//
//  Returns:    S_OK
//
//  Notes:
//

HRESULT CDeviceFinderCallback::DeviceAdded(
   _In_ LONG            lFindData,
   _In_ IUPnPDevice *   pDevice)
{
   UNREFERENCED_PARAMETER(lFindData);

   HRESULT HR = S_OK;
   TRACE(L"Device Added\n");
   m_pGenericUCPDlg->AddDevice(pDevice);
   return HR;
}

//+---------------------------------------------------------------------------
//
//  Member:		DeviceRemoved
//
//  Purpose:    Called when a device is removed
//
//  Arguments:
//				lFindData	       [in]	AsyncFindHandle
//				bstrUDN		[in]	UDN of the device being removed
//
//  Returns:    S_OK
//
//  Notes:
//

HRESULT CDeviceFinderCallback::DeviceRemoved(
   _In_ LONG    lFindData,
   _In_ BSTR    bstrUDN)
{
   UNREFERENCED_PARAMETER(lFindData);

   TRACE(L"Device Removed\n");
   m_pGenericUCPDlg->RemoveDevice(bstrUDN);
   return S_OK;
}

//+---------------------------------------------------------------------------
//
//  Member:		SearchComplete
//
//  Purpose:    Called when the search is complete
//
//  Arguments:
//				lFindData	       [in]	AsyncFindHandle
//
//  Returns:    S_OK
//
//  Notes:
//
HRESULT CDeviceFinderCallback::SearchComplete(
   _In_ LONG    lFindData)
{
    UNREFERENCED_PARAMETER(lFindData);

    TRACE(L"Search Complete\n");
   m_pGenericUCPDlg->m_StatusText.SetWindowText(L"Device Search Completed");

   if(m_pGenericUCPDlg->m_DeviceCombo.GetCount()==0){
      m_pGenericUCPDlg->MessageBox(L"Found no devices");
   }
   return S_OK;
}

///////////////////////////////////////////////////////////////////
//   CServiceCallback
///////////////////////////////////////////////////////////////////

CServiceCallback::CServiceCallback()
{
    m_cRef = 0;
}

CServiceCallback::~CServiceCallback()
{
}

/////////////////////////////////////////////////////////////////////////////
// IUnknown Functions

HRESULT __stdcall CServiceCallback::QueryInterface(_In_ const IID& iid, _Out_ void** ppv)
{
    if (iid == IID_IUPnPServiceCallback)
    {
        *ppv = static_cast<IUPnPServiceCallback*>(this);
    }
    else if (iid == IID_IUnknown)
    {
        *ppv = static_cast<IUnknown*>(this);
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    static_cast<CServiceCallback*>(*ppv)->AddRef();

    return S_OK;

}

ULONG __stdcall CServiceCallback::AddRef()
{
    return InterlockedIncrement(&m_cRef);
}

ULONG __stdcall CServiceCallback::Release()
{
    int iCount = InterlockedDecrement(&m_cRef);
    if (iCount == 0)
    {
        delete this;
    }

    return iCount;
}

//+---------------------------------------------------------------------------
//
//  Member:		Create
//
//  Purpose:    Create the Device Finder callback object
//
//  Arguments:
//				None
//
//  Returns:    None
//
//  Notes:		This is a static function
//

CServiceCallback* CServiceCallback::Create()
{
   CServiceCallback * pCallback = new CServiceCallback();
   pCallback->AddRef();
   return pCallback;
}

//+---------------------------------------------------------------------------
//
//  Member:		SetDialogPointer
//
//  Purpose:    Set the CGenericUCPDlg Dialog Pointer to call back
//
//  Arguments:
//				None
//
//  Returns:    None
//
//  Notes:
//

void CServiceCallback::SetDialogPointer(CGenericUCPDlg* pDialog){
   m_pGenericUCPDlg=pDialog;
}

//+---------------------------------------------------------------------------
//
//  Member:		StateVariableChanged
//
//  Purpose:    Called when the state variable is changed
//
//  Arguments:
//				pus					[in]	COM interface pointer of the service
//				pcwszStateVarName	[in]	State Variable Name
//				varValue			       [in]	State Variable Value
//
//  Returns:    HRESULT
//
//  Notes:
//

HRESULT CServiceCallback::StateVariableChanged(
   _In_ IUPnPService *pus,
   _In_ LPCWSTR pcwszStateVarName,
   _In_ VARIANT varValue)
{
   HRESULT hr = S_OK;
   WCHAR wszMessage[DATA_BUFSIZE];
   BSTR bstrServiceId = NULL;
   VARIANT varTemp;
   TRACE(L"State Variable Changed\n");

   VariantInit(&varTemp);

   hr = pus->get_Id(&bstrServiceId);
   if(SUCCEEDED(hr)){
      hr = VariantCopy(&varTemp, &varValue);
      if (SUCCEEDED(hr)){
         hr=VariantChangeType(&varTemp, &varTemp, VARIANT_ALPHABOOL, VT_BSTR);
         if(SUCCEEDED(hr)){
            _snwprintf_s(wszMessage, DATA_BUFSIZE, _TRUNCATE, L"State variable %ls changed to %ls in %ls", pcwszStateVarName, varTemp.bstrVal, bstrServiceId);
            Sleep(350);
            m_pGenericUCPDlg->m_EventText.SetWindowText(wszMessage);
         }
         else{
            PrintErrorText(hr);
         }
      }
      SysFreeString(bstrServiceId);
   }
   else{
      PrintErrorText(hr);
      m_pGenericUCPDlg->m_StatusText.SetWindowText(L"Error: ServiceId failed");
   }

   VariantClear(&varTemp);
   return hr;
}

//+---------------------------------------------------------------------------
//
//  Member:		ServiceInstanceDied
//
//  Purpose:    Called when the service dies
//
//  Arguments:
//				pus					[in]	COM interface pointer of the service
//
//  Returns:    HRESULT
//
//  Notes:
//

HRESULT CServiceCallback::ServiceInstanceDied(
   _In_ IUPnPService *pus)
{
   HRESULT hr=S_OK;
   WCHAR wszMessage[DATA_BUFSIZE];
   TRACE(L"Service instance died\n");

   BSTR bstrServiceId = NULL;
   hr = pus->get_Id(&bstrServiceId);
   if(SUCCEEDED(hr)){
      _snwprintf_s(wszMessage, DATA_BUFSIZE, _TRUNCATE, L"Service %ls died",bstrServiceId);
      m_pGenericUCPDlg->m_EventText.SetWindowText(wszMessage);
      SysFreeString(bstrServiceId);
   }
   else{
      PrintErrorText(hr);
      m_pGenericUCPDlg->m_StatusText.SetWindowText(L"Error: ServiceId failed");
   }
   return hr;
}

//+---------------------------------------------------------------------------
//
//  Member:		OnClose
//
//  Purpose:    Called when the dialog box closes
//
//  Arguments:
//				None
//
//  Returns:    S_OK
//
//  Notes:
//

void CGenericUCPDlg::OnClose()
{
   SetEvent(m_hCloseEvent);
   ClearAllDataStructures(TRUE);

   CDialog::OnClose();
}

{'repo_name': 'microsoft/Windows-classic-samples', 'stars': '2089', 'repo_language': 'None', 'file_name': 'ialtertable.cpp', 'mime_type': 'text/x-c++', 'hash': 7850811193683780811, 'source_dataset': 'data'}


#ifndef UAE_FLASHROM_H
#define UAE_FLASHROM_H

#include "uae/types.h"

/* FLASH */

void *flash_new(uae_u8 *rom, int flashsize, int allocsize, uae_u8 mfgcode, uae_u8 devcode, struct zfile *zf, int flags);
void flash_free(void *fdv);

bool flash_write(void *fdv, uaecptr addr, uae_u8 v);
uae_u32 flash_read(void *fdv, uaecptr addr);
bool flash_active(void *fdv, uaecptr addr);
int flash_size(void *fdv);

/* I2C EEPROM */

#define BITBANG_I2C_SDA 0
#define BITBANG_I2C_SCL 1

void *eeprom_new(uae_u8 *rom, int size, struct zfile *zf);
void eeprom_free(void *i2c);
void eeprom_reset(void *i2c);
int eeprom_i2c_set(void *i2c, int line, int level);

#define FLASHROM_EVERY_OTHER_BYTE 1
#define FLASHROM_EVERY_OTHER_BYTE_ODD 2
#define FLASHROM_PARALLEL_EEPROM 4

void *i2c_new(uae_u8 device_address, int size, uae_u8(*read_func)(uae_u8 addr), void(*write_func)(uae_u8 addr, uae_u8 v));
void i2c_free(void *i2c);
int i2c_set(void *i2c, int line, int level);
void i2c_reset(void *i2c);

/* MICROWIRE EEPROM */

void eeprom93xx_write(void *eepromp, int eecs, int eesk, int eedi);
uae_u16 eeprom93xx_read(void *eepromp);
void *eeprom93xx_new(const uae_u8 *memory, int nwords, struct zfile *zf);
void eeprom93xx_free(void *eepromp);
uae_u8 eeprom93xx_read_byte(void *eepromp, int offset);

#endif /* UAE_FLASHROM_H */

{'repo_name': 'FrodeSolheim/fs-uae', 'stars': '278', 'repo_language': 'C++', 'file_name': 'format', 'mime_type': 'text/plain', 'hash': -7278351685687783737, 'source_dataset': 'data'}


require 'test_helper'

class PageUnlockPromoCodeTest < ActiveSupport::TestCase
  # test "the truth" do
  #   assert true
  # end
end

{'repo_name': 'indentlabs/notebook', 'stars': '236', 'repo_language': 'Ruby', 'file_name': 'scribe.html', 'mime_type': 'text/html', 'hash': -8444629898242250682, 'source_dataset': 'data'}


// $Id: SSLv2.h 3526 2006-09-12 07:32:21Z vern $

#ifndef SSLV2_H
#define SSLV2_H

#include "SSLInterpreter.h"
#include "SSLCiphers.h"

// --- constants for SSLv2 ---------------------------------------------------

/*!
 * In SSLv2, each record is of a special message type. Note that the message
 * type is encrypted if the record has been encrypted, so we can determine
 * the message type only if we have a cleartext record.
 */
enum SSLv2_MessageTypes {
	SSLv2_MT_ERROR = 0,	///< can be in cleartext or encrypted
	SSLv2_MT_CLIENT_HELLO = 1,	///< always in cleartext
	SSLv2_MT_CLIENT_MASTER_KEY = 2,	///< always in cleartext
	SSLv2_MT_CLIENT_FINISHED = 3,	///< always encrypted
	SSLv2_MT_SERVER_HELLO = 4,	///< always in cleartext
	SSLv2_MT_SERVER_VERIFY = 5,	///< always encrypted
	SSLv2_MT_SERVER_FINISHED = 6,	///< always encrypted
	SSLv2_MT_REQUEST_CERTIFICATE = 7,	///< always encrypted
	SSLv2_MT_CLIENT_CERTIFICATE = 8,	///< always encrypted
};

// Certificate Type Codes.
//
// Authentication Type Codes
// #define SSL_AT_MD5_WITH_RSA_ENCRYPTION		0x01
// Upper/Lower Bounds
// #define SSL_MAX_MASTER_KEY_LENGTH_IN_BITS	256
// #define SSL_MAX_SESSION_ID_LENGTH_IN_BYTES	16
// #define SSL_MIN_RSA_MODULUS_LENGTH_IN_BYTES	64
// #define SSL_MAX_RECORD_LENGTH_2_BYTE_HEADER	32767
// #define SSL_MAX_RECORD_LENGTH_3_BYTE_HEADER	16383

const uint8 SSLv2_CT_X509_CERTIFICATE = 0x01;

/*!
 * Error codes used in the error record.
 */
enum SSLv2_ErrorCodes {
	SSLv2_PE_NO_CIPHER                    = 0x0001,
	SSLv2_PE_NO_CERTIFICATE               = 0x0002,
	SSLv2_PE_BAD_CERTIFICATE              = 0x0004,
	SSLv2_PE_UNSUPPORTED_CERTIFICATE_TYPE = 0x0006
};

// --- structs ----------------------------------------------------------------

const int SSLv2_CLIENT_HELLO_HEADER_SIZE = 9;
struct SSLv2_ClientHelloHeader {
	uint8  messageType;
	uint16 clientVersion;
	uint16 cipherSpecLength;
	uint16 sessionIdLength;
	uint16 challengeLength;
};

const int SSLv2_SERVER_HELLO_HEADER_SIZE = 11;
struct SSLv2_ServerHelloHeader {
	uint8  messageType;
	uint8  sessionIdHit;
	uint8  certificateType;
	uint16 serverVersion;
	uint16 certificateLength;
	uint16 cipherSpecLength;
	uint16 connectionIdLength;
};

const int SSLv2_CLIENT_MASTER_KEY_HEADER_SIZE = 10;
struct SSLv2_ClientMasterKeyHeader {
	uint8  messageType;
	uint32 cipherKind; // caution: is an uint24
	uint16 clearKeyLength;
	uint16 encryptedKeyLength;
	uint16 keyArgLength;
};

const unsigned int SSLv2_ERROR_RECORD_SIZE = 3;
struct SSLv2_ErrorRecord {
	uint8  messageType;
	uint16 errorCode;
};

const unsigned int SSLv2_CLIENT_FINISHED_HEADER_SIZE = 1;
struct SSLv2_ClientFinished {
	uint8 messageType;
	//char CONNECTION-ID[N-1]
};

struct SSLv2_ServerVerify {
	uint8 messageType;
	//char CHALLENGE-DATA[N-1]
};

struct SSLv2_ServerFinished {
	uint8 messageType;
	//char SESSION-ID-DATA[N-1]
};

// MISSING:
// CLIENT-CERTIFICATE
// REQUEST-CERTIFICATE

/*!
 * States used by the internal SSLv2 automaton.
 */
enum SSLv2_States {
	START,                 ///< start state, no data seen yet
	CLIENT_HELLO_SEEN,     ///< client hello flew by
	NEW_SESSION,           ///< server hello with sessionIdHit == 0 seen
	CACHED_SESSION,        ///< server hello with sessionIdHit != 0 seen
	CLIENT_MASTERKEY_SEEN, ///< we saw a client master key record
	ERROR_SEEN,            ///< we saw an error record
	ERROR_REQUIRED         ///< one of our critical checks failed, so we think we should see an error record
};

// --- forward declarations ---------------------------------------------------

class SSLv2_Interpreter;
class SSLv2_Endpoint;
class SSLv2_Record;
class SSL_DataBlock;
class SSL_RecordBuilder;

// --- class SSLv2_Interpreter ------------------------------------------------

/*!
 * \brief This class is used to analyze SSLv2 connections.
 *
 * Since there's currently no support for decrypting ssl connections, analysis
 * stops when a connection switches to encrypted communication.
 * The interpreter does several checks, both record- and connection-orientated.
 *
 * The record checks mainly consist of consistency checks, where the correct
 * use of the SSL 2.0 specification is checked. Furthermore, the CIPHER-SPECS
 * of the client and the server can be compared to detect non-intersecting sets.
 *
 * The connection check monitors the handshaking process for invalid transitions,
 * until the end of the cleartext phase.
 *
 * Several events are thrown for BroScript, including client connection attempt,
 * server reply, ssl connection establishment/reuse of former connection, proposed
 * cipher suites and certificates seen.
 *
 * \see SSLv2_Endpoint
 */
class SSLv2_Interpreter : public SSL_Interpreter {
public:
	SSLv2_Interpreter(SSLProxy_Analyzer* proxy);
	~SSLv2_Interpreter();

	void NewSSLRecord(SSL_InterpreterEndpoint* s, int length, const u_char* data);
	void analyzeRecord(SSL_InterpreterEndpoint* s, int length, const u_char* data);
	SSLv2_States ClientHelloRecord(SSL_InterpreterEndpoint* s,
	                                int recordLength,
	                                const u_char* recordData);
	SSLv2_States ServerHelloRecord(SSL_InterpreterEndpoint* s,
	                                int recordLength, const u_char* recordData);
	SSLv2_States ClientMasterKeyRecord(SSL_InterpreterEndpoint* s,
	                                    int recordLength,
	                                    const u_char* recordData);
	SSLv2_States ErrorRecord(SSL_InterpreterEndpoint* s,
	                          int recordLength,
	                          const u_char* recordData);

	TableVal* analyzeCiphers(SSL_InterpreterEndpoint* s,
					int length, const u_char* data);
	SSLv2_States ConnState();

	static void printStats();

#define MAX_CIPHERSPEC_SIZE ssl_max_cipherspec_size

	// Global connection counters.
	static uint totalConnections;	///< counter for total sslv2 connections
	static uint analyzedConnections;	///< counter for analyzed (=not partial) connections
	static uint openedConnections;	///< counter for SSLv2 connections with complete handshake
	static uint failedConnections;	///< counter for SSLv2 connections with failed but correct handshake
	static uint weirdConnections;	///< counter for SSLv2 connections with failed and weird handshake

	// Global record counters.
	static uint totalRecords;	///< counter for total SSLv2 records seen
	static uint clientHelloRecords;	///< counter for SSLv2 CLIENT-HELLOs seen
	static uint serverHelloRecords;	///< counter for SSLv2 SERVER-HELLOs seen
	static uint clientMasterKeyRecords;	///< counter for SSLv2 CLIENT-MASTER-KEYSs seen
	static uint errorRecords;	///< counter for SSLv2 ERRORs seen

	// Counters for this instance.
	uint32 records;	///< counter for SSLv2 records of this connection
	SSLv2_States connState;	///< state of connection

	bool bAnalyzedCounted;	///< flag for counting analyzedConnections

	// FIXME: this should be states.
	bool bClientWantsCachedSession;	///< true if the client wants a cached session, false otherwise

protected:
	void BuildInterpreterEndpoints();

	SSL_DataBlock* pClientCipherSpecs;	///< the CIPHER-SPECs from the client
	SSL_DataBlock* pServerCipherSpecs;	///< the CIPHER-SPECs from the server
	SSLv2_CipherSpec usedCipherSpec;	///< the used CIPHER-SPEC for this connection

	// Currently experimental:
	SSL_DataBlock* pConnectionId;	// 16 <= ConnectionId <= 32
	SSL_DataBlock* pChallenge;	// 16 <= Challenge <= 32
	SSL_DataBlock* pSessionId;	// has to be 16 Bytes
	SSL_DataBlock* pMasterClearKey;
	SSL_DataBlock* pMasterEncryptedKey;
	SSL_DataBlock* pClientReadKey;
	SSL_DataBlock* pServerReadKey;
};

// --- class SSLv2_Endpoint ---------------------------------------------------

/*!
 * \brief This class represents an endpoint of an SSLv2 connection.
 *
 * Fully reassembled SSLv2 records are passed to its Deliver() function.
 * There, some counters are updated and the record is then passed to
 * SSLv2_Interpreter::NewSSLRecord().
 */
class SSLv2_Endpoint: public SSL_InterpreterEndpoint {
public:
	SSLv2_Endpoint(SSLv2_Interpreter* interpreter, int is_orig);
	virtual ~SSLv2_Endpoint();

	void Deliver(int len, const u_char* data);

	uint32 sentRecords; ///< counter for sent records of this endpoint
};

#endif

{'repo_name': 'ewust/telex', 'stars': '132', 'repo_language': 'C++', 'file_name': 'ssl.h', 'mime_type': 'text/x-c', 'hash': -7112733344484401118, 'source_dataset': 'data'}