Example #1
0
    Variable Object::CreateVariable(const NodeId& newVariableId, const QualifiedName& browseName, const Variant& value)
    {
      // Creating new node for object
      AddNodesItem newNodeRequest;
      newNodeRequest.BrowseName = browseName;
      newNodeRequest.RequestedNewNodeId = newVariableId;
      newNodeRequest.Class = NodeClass::Variable;
      newNodeRequest.ParentNodeId = GetId();
      newNodeRequest.ReferenceTypeId = ObjectId::HasProperty;
      newNodeRequest.TypeDefinition = NodeId();
      VariableAttributes attrs;
      attrs.Description = LocalizedText(browseName.Name);
      attrs.DisplayName = LocalizedText(browseName.Name);
      attrs.Value = value;
      attrs.Type = OpcUa::VariantTypeToDataType(value.Type());
      newNodeRequest.Attributes = attrs;

      NodeManagementServices::SharedPtr nodes = GetServices()->NodeManagement();
      std::vector<AddNodesResult> newNode = nodes->AddNodes({newNodeRequest});
      if (newNode.size() != 1)
      {
        throw std::runtime_error("opcua_model| Server returned wrong number new nodes results.");
      }

      OpcUa::CheckStatusCode(newNode[0].Status);
      Variable newVariable(GetServices());
      newVariable.Id = newNode[0].AddedNodeId;
      newVariable.BrowseName = browseName;
      newVariable.DisplayName = attrs.Description;
      newVariable.DataType = value.Type();
      newVariable.TypeId = newNodeRequest.TypeDefinition;
      return newVariable;
    }
Example #2
0
bool BXServicesManager::GetServicesIds(std::vector<std::string>& servicesIdsVec)
{
  BXBoxeeServices services;

  GetServices(services);

  int numOfServices = services.GetNumOfServices();

  if(numOfServices < 1)
  {
    LOG(LOG_LEVEL_ERROR,"BXServicesManager::GetServicesIds - Call to GetServices returned [ServicesListSize=%d] (serv)",numOfServices);
    return false;
  }

  for(int i=0; i<numOfServices; i++)
  {
    BXObject obj = services.GetService(i);

    servicesIdsVec.push_back(obj.GetID());
  }

  LOG(LOG_LEVEL_DEBUG,"BXServicesManager::GetServicesIds - Going to return  [servicesIdsVecSize=%d] (serv)",servicesIdsVec.size());

  return true;
}
Example #3
0
bool BXServicesManager::GetServices(std::vector<BXServicesItem>& servicesVec)
{
  BXBoxeeServices services;
  
  GetServices(services);
  
  int numOfServices = services.GetNumOfServices();
  
  if(numOfServices < 1)
  {
    LOG(LOG_LEVEL_ERROR,"BXServicesManager::GetServices - Call to GetServices returned [ServicesListSize=%d] (serv)",numOfServices);
    return false;
  }
  
  for(int i=0; i<numOfServices; i++)
  {
    BXObject obj = services.GetService(i);
    
    std::string type = obj.GetType();
    std::string id = obj.GetID();
    std::string name = obj.GetValue(MSG_KEY_NAME);
    
    BXServicesItem servicesItem(type,id,name);
    
    servicesVec.push_back(servicesItem);
  }

  LOG(LOG_LEVEL_DEBUG,"BXServicesManager::GetServices - Going to return  [ServicesListSize=%d] (serv)",servicesVec.size());

  return true;
}
Example #4
0
    AddNodesItem Object::CreateObjectCopy(const NodeId& parentId, const ReferenceDescription& ref)
    {
      const NodeId& nodeId = ref.TargetNodeId;

      ReadParameters readParams;
      readParams.AttributesToRead.push_back(ToReadValueId(nodeId, AttributeId::DisplayName));
      readParams.AttributesToRead.push_back(ToReadValueId(nodeId, AttributeId::Description));
      readParams.AttributesToRead.push_back(ToReadValueId(nodeId, AttributeId::WriteMask));
      readParams.AttributesToRead.push_back(ToReadValueId(nodeId, AttributeId::UserWriteMask));
      readParams.AttributesToRead.push_back(ToReadValueId(nodeId, AttributeId::BrowseName));
      std::vector<DataValue> values = GetServices()->Attributes()->Read(readParams);

      ObjectAttributes attrs;
      attrs.DisplayName = values[0].Value.As<LocalizedText>();
      attrs.Description = values[1].Value.As<LocalizedText>();
      attrs.WriteMask = values[2].Value.As<uint32_t>();
      attrs.UserWriteMask = values[3].Value.As<uint32_t>();

      AddNodesItem newNode;
      newNode.BrowseName = values[4].Value.As<QualifiedName>();
      newNode.Class = NodeClass::Object;
      newNode.ParentNodeId = parentId;
      newNode.ReferenceTypeId = ref.ReferenceTypeId;
      newNode.TypeDefinition = ref.TargetNodeTypeDefinition;
      newNode.Attributes = attrs;
      return newNode;
    }
Example #5
0
    Variable Object::GetVariable(const RelativePath& relativePath) const
    {
      OpcUa::BrowsePath browsePath;
      browsePath.StartingNode = GetId();
      browsePath.Path = relativePath;
      OpcUa::TranslateBrowsePathsParameters params;
      params.BrowsePaths.push_back(browsePath);
      const std::vector<OpcUa::BrowsePathResult>& result = GetServices()->Views()->TranslateBrowsePathsToNodeIds(params);
      if (result.size() != 1)
        throw std::runtime_error("object_model| Server returned more than one browse paths on TranslateBrowsePathsToNodeIds request.");

      const OpcUa::BrowsePathResult& resultPath = result.back();
      OpcUa::CheckStatusCode(resultPath.Status);
      if (resultPath.Targets.size() != 1)
        throw std::runtime_error("object_model| Server returned too many target elements on TranslateBrowsePathsToNodeIds request.");

      return Variable(resultPath.Targets.back().Node, GetServices());
    }
Example #6
0
    Object Object::CreateObject(const NodeId& newNodeId, const NodeId& parentNode, const NodeId& typeId, const QualifiedName& browseName, const std::string& displayName)
    {
      Object object(GetServices());
      object.Id = InstantiateType(newNodeId, parentNode, typeId, NodeClass::Object, browseName, displayName);
      object.BrowseName = browseName;
      object.DisplayName = LocalizedText(displayName);
      return object;

    }
Example #7
0
 DataValue Variable::GetValue() const
 {
   ReadParameters params;
   params.AttributesToRead.push_back(AttributeValueID(GetID(), AttributeID::VALUE));
   const std::vector<DataValue> result = GetServices()->Attributes()->Read(params);
   if (result.size() != 1)
   {
     throw std::runtime_error("Cannot read variable value. Server returned invalid number of values.");
   }
   return result.front();
 }
Example #8
0
INT_PTR CALLBACK
ServicesPageWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    LV_COLUMN   column;
    TCHAR       szTemp[256];
    DWORD dwStyle;

    UNREFERENCED_PARAMETER(lParam);
    UNREFERENCED_PARAMETER(wParam);

    switch (message) {
    case WM_INITDIALOG:

        hServicesListCtrl = GetDlgItem(hDlg, IDC_SERVICES_LIST);
        hServicesDialog = hDlg;

        dwStyle = (DWORD) SendMessage(hServicesListCtrl, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
        dwStyle = dwStyle | LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES;
        SendMessage(hServicesListCtrl, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dwStyle);

        SetWindowPos(hDlg, NULL, 10, 32, 0, 0, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER);

        // Initialize the application page's controls
        column.mask = LVCF_TEXT | LVCF_WIDTH;

        LoadString(hInst, IDS_SERVICES_COLUMN_SERVICE, szTemp, 256);
        column.pszText = szTemp;
        column.cx = 200;
        (void)ListView_InsertColumn(hServicesListCtrl, 0, &column);

        column.mask = LVCF_TEXT | LVCF_WIDTH;
        LoadString(hInst, IDS_SERVICES_COLUMN_REQ, szTemp, 256);
        column.pszText = szTemp;
        column.cx = 70;
        (void)ListView_InsertColumn(hServicesListCtrl, 1, &column);

        column.mask = LVCF_TEXT | LVCF_WIDTH;
        LoadString(hInst, IDS_SERVICES_COLUMN_VENDOR, szTemp, 256);
        column.pszText = szTemp;
        column.cx = 200;
        (void)ListView_InsertColumn(hServicesListCtrl, 2, &column);

        column.mask = LVCF_TEXT | LVCF_WIDTH;
        LoadString(hInst, IDS_SERVICES_COLUMN_STATUS, szTemp, 256);
        column.pszText = szTemp;
        column.cx = 70;
        (void)ListView_InsertColumn(hServicesListCtrl, 3, &column);

        GetServices();
        return TRUE;
    }

    return 0;
}
Example #9
0
 void Variable::SetValue(const DataValue& value)
 {
   WriteValue writeValue;
   writeValue.Attribute = AttributeID::VALUE;
   writeValue.Data = value;
   writeValue.Node = Id;
   std::vector<StatusCode> result = GetServices()->Attributes()->Write({writeValue});
   if (result.size() != 1)
   {
     throw std::runtime_error("Failed to write data. Server returned wron nunber of status codes.");
   }
   CheckStatusCode(result[0]);
 }
Example #10
0
    std::vector<ReferenceDescription> Object::BrowseObjectsAndVariables(const NodeId& id)
    {
      // Id of the new node.
      BrowseDescription desc;
      desc.Direction = BrowseDirection::Forward;
      desc.IncludeSubtypes = true;
      desc.NodeClasses =   NodeClass::Object | NodeClass::Variable | NodeClass::Method;
      desc.ReferenceTypeId = ObjectId::HierarchicalReferences;
      desc.NodeToBrowse = id;
      desc.ResultMask = BrowseResultMask::NodeClass | BrowseResultMask::TypeDefinition | BrowseResultMask::BrowseName | BrowseResultMask::DisplayName;

      // browse sub objects and variables.
      NodesQuery query;
      query.NodesToBrowse.push_back(desc);
      ViewServices::SharedPtr views = GetServices()->Views();
      return views->Browse(query)[0].Referencies; //FIME: this method should return BrowseResults
    }
Example #11
0
    AddNodesItem Object::CreateVariableCopy(const NodeId& parentId, const ReferenceDescription& ref)
    {
      const NodeId& nodeId = ref.TargetNodeId;

      ReadParameters readParams;
      readParams.AttributesToRead.push_back(ToReadValueId(nodeId, AttributeId::DisplayName));
      readParams.AttributesToRead.push_back(ToReadValueId(nodeId, AttributeId::Description));
      readParams.AttributesToRead.push_back(ToReadValueId(nodeId, AttributeId::Value));
      readParams.AttributesToRead.push_back(ToReadValueId(nodeId, AttributeId::DataType));
      readParams.AttributesToRead.push_back(ToReadValueId(nodeId, AttributeId::ValueRank));
      readParams.AttributesToRead.push_back(ToReadValueId(nodeId, AttributeId::ArrayDimensions));
      readParams.AttributesToRead.push_back(ToReadValueId(nodeId, AttributeId::AccessLevel));
      readParams.AttributesToRead.push_back(ToReadValueId(nodeId, AttributeId::UserAccessLevel));
      readParams.AttributesToRead.push_back(ToReadValueId(nodeId, AttributeId::MinimumSamplingInterval));
      readParams.AttributesToRead.push_back(ToReadValueId(nodeId, AttributeId::Historizing));
      readParams.AttributesToRead.push_back(ToReadValueId(nodeId, AttributeId::WriteMask));
      readParams.AttributesToRead.push_back(ToReadValueId(nodeId, AttributeId::UserWriteMask));
      readParams.AttributesToRead.push_back(ToReadValueId(nodeId, AttributeId::BrowseName));
      std::vector<DataValue> values = GetServices()->Attributes()->Read(readParams);

      VariableAttributes attrs;
      attrs.DisplayName = values[0].Value.As<LocalizedText>();
      attrs.Description = values[1].Value.As<LocalizedText>();
      attrs.Value = values[2].Value;
      attrs.Type = values[3].Value.As<NodeId>();
      attrs.Rank = values[4].Value.As<int32_t>();
      attrs.Dimensions = values[5].Value.As<std::vector<uint32_t>>();
      attrs.AccessLevel = static_cast<VariableAccessLevel>(values[6].Value.As<uint8_t>());
      attrs.UserAccessLevel = static_cast<VariableAccessLevel>(values[7].Value.As<uint8_t>());
      attrs.MinimumSamplingInterval = values[8].Value.As<Duration>();
      attrs.Historizing = values[9].Value.As<bool>();
      attrs.WriteMask = values[10].Value.As<uint32_t>();
      attrs.UserWriteMask = values[11].Value.As<uint32_t>();

      AddNodesItem newNode;
      newNode.BrowseName = values[12].Value.As<QualifiedName>();
      newNode.Class = NodeClass::Variable;
      newNode.ParentNodeId = parentId;
      newNode.ReferenceTypeId = ref.ReferenceTypeId;
      newNode.TypeDefinition = ref.TargetNodeTypeDefinition;
      newNode.Attributes = attrs;
      return newNode;
    }
Example #12
0
 std::map<NodeId, std::vector<ReferenceDescription>> Object::CopyObjectsAndVariables(const NodeId& targetNode, const std::vector<ReferenceDescription>& refs)
 {
   std::map<NodeId, std::vector<ReferenceDescription>> nextCopyData;
   for (const ReferenceDescription& ref : refs)
   {
     std::vector<AddNodesResult> result;
     std::vector<AddNodesItem> newNodeRequest;
     switch (ref.TargetNodeClass)
     {
       case NodeClass::Object:
       {
         if (ref.TargetNodeTypeDefinition !=ObjectId::Null)
         {
           InstantiateType(NodeId(), targetNode, ref.TargetNodeTypeDefinition, NodeClass::Object, ref.BrowseName, ref.DisplayName.Text);
         }
         else
         {
           newNodeRequest = {CreateObjectCopy(targetNode, ref)};
         }
         break;
       }
       case NodeClass::Variable:
       {
         newNodeRequest = {CreateVariableCopy(targetNode, ref)};
         break;
       }
       default:
       {
         continue;
       }
     }
     if (newNodeRequest.empty())
     {
       continue;
     }
     result = GetServices()->NodeManagement()->AddNodes(newNodeRequest);
     std::vector<ReferenceDescription> newRefs = BrowseObjectsAndVariables(ref.TargetNodeId);
     nextCopyData.insert({result[0].AddedNodeId, newRefs});
   }
   return nextCopyData;
 }
Example #13
0
    NodeId Object::InstantiateType(const NodeId& newNodeId, const NodeId& parentNode, const NodeId& typeId, NodeClass nodeClass, const QualifiedName& browseName, const std::string& displayName)
    {
      // Creating new node for object
      AddNodesItem newNodeRequest;
      newNodeRequest.BrowseName = browseName;
      newNodeRequest.RequestedNewNodeId = newNodeId;
      newNodeRequest.Class = nodeClass;
      newNodeRequest.ParentNodeId = parentNode;
      newNodeRequest.ReferenceTypeId = nodeClass == NodeClass::Object ? ObjectId::HasComponent : ObjectId::HasProperty;
      newNodeRequest.TypeDefinition = typeId;
      ObjectAttributes attrs;
      attrs.Description = LocalizedText(displayName);
      attrs.DisplayName = LocalizedText(displayName);
      newNodeRequest.Attributes = attrs;

      NodeManagementServices::SharedPtr nodes = GetServices()->NodeManagement();
      std::vector<AddNodesResult> newObjectNode = nodes->AddNodes({newNodeRequest});
      if (newObjectNode.size() != 1)
      {
        throw std::runtime_error("opcua_model| Server returned wrong number new nodes results.");
      }

      OpcUa::CheckStatusCode(newObjectNode[0].Status);

      std::map<NodeId, std::vector<ReferenceDescription>> nextRefs;
      nextRefs.insert({newObjectNode[0].AddedNodeId, BrowseObjectsAndVariables(typeId)});
      while(!nextRefs.empty())
      {
        std::map<NodeId, std::vector<ReferenceDescription>> newRefs;
        for (auto idRefs : nextRefs)
        {
          std::map<NodeId, std::vector<ReferenceDescription>> tmpRefs = CopyObjectsAndVariables(idRefs.first, idRefs.second);
          newRefs.insert(tmpRefs.begin(), tmpRefs.end());
        }
        nextRefs = std::move(newRefs);
      }
      return newObjectNode[0].AddedNodeId;
    }
Example #14
0
 std::vector<Variable> Object::GetVariables() const
 {
   return Browse<Variable>(GetId(), NodeClass::Variable, GetServices());
 }
Example #15
0
 ObjectType Object::GetType() const
 {
   return ObjectType(ObjectId::Null, GetServices());
 }
Example #16
0
 Object Object::GetObject(const QualifiedName& name) const
 {
   return Object(ObjectId::Null, GetServices());
 }
Example #17
0
 Object Object::GetObject(const RelativePath& name) const
 {
   return Object(ObjectId::Null, GetServices());
 }
Example #18
0
 std::vector<Variable> Variable::Variables() const
 {
   return Browse<Variable>(GetID(), NODE_CLASS_VARIABLE, GetServices());
 }
Example #19
0
 std::vector<Object> Object::GetObjects() const
 {
   return Browse<Object>(GetId(), NodeClass::Object, GetServices());
 }
Example #20
0
 Variable Object::CreateVariable(const NodeId& newVariableId, const QualifiedName& browseName, const VariableType& type)
 {
   return Variable(GetServices());
 }
Example #21
0
INT_PTR CALLBACK
ServicesPageWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    UNREFERENCED_PARAMETER(wParam);

    switch (message)
    {
        case WM_INITDIALOG:
        {
            hServicesPage     = hDlg;
            hServicesListCtrl = GetDlgItem(hServicesPage, IDC_SERVICES_LIST);

            //
            // Correctly display message strings.
            //
            LPCWSTR szOSVendor = (bIsWindows ? IDS_MICROSOFT : IDS_REACTOS);

            size_t itemLength = 0;
            LPWSTR szItem = NULL, szNewItem = NULL;

            itemLength = GetWindowTextLength(GetDlgItem(hServicesPage, IDC_STATIC_SERVICES_WARNING)) + 1;
            szItem     = (LPWSTR)MemAlloc(0, itemLength * sizeof(WCHAR));
            GetDlgItemText(hServicesPage, IDC_STATIC_SERVICES_WARNING, szItem, (int)itemLength);
            szNewItem  = FormatString(szItem, szOSVendor);
            SetDlgItemText(hServicesPage, IDC_STATIC_SERVICES_WARNING, szNewItem);
            MemFree(szNewItem);
            MemFree(szItem);

            itemLength = GetWindowTextLength(GetDlgItem(hServicesPage, IDC_CBX_SERVICES_MASK_PROPRIETARY_SVCS)) + 1;
            szItem     = (LPWSTR)MemAlloc(0, itemLength * sizeof(WCHAR));
            GetDlgItemText(hServicesPage, IDC_CBX_SERVICES_MASK_PROPRIETARY_SVCS, szItem, (int)itemLength);
            szNewItem  = FormatString(szItem, szOSVendor);
            SetDlgItemText(hServicesPage, IDC_CBX_SERVICES_MASK_PROPRIETARY_SVCS, szNewItem);
            MemFree(szNewItem);
            MemFree(szItem);

            //
            // Initialize the styles.
            //
            DWORD dwStyle = ListView_GetExtendedListViewStyle(hServicesListCtrl);
            ListView_SetExtendedListViewStyle(hServicesListCtrl, dwStyle | LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES);
            SetWindowTheme(hServicesListCtrl, L"Explorer", NULL);

            //
            // Initialize the application page's controls.
            //
            LVCOLUMN column = {};

            // First column : Service's name.
            column.mask = LVCF_TEXT | LVCF_WIDTH;
            column.pszText = LoadResourceString(hInst, IDS_SERVICES_COLUMN_SERVICE);
            column.cx = 150;
            ListView_InsertColumn(hServicesListCtrl, 0, &column);
            MemFree(column.pszText);

            // Second column : Whether the service is required or not.
            column.mask = LVCF_TEXT | LVCF_WIDTH;
            column.pszText = LoadResourceString(hInst, IDS_SERVICES_COLUMN_REQ);
            column.cx = 60;
            ListView_InsertColumn(hServicesListCtrl, 1, &column);
            MemFree(column.pszText);

            // Third column : Service's vendor.
            column.mask = LVCF_TEXT | LVCF_WIDTH;
            column.pszText = LoadResourceString(hInst, IDS_SERVICES_COLUMN_VENDOR);
            column.cx = 150;
            ListView_InsertColumn(hServicesListCtrl, 2, &column);
            MemFree(column.pszText);

            // Fourth column : Service's status.
            column.mask = LVCF_TEXT | LVCF_WIDTH;
            column.pszText = LoadResourceString(hInst, IDS_SERVICES_COLUMN_STATUS);
            column.cx = 60;
            ListView_InsertColumn(hServicesListCtrl, 3, &column);
            MemFree(column.pszText);

            // Fifth column : Service's disabled date.
            column.mask = LVCF_TEXT | LVCF_WIDTH;
            column.pszText = LoadResourceString(hInst, IDS_SERVICES_COLUMN_DATEDISABLED);
            column.cx = 120;
            ListView_InsertColumn(hServicesListCtrl, 4, &column);
            MemFree(column.pszText);

            //
            // Populate and sort the list.
            //
            GetServices();
            ListView_Sort(hServicesListCtrl, 0);
            Update_Btn_States(hDlg);

            // Select the first item.
            ListView_SetItemState(hServicesListCtrl, 0, LVIS_SELECTED, LVIS_SELECTED);

            return TRUE;
        }

        case WM_DESTROY:
        {
            ClearServicesList();
            userModificationsList.RemoveAll();
            return 0;
        }

        case WM_COMMAND:
        {
            switch (LOWORD(wParam))
            {
                case IDC_BTN_SERVICES_ACTIVATE:
                {
                    BOOL bAreThereModifs = FALSE;

                    int index = -1; // From the beginning.
                    while ((index = ListView_GetNextItem(hServicesListCtrl, index, LVNI_ALL)) != -1)
                    {
                        bAreThereModifs = ValidateItem(index, TRUE, FALSE) || bAreThereModifs; // The order is verrrrrry important !!!!
                    }

                    if (bAreThereModifs)
                    {
                        Update_Btn_States(hDlg);
                        PropSheet_Changed(GetParent(hServicesPage), hServicesPage);
                    }

                    return TRUE;
                }

                case IDC_BTN_SERVICES_DEACTIVATE:
                {
                    BOOL bAreThereModifs = FALSE;

                    int index = -1; // From the beginning.
                    while ((index = ListView_GetNextItem(hServicesListCtrl, index, LVNI_ALL)) != -1)
                    {
                        bAreThereModifs = ValidateItem(index, FALSE, FALSE) || bAreThereModifs; // The order is verrrrrry important !!!!
                    }

                    if (bAreThereModifs)
                    {
                        Update_Btn_States(hDlg);
                        PropSheet_Changed(GetParent(hServicesPage), hServicesPage);
                    }

                    return TRUE;
                }

                case IDC_CBX_SERVICES_MASK_PROPRIETARY_SVCS:
                {
                    bMaskProprietarySvcs = !bMaskProprietarySvcs;
                    GetServices(bMaskProprietarySvcs);
                    Update_Btn_States(hDlg);

                    return TRUE;
                }

                default:
                    return FALSE;
            }
            return FALSE;
        }

        case UM_CHECKSTATECHANGE:
        {
            BOOL bNewCheckState = !!((ListView_GetCheckState(hServicesListCtrl, int(lParam)) + 1) % 2);

            if (ValidateItem(/*reinterpret_cast<int>*/ int(lParam), bNewCheckState, !HideEssentialServiceWarning()))
            {
                Update_Btn_States(hDlg);
                PropSheet_Changed(GetParent(hServicesPage), hServicesPage);
            }

            return TRUE;
        }

        case WM_NOTIFY:
        {
            if (reinterpret_cast<LPNMHDR>(lParam)->hwndFrom == hServicesListCtrl)
            {
                switch (reinterpret_cast<LPNMHDR>(lParam)->code)
                {
                    case NM_CLICK:
                    case NM_RCLICK:
                    {
                        DWORD         dwpos = GetMessagePos();
                        LVHITTESTINFO ht    = {};
                        ht.pt.x = GET_X_LPARAM(dwpos);
                        ht.pt.y = GET_Y_LPARAM(dwpos);
                        MapWindowPoints(HWND_DESKTOP /*NULL*/, hServicesListCtrl, &ht.pt, 1);

                        /*
                         * We use ListView_SubItemHitTest(...) and not ListView_HitTest(...)
                         * because ListView_HitTest(...) returns bad flags when one clicks
                         * on a sub-item different from 0. The flags then contain LVHT_ONITEMSTATEICON
                         * which must not be obviously present in this case.
                         */
                        ListView_SubItemHitTest(hServicesListCtrl, &ht);

                        if (LVHT_ONITEMSTATEICON & ht.flags)
                        {
                            PostMessage(hDlg, UM_CHECKSTATECHANGE, 0, (LPARAM)ht.iItem);

                            // Disable default behaviour. Needed for the UM_CHECKSTATECHANGE
                            // custom notification to work as expected.
                            SetWindowLongPtr(hDlg, DWLP_MSGRESULT, TRUE);
                        }

                        return TRUE;
                    }

                    case NM_DBLCLK:
                    case NM_RDBLCLK:
                    {
                        // We deactivate double-clicks.
                        SetWindowLongPtr(hDlg, DWLP_MSGRESULT, TRUE);
                        return TRUE;
                    }

                    case LVN_KEYDOWN:
                    {
                        if (reinterpret_cast<LPNMLVKEYDOWN>(lParam)->wVKey == VK_SPACE)
                        {
                            int iItem = ListView_GetSelectionMark(hServicesListCtrl);
                            PostMessage(hDlg, UM_CHECKSTATECHANGE, 0, (LPARAM)iItem);

                            // Disable default behaviour. Needed for the UM_CHECKSTATECHANGE
                            // custom notification to work as expected.
                            SetWindowLongPtr(hDlg, DWLP_MSGRESULT, TRUE);
                        }

                        return TRUE;
                    }

                    case LVN_COLUMNCLICK:
                    {
                        int iSortingColumn = reinterpret_cast<LPNMLISTVIEW>(lParam)->iSubItem;

                        ListView_SortEx(hServicesListCtrl, iSortingColumn, iSortedColumn);
                        iSortedColumn = iSortingColumn;

                        return TRUE;
                    }
                }
            }
            else
            {
                switch (reinterpret_cast<LPNMHDR>(lParam)->code)
                {
                    case PSN_APPLY:
                    {
                        // Try to apply the modifications to the system.
                        MessageBox(NULL, _T("In Services page: PSN_APPLY"), _T("Info"), MB_ICONINFORMATION);

                        /*
                        //
                        // Move this away...
                        //
                        int iRetVal = MessageBox(NULL, _T("Would you really want to modify the configuration of your system ?"), _T("Warning"), MB_ICONWARNING | MB_YESNOCANCEL);

                        if (iRetVal == IDYES /\* modifications are OK *\/)
                            SetWindowLongPtr(hServicesPage, DWLP_MSGRESULT, PSNRET_NOERROR);
                        else if (iRetVal == IDNO /\* modifications are not OK *\/)
                            SetWindowLongPtr(hServicesPage, DWLP_MSGRESULT, PSNRET_NOERROR);
                        else // if (iRetVal == IDCANCEL) // There was an error...
                            SetWindowLongPtr(hServicesPage, DWLP_MSGRESULT, PSNRET_INVALID);
                        */

                        //
                        // We modify the services which are stored in the user modification list.
                        //

                        // 1- Open the Service Control Manager for modifications.
                        SC_HANDLE hSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
                        if (hSCManager != NULL)
                        {
                            LPCWSTR svcName;

                            for (POSITION it = userModificationsList.GetHeadPosition(); it; userModificationsList.GetNext(it))
                            {
                                svcName = userModificationsList.GetAt(it);

                                // 2- Retrieve a handle to the service.
                                SC_HANDLE hService = OpenServiceW(hSCManager, svcName, SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);
                                if (hService == NULL)
                                {
                                    // TODO : Show a message box.
                                    continue;
                                }

                                DWORD dwBytesNeeded = 0;
                                QueryServiceConfigW(hService, NULL, 0, &dwBytesNeeded);
                                // if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)

                                LPQUERY_SERVICE_CONFIG lpServiceConfig = (LPQUERY_SERVICE_CONFIG)MemAlloc(0, dwBytesNeeded);
                                if (!lpServiceConfig)
                                {
                                    CloseServiceHandle(hService);
                                    continue; // TODO ? Show a message box...
                                }
                                QueryServiceConfigW(hService, lpServiceConfig, dwBytesNeeded, &dwBytesNeeded);

                                if (lpServiceConfig->dwStartType == SERVICE_DISABLED) // We have a disabled service which is becoming to be enabled.
                                {
                                    // 3a- Retrive the properties of the disabled service from the registry.
                                    RegistryDisabledServiceItemParams params = {};

                                    QUERY_REGISTRY_KEYS_TABLE KeysQueryTable[2] = {};
                                    KeysQueryTable[0].QueryRoutine = GetRegistryKeyedDisabledServicesQueryRoutine;
                                    KeysQueryTable[0].EntryContext = &params;
                                    RegQueryRegistryKeys(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Shared Tools\\MSConfig\\services", KeysQueryTable, (PVOID)svcName);

                                    if (bIsWindows && bIsPreVistaOSVersion && !params.bIsPresent)
                                    {
                                        QUERY_REGISTRY_VALUES_TABLE ValuesQueryTable[2] = {};
                                        ValuesQueryTable[0].QueryRoutine = GetRegistryValuedDisabledServicesQueryRoutine;
                                        ValuesQueryTable[0].EntryContext = &params;
                                        RegQueryRegistryValues(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Shared Tools\\MSConfig\\services", ValuesQueryTable, (PVOID)svcName);
                                    }

                                    if (params.bIsPresent)
                                    {
                                        // 4a- Modify the service.
                                        ChangeServiceConfigW(hService, SERVICE_NO_CHANGE, params.dwStartType, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL);

                                        // 5a- Remove the registry entry of the service.
                                        if (params.bIsKeyed)
                                        {
                                            CAtlStringW serviceRegKey(L"SOFTWARE\\Microsoft\\Shared Tools\\MSConfig\\services\\");
                                            serviceRegKey += svcName;
                                            RegDeleteKeyW(HKEY_LOCAL_MACHINE, serviceRegKey);

                                            /***** HACK for Windows < Vista (e.g. 2000, Xp, 2003...) *****/
                                            //
                                            // Delete also the valued-entry of the service.
                                            //
                                            if (bIsWindows && bIsPreVistaOSVersion)
                                            {
                                                HKEY hSubKey = NULL;
                                                if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Shared Tools\\MSConfig\\services", 0, KEY_SET_VALUE /*KEY_READ*/, &hSubKey) == ERROR_SUCCESS)
                                                {
                                                    RegDeleteValue(hSubKey, svcName);
                                                    RegCloseKey(hSubKey);
                                                }
                                            }
                                            /*************************************************************/
                                        }
                                        else
                                        {
                                            HKEY hSubKey = NULL;
                                            if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Shared Tools\\MSConfig\\services", 0, KEY_SET_VALUE /*KEY_READ*/, &hSubKey) == ERROR_SUCCESS)
                                            {
                                                RegDeleteValue(hSubKey, svcName);
                                                RegCloseKey(hSubKey);
                                            }
                                        }

                                        ////////// HACKHACKHACKHACKHACKHACKHACKHACKHACKHACKHACK ///////////
                                        // userModificationsList.RemoveAt(it);
                                    }
                                    else
                                    {
                                        // Ohoh !! We have a very big problem.
                                        MessageBox(NULL, _T("WTF ??"), _T("FATAL ERROR !!!!"), MB_ICONERROR);
                                    }
                                }
                                else // We have an enabled service which is becoming to be disabled.
                                {
                                    // 3b- Retrieve the local time of disabling.
                                    SYSTEMTIME disableDate = {};
                                    GetLocalTime(&disableDate);

                                    // 4b- Modify the service.
                                    ChangeServiceConfigW(hService, SERVICE_NO_CHANGE, SERVICE_DISABLED, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL);

                                    // 5b- Register the service into the registry.
                                    CAtlStringW serviceRegKey(L"SOFTWARE\\Microsoft\\Shared Tools\\MSConfig\\services\\");
                                    serviceRegKey += svcName;
                                    HKEY hSubKey = NULL;
                                    if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, serviceRegKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hSubKey, NULL) == ERROR_SUCCESS)
                                    {
                                        RegSetDWORDValue(hSubKey, NULL, svcName, FALSE, lpServiceConfig->dwStartType);

                                    #if 1 // DisableDate
                                        RegSetDWORDValue(hSubKey, NULL, L"DAY"   , FALSE, disableDate.wDay   );
                                        RegSetDWORDValue(hSubKey, NULL, L"HOUR"  , FALSE, disableDate.wHour  );
                                        RegSetDWORDValue(hSubKey, NULL, L"MINUTE", FALSE, disableDate.wMinute);
                                        RegSetDWORDValue(hSubKey, NULL, L"MONTH" , FALSE, disableDate.wMonth );
                                        RegSetDWORDValue(hSubKey, NULL, L"SECOND", FALSE, disableDate.wSecond);
                                        RegSetDWORDValue(hSubKey, NULL, L"YEAR"  , FALSE, disableDate.wYear  );
                                    #endif

                                        RegCloseKey(hSubKey);
                                    }

                                    /***** HACK for Windows < Vista (e.g. 2000, Xp, 2003...) *****/
                                    //
                                    // Save also a valued-entry for the service.
                                    //
                                    if (bIsWindows && bIsPreVistaOSVersion)
                                    {
                                        RegSetDWORDValue(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Shared Tools\\MSConfig\\services", svcName, TRUE, lpServiceConfig->dwStartType);
                                    }
                                    /*************************************************************/

                                    ////////// HACKHACKHACKHACKHACKHACKHACKHACKHACKHACKHACK ///////////
                                    // userModificationsList.RemoveAt(it);
                                }

                                MemFree(lpServiceConfig);
                                CloseServiceHandle(hService);
                            }

                            //////////// HACK HACK !!!! ////////////
                            userModificationsList.RemoveAll();
                            ////////////////////////////////////////

                            CloseServiceHandle(hSCManager);


                            //// PropSheet_UnChanged(GetParent(hServicesPage), hServicesPage); ////
                            PropSheet_CancelToClose(GetParent(hDlg));

                            /* Modifications are OK */
                            SetWindowLongPtr(hServicesPage, DWLP_MSGRESULT, PSNRET_NOERROR);
                        }
                        else
                        {
                            MessageBox(hDlg, _T("Impossible to open the SC manager..."), _T("Error"), MB_ICONERROR);

                            // There was an error...
                            SetWindowLongPtr(hServicesPage, DWLP_MSGRESULT, PSNRET_INVALID);
                        }

                        GetServices(bMaskProprietarySvcs);
                        Update_Btn_States(hDlg);

                        return TRUE;
                    }

                    case PSN_HELP:
                    {
                        MessageBox(hServicesPage, _T("Help not implemented yet!"), _T("Help"), MB_ICONINFORMATION | MB_OK);
                        return TRUE;
                    }

                    case PSN_KILLACTIVE: // Is going to lose activation.
                    {
                        // Changes are always valid of course.
                        SetWindowLongPtr(hServicesPage, DWLP_MSGRESULT, FALSE);
                        return TRUE;
                    }

                    case PSN_QUERYCANCEL:
                    {
                        // RefreshStartupList();

                        // Allows cancellation.
                        SetWindowLongPtr(hServicesPage, DWLP_MSGRESULT, FALSE);

                        return TRUE;
                    }

                    case PSN_QUERYINITIALFOCUS:
                    {
                        // Give the focus on and select the first item.
                        ListView_SetItemState(hServicesListCtrl, 0, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);

                        SetWindowLongPtr(hServicesPage, DWLP_MSGRESULT, (LONG_PTR)hServicesListCtrl);
                        return TRUE;
                    }

                    //
                    // DO NOT TOUCH THESE NEXT MESSAGES, THEY ARE OK LIKE THIS...
                    //
                    case PSN_RESET: // Perform final cleaning, called before WM_DESTROY.
                        return TRUE;

                    case PSN_SETACTIVE: // Is going to gain activation.
                    {
                        SetWindowLongPtr(hServicesPage, DWLP_MSGRESULT, 0);
                        return TRUE;
                    }

                    default:
                        break;
                }
            }
        }

        default:
            return FALSE;
    }

    return FALSE;
}