Exemple #1
0
static HRESULT EnumerateAltStreams(
    const NFind::CFileInfo &fi,
    const NWildcard::CCensorNode &curNode,
    int phyParent, int logParent, const FString &fullPath,
    const UStringVector &addArchivePrefix,  // prefix from curNode
    CDirItems &dirItems)
{
  NFind::CStreamEnumerator enumerator(fullPath);
  for (;;)
  {
    NFind::CStreamInfo si;
    bool found;
    if (!enumerator.Next(si, found))
    {
      return dirItems.AddError(fullPath + FTEXT(":*")); // , (DWORD)E_FAIL
    }
    if (!found)
      return S_OK;
    if (si.IsMainStream())
      continue;
    UStringVector addArchivePrefixNew = addArchivePrefix;
    UString reducedName = si.GetReducedName();
    addArchivePrefixNew.Back() += reducedName;
    if (curNode.CheckPathToRoot(false, addArchivePrefixNew, true))
      continue;
    NFind::CFileInfo fi2 = fi;
    fi2.Name += us2fs(reducedName);
    fi2.Size = si.Size;
    fi2.Attrib &= ~FILE_ATTRIBUTE_DIRECTORY;
    fi2.IsAltStream = true;
    dirItems.AddDirFileInfo(phyParent, logParent, -1, fi2);
  }
}
Exemple #2
0
HRESULT EnumerateItems(
    const NWildcard::CCensor &censor,
    CDirItems &dirItems,
    IEnumDirItemCallback *callback,
    FStringVector &errorPaths,
    CRecordVector<DWORD> &errorCodes)
{
  for (int i = 0; i < censor.Pairs.Size(); i++)
  {
    const NWildcard::CPair &pair = censor.Pairs[i];
    int phyParent = pair.Prefix.IsEmpty() ? -1 : dirItems.AddPrefix(-1, -1, pair.Prefix);
    RINOK(EnumerateDirItems(pair.Head, phyParent, -1, us2fs(pair.Prefix), UStringVector(), dirItems, false,
        callback, errorPaths, errorCodes));
  }
  dirItems.ReserveDown();
  return S_OK;
}
void EnumerateDirItemsAndSort(NWildcard::CCensor &wildcardCensor,
    UStringVector &sortedPaths,
    UStringVector &sortedFullPaths)
{
  UStringVector paths;
  {
    CDirItems dirItems;
    {
      UStringVector errorPaths;
      CRecordVector<DWORD> errorCodes;
      HRESULT res = EnumerateItems(wildcardCensor, dirItems, NULL, errorPaths, errorCodes);
      if (res != S_OK || errorPaths.Size() > 0)
        throw "cannot find archive";
    }
    for (int i = 0; i < dirItems.Items.Size(); i++)
    {
      const CDirItem &dirItem = dirItems.Items[i];
      if (!dirItem.IsDir())
        paths.Add(dirItems.GetPhyPath(i));
    }
  }
  
  if (paths.Size() == 0)
    throw "there is no such archive";
  
  UStringVector fullPaths;
  
  int i;
  for (i = 0; i < paths.Size(); i++)
  {
    UString fullPath;
    NFile::NDirectory::MyGetFullPathName(paths[i], fullPath);
    fullPaths.Add(fullPath);
  }
  CIntVector indices;
  SortFileNames(fullPaths, indices);
  sortedPaths.Reserve(indices.Size());
  sortedFullPaths.Reserve(indices.Size());
  for (i = 0; i < indices.Size(); i++)
  {
    int index = indices[i];
    sortedPaths.Add(paths[index]);
    sortedFullPaths.Add(fullPaths[index]);
  }
}
static HRESULT EnumerateDirItems_Spec(
    const NWildcard::CCensorNode &curNode,
    int phyParent, int logParent, const FString &curFolderName,
    const FString &phyPrefix,
    const UStringVector &addArchivePrefix,
    CDirItems &dirItems,
    bool enterToSubFolders)
{
  const FString name2 = curFolderName + FCHAR_PATH_SEPARATOR;
  unsigned parent = dirItems.AddPrefix(phyParent, logParent, fs2us(name2));
  unsigned numItems = dirItems.Items.Size();
  HRESULT res = EnumerateDirItems(
      curNode, parent, parent, phyPrefix + name2,
      addArchivePrefix, dirItems, enterToSubFolders);
  if (numItems == dirItems.Items.Size())
    dirItems.DeleteLastPrefix();
  return res;
}
Exemple #5
0
static HRESULT EnumerateDirItems_Spec(const NWildcard::CCensorNode &curNode,
    int phyParent, int logParent, const FString &curFolderName,
    const FString &phyPrefix,
    const UStringVector &addArchivePrefix,
    CDirItems &dirItems,
    bool enterToSubFolders,
    IEnumDirItemCallback *callback,
    FStringVector &errorPaths,
    CRecordVector<DWORD> &errorCodes)
  
{
  const FString name2 = curFolderName + FCHAR_PATH_SEPARATOR;
  int parent = dirItems.AddPrefix(phyParent, logParent, fs2us(name2));
  int numItems = dirItems.Items.Size();
  HRESULT res = EnumerateDirItems(curNode, parent, parent, phyPrefix + name2,
    addArchivePrefix, dirItems, enterToSubFolders, callback, errorPaths, errorCodes);
  if (numItems == dirItems.Items.Size())
    dirItems.DeleteLastPrefix();
  return res;
}
static HRESULT EnumerateForItem(
    NFind::CFileInfo &fi,
    const NWildcard::CCensorNode &curNode,
    int phyParent, int logParent, const FString &phyPrefix,
    const UStringVector &addArchivePrefix,  // prefix from curNode
    CDirItems &dirItems,
    bool enterToSubFolders)
{
  const UString name = fs2us(fi.Name);
  bool enterToSubFolders2 = enterToSubFolders;
  UStringVector addArchivePrefixNew = addArchivePrefix;
  addArchivePrefixNew.Add(name);
  {
    UStringVector addArchivePrefixNewTemp(addArchivePrefixNew);
    if (curNode.CheckPathToRoot(false, addArchivePrefixNewTemp, !fi.IsDir()))
      return S_OK;
  }
  int dirItemIndex = -1;
  
  bool addAllSubStreams = false;

  if (curNode.CheckPathToRoot(true, addArchivePrefixNew, !fi.IsDir()))
  {
    int secureIndex = -1;
    #ifdef _USE_SECURITY_CODE
    if (dirItems.ReadSecure)
    {
      RINOK(dirItems.AddSecurityItem(phyPrefix + fi.Name, secureIndex));
    }
    #endif
    
    dirItemIndex = dirItems.Items.Size();
    dirItems.AddDirFileInfo(phyParent, logParent, secureIndex, fi);
    if (fi.IsDir())
      enterToSubFolders2 = true;

    addAllSubStreams = true;
  }

  #ifndef UNDER_CE
  if (dirItems.ScanAltStreams)
  {
    RINOK(EnumerateAltStreams(fi, curNode, phyParent, logParent,
        phyPrefix + fi.Name,
        addArchivePrefixNew,
        addAllSubStreams,
        dirItems));
  }

  if (dirItemIndex >= 0)
  {
    CDirItem &dirItem = dirItems.Items[dirItemIndex];
    RINOK(dirItems.SetLinkInfo(dirItem, fi, phyPrefix));
    if (dirItem.ReparseData.Size() != 0)
      return S_OK;
  }
  #endif
  
  if (!fi.IsDir())
    return S_OK;
  
  const NWildcard::CCensorNode *nextNode = 0;
  if (addArchivePrefix.IsEmpty())
  {
    int index = curNode.FindSubNode(name);
    if (index >= 0)
      nextNode = &curNode.SubNodes[index];
  }
  if (!enterToSubFolders2 && nextNode == 0)
    return S_OK;
  
  addArchivePrefixNew = addArchivePrefix;
  if (nextNode == 0)
  {
    nextNode = &curNode;
    addArchivePrefixNew.Add(name);
  }
  
  return EnumerateDirItems_Spec(
      *nextNode, phyParent, logParent, fi.Name, phyPrefix,
      addArchivePrefixNew,
      dirItems,
      enterToSubFolders2);
}
Exemple #7
0
STDMETHODIMP CAgent::DoOperation(
    CCodecs *codecs,
    int formatIndex,
    const wchar_t *newArchiveName,
    const Byte *stateActions,
    const wchar_t *sfxModule,
    IFolderArchiveUpdateCallback *updateCallback100)
{
  if (!CanUpdate())
    return E_NOTIMPL;
  NUpdateArchive::CActionSet actionSet;
  int i;
  for (i = 0; i < NUpdateArchive::NPairState::kNumValues; i++)
    actionSet.StateActions[i] = (NUpdateArchive::NPairAction::EEnum)stateActions[i];

  CDirItems dirItems;

  {
    UString folderPrefix = _folderPrefix;
    NFile::NName::NormalizeDirPathPrefix(folderPrefix);
    UStringVector errorPaths;
    CRecordVector<DWORD> errorCodes;
    dirItems.EnumerateDirItems2(folderPrefix, _archiveNamePrefix, _names, errorPaths, errorCodes);
    if (errorCodes.Size() > 0)
      return errorCodes.Front();
  }

  CMyComPtr<IOutArchive> outArchive;
  if (GetArchive())
  {
    RINOK(GetArchive()->QueryInterface(IID_IOutArchive, (void **)&outArchive));
  }
  else
  {
    if (formatIndex < 0)
      return E_FAIL;
    RINOK(codecs->CreateOutArchive(formatIndex, outArchive));
    #ifdef EXTERNAL_CODECS
    {
      CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
      outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
      if (setCompressCodecsInfo)
      {
        RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs));
      }
    }
    #endif

  }

  NFileTimeType::EEnum fileTimeType;
  UInt32 value;
  RINOK(outArchive->GetFileTimeType(&value));

  switch(value)
  {
    case NFileTimeType::kWindows:
    case NFileTimeType::kDOS:
    case NFileTimeType::kUnix:
      fileTimeType = NFileTimeType::EEnum(value);
      break;
    default:
      return E_FAIL;
  }


  CObjectVector<CArcItem> arcItems;
  if (GetArchive())
  {
    RINOK(ReadItems());
    EnumerateArchiveItems(this, _proxyArchive->RootFolder,  L"", arcItems);
  }

  CRecordVector<CUpdatePair2> updatePairs2;

  {
    CRecordVector<CUpdatePair> updatePairs;
    GetUpdatePairInfoList(dirItems, arcItems, fileTimeType, updatePairs);
    CAgUpCallbackImp upCallback(&arcItems, updateCallback100);
    UpdateProduce(updatePairs, actionSet, updatePairs2, &upCallback);
  }

  UInt32 numFiles = 0;
  for (i = 0; i < updatePairs2.Size(); i++)
    if (updatePairs2[i].NewData)
      numFiles++;
  
  if (updateCallback100)
  {
    RINOK(updateCallback100->SetNumFiles(numFiles));
  }
  
  CUpdateCallbackAgent updateCallbackAgent;
  updateCallbackAgent.SetCallback(updateCallback100);
  CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
  CMyComPtr<IArchiveUpdateCallback> updateCallback(updateCallbackSpec );

  updateCallbackSpec->DirItems = &dirItems;
  updateCallbackSpec->ArcItems = &arcItems;
  updateCallbackSpec->UpdatePairs = &updatePairs2;
  updateCallbackSpec->Archive = GetArchive();
  updateCallbackSpec->Callback = &updateCallbackAgent;

  COutFileStream *outStreamSpec = new COutFileStream;
  CMyComPtr<IOutStream> outStream(outStreamSpec);
  UString archiveName = newArchiveName;
  {
    UString resultPath;
    int pos;
    if(!NFile::NDirectory::MyGetFullPathName(archiveName, resultPath, pos))
      return E_FAIL;
    NFile::NDirectory::CreateComplexDirectory(resultPath.Left(pos));
  }
  if (!outStreamSpec->Create(archiveName, true))
  {
    // ShowLastErrorMessage();
    return E_FAIL;
  }
  
  CMyComPtr<ISetProperties> setProperties;
  if (outArchive->QueryInterface(IID_ISetProperties, (void **)&setProperties) == S_OK)
  {
    if (m_PropNames.Size() == 0)
    {
      RINOK(setProperties->SetProperties(0, 0, 0));
    }
    else
    {
      CRecordVector<const wchar_t *> names;
      for(i = 0; i < m_PropNames.Size(); i++)
        names.Add((const wchar_t *)m_PropNames[i]);

      NWindows::NCOM::CPropVariant *propValues = new NWindows::NCOM::CPropVariant[m_PropValues.Size()];
      try
      {
        for (int i = 0; i < m_PropValues.Size(); i++)
          propValues[i] = m_PropValues[i];
        RINOK(setProperties->SetProperties(&names.Front(), propValues, names.Size()));
      }
      catch(...)
      {
        delete []propValues;
        return E_FAIL;
      }
      delete []propValues;
    }
  }
  m_PropNames.Clear();
  m_PropValues.Clear();

  if (sfxModule != NULL)
  {
    CInFileStream *sfxStreamSpec = new CInFileStream;
    CMyComPtr<IInStream> sfxStream(sfxStreamSpec);
    if (!sfxStreamSpec->Open(sfxModule))
      return E_FAIL;
      // throw "Can't open sfx module";
    RINOK(CopyBlock(sfxStream, outStream));
  }

  RINOK(outArchive->UpdateItems(outStream, updatePairs2.Size(),updateCallback));
  return outStreamSpec->Close();
}
Exemple #8
0
void GetUpdatePairInfoList(
    const CDirItems &dirItems,
    const CObjectVector<CArcItem> &arcItems,
    NFileTimeType::EEnum fileTimeType,
    CRecordVector<CUpdatePair> &updatePairs)
{
  CUIntVector dirIndices, arcIndices;
  
  unsigned numDirItems = dirItems.Items.Size();
  unsigned numArcItems = arcItems.Size();
  
  CIntArr duplicatedArcItem(numArcItems);
  {
    int *vals = &duplicatedArcItem[0];
    for (unsigned i = 0; i < numArcItems; i++)
      vals[i] = 0;
  }

  {
    arcIndices.ClearAndSetSize(numArcItems);
    if (numArcItems != 0)
    {
      unsigned *vals = &arcIndices[0];
      for (unsigned i = 0; i < numArcItems; i++)
        vals[i] = i;
    }
    arcIndices.Sort(CompareArcItems, (void *)&arcItems);
    for (unsigned i = 0; i + 1 < numArcItems; i++)
      if (CompareArcItemsBase(
          arcItems[arcIndices[i]],
          arcItems[arcIndices[i + 1]]) == 0)
      {
        duplicatedArcItem[i] = 1;
        duplicatedArcItem[i + 1] = -1;
      }
  }

  UStringVector dirNames;
  {
    dirNames.ClearAndReserve(numDirItems);
    unsigned i;
    for (i = 0; i < numDirItems; i++)
      dirNames.AddInReserved(dirItems.GetLogPath(i));
    SortFileNames(dirNames, dirIndices);
    for (i = 0; i + 1 < numDirItems; i++)
    {
      const UString &s1 = dirNames[dirIndices[i]];
      const UString &s2 = dirNames[dirIndices[i + 1]];
      if (CompareFileNames(s1, s2) == 0)
        ThrowError(k_Duplicate_inDir_Message, s1, s2);
    }
  }
  
  unsigned dirIndex = 0;
  unsigned arcIndex = 0;

  int prevHostFile = -1;
  const UString *prevHostName = NULL;
  
  while (dirIndex < numDirItems || arcIndex < numArcItems)
  {
    CUpdatePair pair;
    
    int dirIndex2 = -1;
    int arcIndex2 = -1;
    const CDirItem *di = NULL;
    const CArcItem *ai = NULL;
    
    int compareResult = -1;
    const UString *name = NULL;
    
    if (dirIndex < numDirItems)
    {
      dirIndex2 = dirIndices[dirIndex];
      di = &dirItems.Items[dirIndex2];
    }

    if (arcIndex < numArcItems)
    {
      arcIndex2 = arcIndices[arcIndex];
      ai = &arcItems[arcIndex2];
      compareResult = 1;
      if (dirIndex < numDirItems)
      {
        compareResult = CompareFileNames(dirNames[dirIndex2], ai->Name);
        if (compareResult == 0)
        {
          if (di->IsDir() != ai->IsDir)
            compareResult = (ai->IsDir ? 1 : -1);
        }
      }
    }
    
    if (compareResult < 0)
    {
      name = &dirNames[dirIndex2];
      pair.State = NUpdateArchive::NPairState::kOnlyOnDisk;
      pair.DirIndex = dirIndex2;
      dirIndex++;
    }
    else if (compareResult > 0)
    {
      name = &ai->Name;
      pair.State = ai->Censored ?
          NUpdateArchive::NPairState::kOnlyInArchive:
          NUpdateArchive::NPairState::kNotMasked;
      pair.ArcIndex = arcIndex2;
      arcIndex++;
    }
    else
    {
      int dupl = duplicatedArcItem[arcIndex];
      if (dupl != 0)
        ThrowError(k_Duplicate_inArc_Message, ai->Name, arcItems[arcIndices[arcIndex + dupl]].Name);

      name = &dirNames[dirIndex2];
      if (!ai->Censored)
        ThrowError(k_NotCensoredCollision_Message, *name, ai->Name);
      
      pair.DirIndex = dirIndex2;
      pair.ArcIndex = arcIndex2;

      switch (ai->MTimeDefined ? MyCompareTime(
          ai->TimeType != - 1 ? (NFileTimeType::EEnum)ai->TimeType : fileTimeType,
          di->MTime, ai->MTime): 0)
      {
        case -1: pair.State = NUpdateArchive::NPairState::kNewInArchive; break;
        case  1: pair.State = NUpdateArchive::NPairState::kOldInArchive; break;
        default:
          pair.State = (ai->SizeDefined && di->Size == ai->Size) ?
              NUpdateArchive::NPairState::kSameFiles :
              NUpdateArchive::NPairState::kUnknowNewerFiles;
      }
      
      dirIndex++;
      arcIndex++;
    }
    
    if ((di && di->IsAltStream) ||
        (ai && ai->IsAltStream))
    {
      if (prevHostName)
      {
        unsigned hostLen = prevHostName->Len();
        if (name->Len() > hostLen)
          if ((*name)[hostLen] == ':' && CompareFileNames(*prevHostName, name->Left(hostLen)) == 0)
            pair.HostIndex = prevHostFile;
      }
    }
    else
    {
      prevHostFile = updatePairs.Size();
      prevHostName = name;
    }
    
    updatePairs.Add(pair);
  }

  updatePairs.ReserveDown();
}
Exemple #9
0
static HRESULT EnumerateDirItems(const NWildcard::CCensorNode &curNode,
    int phyParent, int logParent, const FString &phyPrefix,
    const UStringVector &addArchivePrefix,  // prefix from curNode
    CDirItems &dirItems,
    bool enterToSubFolders,
    IEnumDirItemCallback *callback,
    FStringVector &errorPaths,
    CRecordVector<DWORD> &errorCodes)
{
  if (!enterToSubFolders)
    if (curNode.NeedCheckSubDirs())
      enterToSubFolders = true;
  if (callback)
    RINOK(callback->ScanProgress(dirItems.GetNumFolders(), dirItems.Items.Size(), fs2us(phyPrefix)));

  // try direct_names case at first
  if (addArchivePrefix.IsEmpty() && !enterToSubFolders)
  {
    // check that all names are direct
    int i;
    for (i = 0; i < curNode.IncludeItems.Size(); i++)
    {
      const NWildcard::CItem &item = curNode.IncludeItems[i];
      if (item.Recursive || item.PathParts.Size() != 1)
        break;
      const UString &name = item.PathParts.Front();
      if (name.IsEmpty() || DoesNameContainWildCard(name))
        break;
    }
    if (i == curNode.IncludeItems.Size())
    {
      // all names are direct (no wildcards)
      // so we don't need file_system's dir enumerator
      CRecordVector<bool> needEnterVector;
      for (i = 0; i < curNode.IncludeItems.Size(); i++)
      {
        const NWildcard::CItem &item = curNode.IncludeItems[i];
        const UString &name = item.PathParts.Front();
        const FString fullPath = phyPrefix + us2fs(name);
        NFind::CFileInfo fi;
        if (!fi.Find(fullPath))
        {
          errorCodes.Add(::GetLastError());
          errorPaths.Add(fullPath);
          continue;
        }
        bool isDir = fi.IsDir();
        if (isDir && !item.ForDir || !isDir && !item.ForFile)
        {
          errorCodes.Add((DWORD)E_FAIL);
          errorPaths.Add(fullPath);
          continue;
        }
        {
          UStringVector pathParts;
          pathParts.Add(fs2us(fi.Name));
          if (curNode.CheckPathToRoot(false, pathParts, !isDir))
            continue;
        }
        AddDirFileInfo(phyParent, logParent, fi, dirItems.Items);
        if (!isDir)
          continue;
        
        UStringVector addArchivePrefixNew;
        const NWildcard::CCensorNode *nextNode = 0;
        int index = curNode.FindSubNode(name);
        if (index >= 0)
        {
          for (int t = needEnterVector.Size(); t <= index; t++)
            needEnterVector.Add(true);
          needEnterVector[index] = false;
          nextNode = &curNode.SubNodes[index];
        }
        else
        {
          nextNode = &curNode;
          addArchivePrefixNew.Add(name); // don't change it to fi.Name. It's for shortnames support
        }

        RINOK(EnumerateDirItems_Spec(*nextNode, phyParent, logParent, fi.Name, phyPrefix,
            addArchivePrefixNew, dirItems, true, callback, errorPaths, errorCodes));
      }
      for (i = 0; i < curNode.SubNodes.Size(); i++)
      {
        if (i < needEnterVector.Size())
          if (!needEnterVector[i])
            continue;
        const NWildcard::CCensorNode &nextNode = curNode.SubNodes[i];
        const FString fullPath = phyPrefix + us2fs(nextNode.Name);
        NFind::CFileInfo fi;
        if (!fi.Find(fullPath))
        {
          if (!nextNode.AreThereIncludeItems())
            continue;
          errorCodes.Add(::GetLastError());
          errorPaths.Add(fullPath);
          continue;
        }
        if (!fi.IsDir())
        {
          errorCodes.Add((DWORD)E_FAIL);
          errorPaths.Add(fullPath);
          continue;
        }

        RINOK(EnumerateDirItems_Spec(nextNode, phyParent, logParent, fi.Name, phyPrefix,
            UStringVector(), dirItems, false, callback, errorPaths, errorCodes));
      }
      return S_OK;
    }
  }


  NFind::CEnumerator enumerator(phyPrefix + FCHAR_ANY_MASK);
  for (int ttt = 0; ; ttt++)
  {
    NFind::CFileInfo fi;
    bool found;
    if (!enumerator.Next(fi, found))
    {
      errorCodes.Add(::GetLastError());
      errorPaths.Add(phyPrefix);
      break;
    }
    if (!found)
      break;

    if (callback && (ttt & 0xFF) == 0xFF)
      RINOK(callback->ScanProgress(dirItems.GetNumFolders(), dirItems.Items.Size(), fs2us(phyPrefix)));
    const UString &name = fs2us(fi.Name);
    bool enterToSubFolders2 = enterToSubFolders;
    UStringVector addArchivePrefixNew = addArchivePrefix;
    addArchivePrefixNew.Add(name);
    {
      UStringVector addArchivePrefixNewTemp(addArchivePrefixNew);
      if (curNode.CheckPathToRoot(false, addArchivePrefixNewTemp, !fi.IsDir()))
        continue;
    }
    if (curNode.CheckPathToRoot(true, addArchivePrefixNew, !fi.IsDir()))
    {
      AddDirFileInfo(phyParent, logParent, fi, dirItems.Items);
      if (fi.IsDir())
        enterToSubFolders2 = true;
    }
    if (!fi.IsDir())
      continue;

    const NWildcard::CCensorNode *nextNode = 0;
    if (addArchivePrefix.IsEmpty())
    {
      int index = curNode.FindSubNode(name);
      if (index >= 0)
        nextNode = &curNode.SubNodes[index];
    }
    if (!enterToSubFolders2 && nextNode == 0)
      continue;

    addArchivePrefixNew = addArchivePrefix;
    if (nextNode == 0)
    {
      nextNode = &curNode;
      addArchivePrefixNew.Add(name);
    }

    RINOK(EnumerateDirItems_Spec(*nextNode, phyParent, logParent, fi.Name, phyPrefix,
        addArchivePrefixNew, dirItems, enterToSubFolders2, callback, errorPaths, errorCodes));
  }
  return S_OK;
}