text: // Copyright (c) 2010 The WebM project authors. All Rights Reserved.
  //
  // Use of this source code is governed by a BSD-style license
  // that can be found in the LICENSE file in the root of the source
  // tree. An additional intellectual property rights grant can be found
  // in the file PATENTS.  All contributing project authors may
  // be found in the AUTHORS file in the root of the source tree.
  #include "comreg.h"
  
  #include <malloc.h>
  
  #include <cassert>
  #include <sstream>
  
  #include "registry.h"
  
  using std::wstring;
  using std::wostringstream;
  
  GUID ComReg::GUIDFromString(const wchar_t* const_str) {
    wchar_t* const str = const_cast<wchar_t*>(const_str);
  
    GUID guid;
  
    const HRESULT hr = CLSIDFromString(str, &guid);
    assert(SUCCEEDED(hr));  // TODO
    hr;
  
    return guid;
  }
  
  HRESULT ComReg::ComRegGetModuleFileName(HMODULE h, std::wstring& result) {
    DWORD n = 256;  // number of TCHARS
  
    for (;;) {
      void* const pv = _alloca(n * sizeof(wchar_t));
      wchar_t* const buf = (wchar_t*)pv;
  
      const DWORD dw = GetModuleFileNameW(h, buf, n);
  
      if (dw == 0) {  // error
  
        result.clear();
  
        const DWORD e = GetLastError();
        return HRESULT_FROM_WIN32(e);
      }
  
      if (dw >= n)  // truncation
      {
        n *= 2;
        continue;
      }
  
      result = buf;
      return S_OK;
    }
  }
  
  HRESULT ComReg::RegisterCustomFileType(const wchar_t* ext, const GUID& filter,
                                         const GUID& mediatype,
                                         const GUID& subtype) {
    Registry::Key parent, key;
  
    LONG e = parent.open(HKEY_CLASSES_ROOT, L"Media Type\\Extensions",
                         KEY_CREATE_SUB_KEY);
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    e = key.create<wchar_t>(parent, ext, 0, 0, KEY_SET_VALUE, 0);
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    wchar_t buf[guid_buflen];
  
    int n = StringFromGUID2(filter, buf, guid_buflen);
    assert(n == guid_buflen);
  
    e = key.set(L"Source Filter", buf);
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    if (mediatype != GUID_NULL) {
      n = StringFromGUID2(mediatype, buf, guid_buflen);
      assert(n == guid_buflen);
  
      e = key.set(L"Media Type", buf);
  
      if (e)
        return HRESULT_FROM_WIN32(e);
    }
  
    if (subtype != GUID_NULL) {
      n = StringFromGUID2(subtype, buf, guid_buflen);
      assert(n == guid_buflen);
  
      e = key.set(L"Subtype", buf);
  
      if (e)
        return HRESULT_FROM_WIN32(e);
    }
  
    return S_OK;
  }
  
  HRESULT ComReg::UnRegisterCustomFileType(const wchar_t* ext,
                                           const GUID& filter) {
    Registry::Key parent, key;
  
    LONG e = parent.open(HKEY_CLASSES_ROOT, L"Media Type\\Extensions",
                         KEY_ALL_ACCESS);
    if (e == ERROR_FILE_NOT_FOUND)
      return S_FALSE;
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    e = key.open(parent, ext, KEY_ALL_ACCESS);
  
    if (e == ERROR_FILE_NOT_FOUND)  // normal
      return S_FALSE;  // not an error if key does not already exist
  
    if (e)  // indicates a deeper problem
      return HRESULT_FROM_WIN32(e);
  
    wstring clsidstr;
  
    e = key.query(L"Source Filter", clsidstr);
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    const wchar_t* const const_str = clsidstr.c_str();
    wchar_t* const str = const_cast<wchar_t*>(const_str);
  
    CLSID clsid;
  
    const HRESULT hr = CLSIDFromString(str, &clsid);
    hr;
    assert(SUCCEEDED(hr));
  
    if (clsid != filter)
      return S_FALSE;
  
    key.close();
  
    e = Registry::DeleteKey(parent, ext);
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    return S_OK;
  }
  
  HRESULT ComReg::RegisterCustomFileType(
      const wchar_t* const* argv,  // array of check-byte strings
      const GUID& filter, const GUID& mediatype, const GUID& subtype) {
    assert(argv);
    assert(*argv);
    assert(filter != GUID_NULL);
    assert(mediatype != GUID_NULL);
    assert(subtype != GUID_NULL);
  
    Registry::Key parent, key1, key2;
  
    LONG e = parent.open(HKEY_CLASSES_ROOT, L"Media Type", KEY_CREATE_SUB_KEY);
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    wchar_t buf[guid_buflen];
  
    int n = StringFromGUID2(mediatype, buf, guid_buflen);
    assert(n == guid_buflen);
  
    e = key1.create<wchar_t>(parent, buf, 0, 0, KEY_SET_VALUE, 0);
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    n = StringFromGUID2(subtype, buf, guid_buflen);
    assert(n == guid_buflen);
  
    e = key2.create<wchar_t>(key1, buf, 0, 0, KEY_SET_VALUE, 0);
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    const wchar_t* arg = *argv;
    assert(arg);
  
    int i = 0;
  
    for (;;) {
      wostringstream os;
      os << i;
  
      const wstring name_ = os.str();
      const wchar_t* const name = name_.c_str();
  
      e = key2.set(name, arg);
  
      if (e)
        return HRESULT_FROM_WIN32(e);
  
      arg = *++argv;
  
      if (arg == 0)
        break;
  
      ++i;
    }
  
    n = StringFromGUID2(filter, buf, guid_buflen);
    assert(n == guid_buflen);
  
    e = key2.set(L"Source Filter", buf);
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    return S_OK;
  }
  
  static HRESULT CreateClsidKey(
      const wstring& clsid_keyname, const wchar_t* friendlyname,
      const wchar_t* inprocserver, const wchar_t* versionindependentprogid,
      const wchar_t* progid, bool insertable, bool control,
      ComReg::ThreadingModel threadingmodel, const GUID& typelib,
      const wchar_t* version, int toolboxbitmap) {
    Registry::Key clsid_key;
  
    LONG e = clsid_key.create(HKEY_CLASSES_ROOT, clsid_keyname);
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    if (friendlyname) {
      e = clsid_key.set(friendlyname);
  
      if (e)
        return HRESULT_FROM_WIN32(e);
    }
  
    if (insertable) {
      Registry::Key subkey;
  
      e = subkey.create(clsid_key, L"Insertable");
  
      if (e)
        return HRESULT_FROM_WIN32(e);
    }
  
    if (control) {
      Registry::Key subkey;
  
      e = subkey.create(clsid_key, L"Control");
  
      if (e)
        return HRESULT_FROM_WIN32(e);
    }
  
    if (versionindependentprogid) {
      Registry::Key subkey;
  
      e = subkey.create(clsid_key, L"VersionIndependentProgID");
  
      if (e)
        return HRESULT_FROM_WIN32(e);
  
      e = subkey.set(versionindependentprogid);
  
      if (e)
        return HRESULT_FROM_WIN32(e);
    }
  
    if (progid) {
      Registry::Key subkey;
  
      e = subkey.create(clsid_key, L"ProgID");
  
      if (e)
        return HRESULT_FROM_WIN32(e);
  
      e = subkey.set(progid);
  
      if (e)
        return HRESULT_FROM_WIN32(e);
    }
  
    if (inprocserver) {
      Registry::Key subkey;
  
      e = subkey.create(clsid_key, L"InprocServer32");
  
      if (e)
        return HRESULT_FROM_WIN32(e);
  
      e = subkey.set(inprocserver);
  
      if (e)
        return HRESULT_FROM_WIN32(e);
  
      if (threadingmodel != ComReg::kSingleThreading) {
        const wchar_t* const a[3] = {L"Apartment", L"Free", L"Both"};
        const wchar_t* const val = a[threadingmodel];
  
        e = subkey.set(L"ThreadingModel", val);
  
        if (e)
          return HRESULT_FROM_WIN32(e);
      }
    }
  
    if (typelib != GUID_NULL) {
      wchar_t guid_str[ComReg::guid_buflen];
  
      const int n = StringFromGUID2(typelib, guid_str, ComReg::guid_buflen);
      assert(n == ComReg::guid_buflen);
      n;
  
      Registry::Key subkey;
  
      e = subkey.create(clsid_key, L"TypeLib");
  
      if (e)
        return HRESULT_FROM_WIN32(e);
  
      e = subkey.set(guid_str);
  
      if (e)
        return HRESULT_FROM_WIN32(e);
    }
  
    if (version) {
      Registry::Key subkey;
  
      e = subkey.create(clsid_key, L"Version");
  
      if (e)
        return HRESULT_FROM_WIN32(e);
  
      e = subkey.set(version);
  
      if (e)
        return HRESULT_FROM_WIN32(e);
    }
  
    if ((toolboxbitmap > 0) && (inprocserver != 0)) {
      Registry::Key subkey;
  
      e = subkey.create(clsid_key, L"ToolBoxBitmap32");
  
      if (e)
        return HRESULT_FROM_WIN32(e);
  
      std::wostringstream os;
      os << inprocserver << L", " << toolboxbitmap;
  
      e = subkey.set(os.str().c_str());  // TODO: fix registry.h
  
      if (e)
        return HRESULT_FROM_WIN32(e);
    }
  
    return S_OK;
  }
  
  static HRESULT CreateProgidKey(
      const wstring& clsid_str, const wchar_t* friendlyname,
      const wchar_t* progid,
      const wchar_t* curver,  // only for versionindependentprogid
      bool insertable) {
    assert(progid);
  
    Registry::Key progid_key;
  
    LONG e = progid_key.create(HKEY_CLASSES_ROOT, progid);
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    if (friendlyname) {
      e = progid_key.set(friendlyname);
  
      if (e)
        return HRESULT_FROM_WIN32(e);
    }
  
    Registry::Key subkey;
  
    e = subkey.create(progid_key, L"CLSID");
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    e = subkey.set<wchar_t>(0, clsid_str);  // TODO: fix registry.h
  
    if (insertable) {
      e = subkey.create(progid_key, L"Insertable");
  
      if (e)
        return HRESULT_FROM_WIN32(e);
    }
  
    if (curver) {
      e = subkey.create(progid_key, L"CurVer");
  
      if (e)
        return HRESULT_FROM_WIN32(e);
  
      e = subkey.set(curver);
  
      if (e)
        return HRESULT_FROM_WIN32(e);
    }
  
    return S_OK;
  }
  
  HRESULT ComReg::RegisterCoclass(const GUID& clsid, const wchar_t* friendlyname,
                                  const wchar_t* inprocserver,
                                  const wchar_t* versionindependentprogid,
                                  const wchar_t* progid, bool insertable,
                                  bool control, ThreadingModel threadingmodel,
                                  const GUID& typelib, const wchar_t* version,
                                  int toolboxbitmap) {
    wchar_t clsid_str[guid_buflen];
  
    const int n = StringFromGUID2(clsid, clsid_str, guid_buflen);
    assert(n == guid_buflen);
    n;
  
    const wstring clsid_keyname = wstring(L"CLSID\\") + clsid_str;
  
    HRESULT hr =
        CreateClsidKey(clsid_keyname, friendlyname, inprocserver,
                       versionindependentprogid, progid, insertable, control,
                       threadingmodel, typelib, version, toolboxbitmap);
  
    if (hr != S_OK)
      return hr;
  
    if (versionindependentprogid) {
      hr = CreateProgidKey(clsid_str, friendlyname, versionindependentprogid,
                           progid,
                           insertable);  // TODO: pass false here?
  
      if (hr != S_OK)
        return hr;
    }
  
    if (progid) {
      hr = CreateProgidKey(clsid_str, friendlyname, progid,
                           0,  // no CurVer for progid
                           insertable);
  
      if (hr != S_OK)
        return hr;
    }
  
    return S_OK;
  }
  
  HRESULT ComReg::UnRegisterCoclass(const GUID& clsid) {
    enum { clsid_strlen = 39 };
    wchar_t clsid_str[clsid_strlen];
  
    const int n = StringFromGUID2(clsid, clsid_str, clsid_strlen);
    assert(n == clsid_strlen);
    n;
  
    const wstring clsid_keyname = wstring(L"CLSID\\") + clsid_str;
  
    Registry::Key clsid_key(HKEY_CLASSES_ROOT, clsid_keyname);
    // TODO: KEY_QUERY_VALUE | KEY_SET_VALUE);
  
    if (!clsid_key.is_open())
      return S_OK;
  
    Registry::Key subkey(clsid_key, L"VersionIndependentProgID");  // open
  
    if (subkey.is_open()) {
      wstring val;
  
      if (subkey(val)) {
        const DWORD dw = Registry::SHDeleteKey(HKEY_CLASSES_ROOT, val);
        assert((dw == ERROR_SUCCESS) || (dw == ERROR_FILE_NOT_FOUND));
        dw;
  
        // TODO: delete subkey from clsid_key
      }
    }
  
    subkey.open(clsid_key, L"ProgID");
  
    if (subkey.is_open()) {
      wstring val;
  
      if (subkey(val)) {
        const DWORD dw = Registry::SHDeleteKey(HKEY_CLASSES_ROOT, val);
        assert((dw == ERROR_SUCCESS) || (dw == ERROR_FILE_NOT_FOUND));
        dw;
  
        // TODO: delete subkey
      }
    }
  
    subkey.close();
    clsid_key.close();
  
    const DWORD dw = Registry::SHDeleteKey(HKEY_CLASSES_ROOT, clsid_keyname);
    assert((dw == ERROR_SUCCESS) || (dw == ERROR_FILE_NOT_FOUND));
    dw;
  
    return S_OK;
  }
  
  HRESULT ComReg::RegisterTypeLibResource(const wchar_t* fullpath_,
                                          const wchar_t* helpdir_) {
    ITypeLib* pTypeLib;
  
    HRESULT hr = LoadTypeLib(fullpath_, &pTypeLib);
    // Does not register if filename is a full path, which is what we want.
  
    if (FAILED(hr))
      return hr;
  
    assert(pTypeLib);
  
    wchar_t* const fullpath = const_cast<wchar_t*>(fullpath_);
    wchar_t* const helpdir = const_cast<wchar_t*>(helpdir_);
  
    hr = RegisterTypeLib(pTypeLib, fullpath, helpdir);
  
    pTypeLib->Release();
    pTypeLib = 0;
  
    return hr;
  }
  
  HRESULT ComReg::UnRegisterTypeLibResource(const wchar_t* fullpath) {
    ITypeLib* pTypeLib;
  
    HRESULT hr = LoadTypeLib(fullpath, &pTypeLib);
    // Does not register if filename is a full path, which is what we want.
  
    if (FAILED(hr))
      return hr;
  
    assert(pTypeLib);
  
    TLIBATTR* pLibAttr;
  
    hr = pTypeLib->GetLibAttr(&pLibAttr);
  
    if (FAILED(hr)) {
      pTypeLib->Release();
      pTypeLib = 0;
  
      return hr;
    }
  
    assert(pLibAttr);
    const TLIBATTR a(*pLibAttr);
  
    pTypeLib->ReleaseTLibAttr(pLibAttr);
  
    pTypeLib->Release();
    pTypeLib = 0;
  
    hr = UnRegisterTypeLib(a.guid, a.wMajorVerNum, a.wMinorVerNum, a.lcid,
                           a.syskind);
  
    // TYPE_E_REGISTRYACCESS is returned when the type lib was not registered.
    // Since DShow filters are supposed to call DllUnregisterServer() from
    // DllRegisterServer(), and DllUnregisterServer() must (attempt to) unregister
    // type libraries, suppress this error because it's going to be returned every
    // time a filter is registered for the first time on a system.
    if (hr == TYPE_E_REGISTRYACCESS)
      hr = S_FALSE;
  
    return hr;
  }
  
  HRESULT ComReg::GetTypeLibAttr(const wchar_t* fullpath, TLIBATTR& a) {
    ITypeLib* pTypeLib;
  
    HRESULT hr = LoadTypeLib(fullpath, &pTypeLib);
    // Does not register if filename is a full path, which is what we want.
  
    if (FAILED(hr))
      return hr;
  
    assert(pTypeLib);
  
    TLIBATTR* pLibAttr;
  
    hr = pTypeLib->GetLibAttr(&pLibAttr);
  
    if (FAILED(hr)) {
      pTypeLib->Release();
      pTypeLib = 0;
  
      return hr;
    }
  
    assert(pLibAttr);
    a = *pLibAttr;
  
    pTypeLib->ReleaseTLibAttr(pLibAttr);
  
    pTypeLib->Release();
    pTypeLib = 0;
  
    return S_OK;
  }
  
  #if (_WIN32_WINNT >= 0x0601)
  HRESULT ComReg::RegisterByteStreamHandler(const wchar_t* ext, const GUID& clsid,
                                            const wchar_t* friendly_name) {
    const wchar_t parent_subkey[] =
        L"Software\\Microsoft\\Windows Media Foundation\\ByteStreamHandlers";
    const wstring subkey = wstring(parent_subkey) + L"\\" + ext;
  
    Registry::Key key;
  
    LONG e = key.create<wchar_t>(HKEY_LOCAL_MACHINE, subkey.c_str(), 0, 0,
                                 KEY_WRITE, 0);
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    wchar_t buf[guid_buflen];
    const int n = StringFromGUID2(clsid, buf, guid_buflen);
    n;
    assert(n == guid_buflen);
  
    e = key.set(buf, friendly_name);
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    return S_OK;
  }
  
  HRESULT ComReg::UnRegisterByteStreamHandler(const wchar_t* ext,
                                              const GUID& clsid) {
    Registry::Key parent, key;
    LONG e = parent.open(HKEY_LOCAL_MACHINE,
                         L"Software\\Microsoft"
                         L"\\Windows Media Foundation\\ByteStreamHandlers",
                         KEY_ALL_ACCESS);
  
    if (e == ERROR_FILE_NOT_FOUND)  // weird
      return S_FALSE;
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    e = key.open(parent, ext, KEY_ALL_ACCESS);
  
    if (e == ERROR_FILE_NOT_FOUND)  // normal
      return S_FALSE;  // not an error if key does not already exist
  
    if (e)  // indicates a deeper problem
      return HRESULT_FROM_WIN32(e);
  
    wchar_t clsidstr[guid_buflen];  // name
  
    const int n = StringFromGUID2(clsid, clsidstr, guid_buflen);
    n;
    assert(n == guid_buflen);
  
    wstring value;
  
    e = key.query(clsidstr, value);
  
    if (e == ERROR_FILE_NOT_FOUND)
      return S_FALSE;
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    key.close();
  
    e = Registry::DeleteKey(parent, ext);
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    return S_OK;
  }
  #endif
  
  HRESULT ComReg::RegisterProtocolSource(const wchar_t* protocol,
                                         const wchar_t* ext, const GUID& filter) {
    if (protocol == 0)
      return E_INVALIDARG;
  
    if (ext == 0)
      return E_INVALIDARG;
  
    if (filter == GUID_NULL)
      return E_INVALIDARG;
  
    Registry::Key pk;  // protocol
  
    LONG e = pk.open(HKEY_CLASSES_ROOT, protocol, KEY_READ | KEY_WRITE);
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    Registry::Key ek;  // extensions
  
    e = ek.open(pk, L"Extensions", KEY_READ | KEY_WRITE);
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    wchar_t buf[guid_buflen];
  
    const int n = StringFromGUID2(filter, buf, guid_buflen);
    assert(n == guid_buflen);
    n;
  
    e = ek.set(ext, buf);
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    return S_OK;
  }
  
  HRESULT ComReg::UnRegisterProtocolSource(const wchar_t* protocol,
                                           const wchar_t* ext,
                                           const GUID& filter) {
    if (protocol == 0)
      return E_INVALIDARG;
  
    if (ext == 0)
      return E_INVALIDARG;
  
    if (filter == GUID_NULL)
      return E_INVALIDARG;
  
    Registry::Key pk;  // protocol
  
    LONG e = pk.open(HKEY_CLASSES_ROOT, protocol, KEY_READ | KEY_WRITE);
  
    if (e == ERROR_FILE_NOT_FOUND)
      return S_FALSE;
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    Registry::Key ek;  // extensions
  
    e = ek.open(pk, L"Extensions", KEY_READ | KEY_WRITE);
  
    if (e == ERROR_FILE_NOT_FOUND)
      return S_FALSE;
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    wstring val;
  
    e = ek.query(ext, val);
  
    if (e == ERROR_FILE_NOT_FOUND)
      return S_FALSE;
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    GUID guid;
  
    const HRESULT hr = ::CLSIDFromString(val.c_str(), &guid);
  
    if (FAILED(hr))
      return S_FALSE;
  
    if (guid != filter)
      return S_FALSE;
  
    e = ::RegDeleteValue(ek, ext);
  
    if (e)
      return HRESULT_FROM_WIN32(e);
  
    return S_OK;
  }
  