text: /*
   *	COMPOBJ library
   *
   *	Copyright 1995	Martin von Loewis
   *	Copyright 1998	Justin Bradford
   *      Copyright 1999  Francis Beaudet
   *      Copyright 1999  Sylvain St-Germain
   *      Copyright 2002  Marcus Meissner
   *      Copyright 2004  Mike Hearn
   *      Copyright 2005-2006 Robert Shearman (for CodeWeavers)
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
   *
   * Note
   * 1. COINIT_MULTITHREADED is 0; it is the lack of COINIT_APARTMENTTHREADED
   *    Therefore do not test against COINIT_MULTITHREADED
   *
   * TODO list:           (items bunched together depend on each other)
   *
   *   - Implement the service control manager (in rpcss) to keep track
   *     of registered class objects: ISCM::ServerRegisterClsid et al
   *   - Implement the OXID resolver so we don't need magic endpoint names for
   *     clients and servers to meet up
   *
   */
  
  #include "config.h"
  
  #include <stdarg.h>
  #include <stdio.h>
  #include <string.h>
  #include <assert.h>
  
  #define COBJMACROS
  #define NONAMELESSUNION
  
  #include "ntstatus.h"
  #define WIN32_NO_STATUS
  #include "windef.h"
  #include "winbase.h"
  #include "winerror.h"
  #include "winreg.h"
  #include "winuser.h"
  #define USE_COM_CONTEXT_DEF
  #include "objbase.h"
  #include "ole2.h"
  #include "ole2ver.h"
  #include "ctxtcall.h"
  #include "dde.h"
  #include "servprov.h"
  
  #include "initguid.h"
  #include "compobj_private.h"
  #include "moniker.h"
  
  #include "wine/unicode.h"
  #include "wine/debug.h"
  
  WINE_DEFAULT_DEBUG_CHANNEL(ole);
  
  /****************************************************************************
   * This section defines variables internal to the COM module.
   */
  
  static APARTMENT *MTA; /* protected by csApartment */
  static APARTMENT *MainApartment; /* the first STA apartment */
  static struct list apts = LIST_INIT( apts ); /* protected by csApartment */
  
  static CRITICAL_SECTION csApartment;
  static CRITICAL_SECTION_DEBUG critsect_debug =
  {
      0, 0, &csApartment,
      { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
        0, 0, { (DWORD_PTR)(__FILE__ ": csApartment") }
  };
  static CRITICAL_SECTION csApartment = { &critsect_debug, -1, 0, 0, 0, 0 };
  
  enum comclass_threadingmodel
  {
      ThreadingModel_Apartment = 1,
      ThreadingModel_Free      = 2,
      ThreadingModel_No        = 3,
      ThreadingModel_Both      = 4,
      ThreadingModel_Neutral   = 5
  };
  
  enum comclass_miscfields
  {
      MiscStatus          = 1,
      MiscStatusIcon      = 2,
      MiscStatusContent   = 4,
      MiscStatusThumbnail = 8,
      MiscStatusDocPrint  = 16
  };
  
  struct comclassredirect_data
  {
      ULONG size;
      BYTE  res;
      BYTE  miscmask;
      BYTE  res1[2];
      DWORD model;
      GUID  clsid;
      GUID  alias;
      GUID  clsid2;
      GUID  tlbid;
      ULONG name_len;
      ULONG name_offset;
      ULONG progid_len;
      ULONG progid_offset;
      ULONG clrdata_len;
      ULONG clrdata_offset;
      DWORD miscstatus;
      DWORD miscstatuscontent;
      DWORD miscstatusthumbnail;
      DWORD miscstatusicon;
      DWORD miscstatusdocprint;
  };
  
  struct ifacepsredirect_data
  {
      ULONG size;
      DWORD mask;
      GUID  iid;
      ULONG nummethods;
      GUID  tlbid;
      GUID  base;
      ULONG name_len;
      ULONG name_offset;
  };
  
  struct progidredirect_data
  {
      ULONG size;
      DWORD reserved;
      ULONG clsid_offset;
  };
  
  struct class_reg_data
  {
      union
      {
          struct
          {
              struct comclassredirect_data *data;
              void *section;
              HANDLE hactctx;
          } actctx;
          HKEY hkey;
      } u;
      BOOL hkey;
  };
  
  struct registered_psclsid
  {
      struct list entry;
      IID iid;
      CLSID clsid;
  };
  
  static struct list registered_psclsid_list = LIST_INIT(registered_psclsid_list);
  
  static CRITICAL_SECTION cs_registered_psclsid_list;
  static CRITICAL_SECTION_DEBUG psclsid_cs_debug =
  {
      0, 0, &cs_registered_psclsid_list,
      { &psclsid_cs_debug.ProcessLocksList, &psclsid_cs_debug.ProcessLocksList },
        0, 0, { (DWORD_PTR)(__FILE__ ": cs_registered_psclsid_list") }
  };
  static CRITICAL_SECTION cs_registered_psclsid_list = { &psclsid_cs_debug, -1, 0, 0, 0, 0 };
  
  /*
   * This is a marshallable object exposing registered local servers.
   * IServiceProvider is used only because it happens meet requirements
   * and already has proxy/stub code. If more functionality is needed,
   * a custom interface may be used instead.
   */
  struct LocalServer
  {
      IServiceProvider IServiceProvider_iface;
      LONG ref;
      APARTMENT *apt;
      IStream *marshal_stream;
  };
  
  /*
   * This lock count counts the number of times CoInitialize is called. It is
   * decreased every time CoUninitialize is called. When it hits 0, the COM
   * libraries are freed
   */
  static LONG s_COMLockCount = 0;
  /* Reference count used by CoAddRefServerProcess/CoReleaseServerProcess */
  static LONG s_COMServerProcessReferences = 0;
  
  /*
   * This linked list contains the list of registered class objects. These
   * are mostly used to register the factories for out-of-proc servers of OLE
   * objects.
   *
   * TODO: Make this data structure aware of inter-process communication. This
   *       means that parts of this will be exported to rpcss.
   */
  typedef struct tagRegisteredClass
  {
    struct list entry;
    CLSID     classIdentifier;
    OXID      apartment_id;
    LPUNKNOWN classObject;
    DWORD     runContext;
    DWORD     connectFlags;
    DWORD     dwCookie;
    void     *RpcRegistration;
  } RegisteredClass;
  
  static struct list RegisteredClassList = LIST_INIT(RegisteredClassList);
  
  static CRITICAL_SECTION csRegisteredClassList;
  static CRITICAL_SECTION_DEBUG class_cs_debug =
  {
      0, 0, &csRegisteredClassList,
      { &class_cs_debug.ProcessLocksList, &class_cs_debug.ProcessLocksList },
        0, 0, { (DWORD_PTR)(__FILE__ ": csRegisteredClassList") }
  };
  static CRITICAL_SECTION csRegisteredClassList = { &class_cs_debug, -1, 0, 0, 0, 0 };
  
  static inline enum comclass_miscfields dvaspect_to_miscfields(DWORD aspect)
  {
      switch (aspect)
      {
      case DVASPECT_CONTENT:
          return MiscStatusContent;
      case DVASPECT_THUMBNAIL:
          return MiscStatusThumbnail;
      case DVASPECT_ICON:
          return MiscStatusIcon;
      case DVASPECT_DOCPRINT:
          return MiscStatusDocPrint;
      default:
          return MiscStatus;
      };
  }
  
  BOOL actctx_get_miscstatus(const CLSID *clsid, DWORD aspect, DWORD *status)
  {
      ACTCTX_SECTION_KEYED_DATA data;
  
      data.cbSize = sizeof(data);
      if (FindActCtxSectionGuid(0, NULL, ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION,
                                clsid, &data))
      {
          struct comclassredirect_data *comclass = (struct comclassredirect_data*)data.lpData;
          enum comclass_miscfields misc = dvaspect_to_miscfields(aspect);
  
          if (!(comclass->miscmask & misc))
          {
              if (!(comclass->miscmask & MiscStatus))
              {
                  *status = 0;
                  return TRUE;
              }
              misc = MiscStatus;
          }
  
          switch (misc)
          {
          case MiscStatus:
              *status = comclass->miscstatus;
              break;
          case MiscStatusIcon:
              *status = comclass->miscstatusicon;
              break;
          case MiscStatusContent:
              *status = comclass->miscstatuscontent;
              break;
          case MiscStatusThumbnail:
              *status = comclass->miscstatusthumbnail;
              break;
          case MiscStatusDocPrint:
              *status = comclass->miscstatusdocprint;
              break;
          default:
             ;
          };
  
          return TRUE;
      }
      else
          return FALSE;
  }
  
  /* wrapper for NtCreateKey that creates the key recursively if necessary */
  static NTSTATUS create_key( HKEY *retkey, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr )
  {
      NTSTATUS status = NtCreateKey( (HANDLE *)retkey, access, attr, 0, NULL, 0, NULL );
  
      if (status == STATUS_OBJECT_NAME_NOT_FOUND)
      {
          HANDLE subkey, root = attr->RootDirectory;
          WCHAR *buffer = attr->ObjectName->Buffer;
          DWORD attrs, pos = 0, i = 0, len = attr->ObjectName->Length / sizeof(WCHAR);
          UNICODE_STRING str;
  
          while (i < len && buffer[i] != '\\') i++;
          if (i == len) return status;
  
          attrs = attr->Attributes;
          attr->ObjectName = &str;
  
          while (i < len)
          {
              str.Buffer = buffer + pos;
              str.Length = (i - pos) * sizeof(WCHAR);
              status = NtCreateKey( &subkey, access, attr, 0, NULL, 0, NULL );
              if (attr->RootDirectory != root) NtClose( attr->RootDirectory );
              if (status) return status;
              attr->RootDirectory = subkey;
              while (i < len && buffer[i] == '\\') i++;
              pos = i;
              while (i < len && buffer[i] != '\\') i++;
          }
          str.Buffer = buffer + pos;
          str.Length = (i - pos) * sizeof(WCHAR);
          attr->Attributes = attrs;
          status = NtCreateKey( (PHANDLE)retkey, access, attr, 0, NULL, 0, NULL );
          if (attr->RootDirectory != root) NtClose( attr->RootDirectory );
      }
      return status;
  }
  
  static const WCHAR classes_rootW[] =
      {'\\','R','e','g','i','s','t','r','y','\\','M','a','c','h','i','n','e',
       '\\','S','o','f','t','w','a','r','e','\\','C','l','a','s','s','e','s',0};
  
  static HKEY classes_root_hkey;
  
  /* create the special HKEY_CLASSES_ROOT key */
  static HKEY create_classes_root_hkey(DWORD access)
  {
      HKEY hkey, ret = 0;
      OBJECT_ATTRIBUTES attr;
      UNICODE_STRING name;
  
      attr.Length = sizeof(attr);
      attr.RootDirectory = 0;
      attr.ObjectName = &name;
      attr.Attributes = 0;
      attr.SecurityDescriptor = NULL;
      attr.SecurityQualityOfService = NULL;
      RtlInitUnicodeString( &name, classes_rootW );
      if (create_key( &hkey, access, &attr )) return 0;
      TRACE( "%s -> %p\n", debugstr_w(attr.ObjectName->Buffer), hkey );
  
      if (!(access & KEY_WOW64_64KEY))
      {
          if (!(ret = InterlockedCompareExchangePointer( (void **)&classes_root_hkey, hkey, 0 )))
              ret = hkey;
          else
              NtClose( hkey );  /* somebody beat us to it */
      }
      else
          ret = hkey;
      return ret;
  }
  
  /* map the hkey from special root to normal key if necessary */
  static inline HKEY get_classes_root_hkey( HKEY hkey, REGSAM access )
  {
      HKEY ret = hkey;
      const BOOL is_win64 = sizeof(void*) > sizeof(int);
      const BOOL force_wow32 = is_win64 && (access & KEY_WOW64_32KEY);
  
      if (hkey == HKEY_CLASSES_ROOT &&
          ((access & KEY_WOW64_64KEY) || !(ret = classes_root_hkey)))
          ret = create_classes_root_hkey(MAXIMUM_ALLOWED | (access & KEY_WOW64_64KEY));
      if (force_wow32 && ret && ret == classes_root_hkey)
      {
          static const WCHAR wow6432nodeW[] = {'W','o','w','6','4','3','2','N','o','d','e',0};
          access &= ~KEY_WOW64_32KEY;
          if (create_classes_key(classes_root_hkey, wow6432nodeW, access, &hkey))
              return 0;
          ret = hkey;
      }
  
      return ret;
  }
  
  LSTATUS create_classes_key( HKEY hkey, const WCHAR *name, REGSAM access, HKEY *retkey )
  {
      OBJECT_ATTRIBUTES attr;
      UNICODE_STRING nameW;
  
      if (!(hkey = get_classes_root_hkey( hkey, access ))) return ERROR_INVALID_HANDLE;
  
      attr.Length = sizeof(attr);
      attr.RootDirectory = hkey;
      attr.ObjectName = &nameW;
      attr.Attributes = 0;
      attr.SecurityDescriptor = NULL;
      attr.SecurityQualityOfService = NULL;
      RtlInitUnicodeString( &nameW, name );
  
      return RtlNtStatusToDosError( create_key( retkey, access, &attr ) );
  }
  
  LSTATUS open_classes_key( HKEY hkey, const WCHAR *name, REGSAM access, HKEY *retkey )
  {
      OBJECT_ATTRIBUTES attr;
      UNICODE_STRING nameW;
  
      if (!(hkey = get_classes_root_hkey( hkey, access ))) return ERROR_INVALID_HANDLE;
  
      attr.Length = sizeof(attr);
      attr.RootDirectory = hkey;
      attr.ObjectName = &nameW;
      attr.Attributes = 0;
      attr.SecurityDescriptor = NULL;
      attr.SecurityQualityOfService = NULL;
      RtlInitUnicodeString( &nameW, name );
  
      return RtlNtStatusToDosError( NtOpenKey( (HANDLE *)retkey, access, &attr ) );
  }
  
  /*****************************************************************************
   * This section contains OpenDllList definitions
   *
   * The OpenDllList contains only handles of dll loaded by CoGetClassObject or
   * other functions that do LoadLibrary _without_ giving back a HMODULE.
   * Without this list these handles would never be freed.
   *
   * FIXME: a DLL that says OK when asked for unloading is unloaded in the
   * next unload-call but not before 600 sec.
   */
  
  typedef HRESULT (CALLBACK *DllGetClassObjectFunc)(REFCLSID clsid, REFIID iid, LPVOID *ppv);
  typedef HRESULT (WINAPI *DllCanUnloadNowFunc)(void);
  
  typedef struct tagOpenDll
  {
    LONG refs;
    LPWSTR library_name;
    HANDLE library;
    DllGetClassObjectFunc DllGetClassObject;
    DllCanUnloadNowFunc DllCanUnloadNow;
    struct list entry;
  } OpenDll;
  
  static struct list openDllList = LIST_INIT(openDllList);
  
  static CRITICAL_SECTION csOpenDllList;
  static CRITICAL_SECTION_DEBUG dll_cs_debug =
  {
      0, 0, &csOpenDllList,
      { &dll_cs_debug.ProcessLocksList, &dll_cs_debug.ProcessLocksList },
        0, 0, { (DWORD_PTR)(__FILE__ ": csOpenDllList") }
  };
  static CRITICAL_SECTION csOpenDllList = { &dll_cs_debug, -1, 0, 0, 0, 0 };
  
  struct apartment_loaded_dll
  {
      struct list entry;
      OpenDll *dll;
      DWORD unload_time;
      BOOL multi_threaded;
  };
  
  static const WCHAR wszAptWinClass[] = {'O','l','e','M','a','i','n','T','h','r','e','a','d','W','n','d','C','l','a','s','s',0};
  
  static ATOM apt_win_class;
  
  /*****************************************************************************
   * This section contains OpenDllList implementation
   */
  
  static OpenDll *COMPOBJ_DllList_Get(LPCWSTR library_name)
  {
      OpenDll *ptr;
      OpenDll *ret = NULL;
      EnterCriticalSection(&csOpenDllList);
      LIST_FOR_EACH_ENTRY(ptr, &openDllList, OpenDll, entry)
      {
          if (!strcmpiW(library_name, ptr->library_name) &&
              (InterlockedIncrement(&ptr->refs) != 1) /* entry is being destroy if == 1 */)
          {
              ret = ptr;
              break;
          }
      }
      LeaveCriticalSection(&csOpenDllList);
      return ret;
  }
  
  /* caller must ensure that library_name is not already in the open dll list */
  static HRESULT COMPOBJ_DllList_Add(LPCWSTR library_name, OpenDll **ret)
  {
      OpenDll *entry;
      int len;
      HRESULT hr = S_OK;
      HANDLE hLibrary;
      DllCanUnloadNowFunc DllCanUnloadNow;
      DllGetClassObjectFunc DllGetClassObject;
  
      TRACE("%s\n", debugstr_w(library_name));
  
      *ret = COMPOBJ_DllList_Get(library_name);
      if (*ret) return S_OK;
  
      /* do this outside the csOpenDllList to avoid creating a lock dependency on
       * the loader lock */
      hLibrary = LoadLibraryExW(library_name, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
      if (!hLibrary)
      {
          ERR("couldn't load in-process dll %s\n", debugstr_w(library_name));
          /* failure: DLL could not be loaded */
          return E_ACCESSDENIED; /* FIXME: or should this be CO_E_DLLNOTFOUND? */
      }
  
      DllCanUnloadNow = (void *)GetProcAddress(hLibrary, "DllCanUnloadNow");
      /* Note: failing to find DllCanUnloadNow is not a failure */
      DllGetClassObject = (void *)GetProcAddress(hLibrary, "DllGetClassObject");
      if (!DllGetClassObject)
      {
          /* failure: the dll did not export DllGetClassObject */
          ERR("couldn't find function DllGetClassObject in %s\n", debugstr_w(library_name));
          FreeLibrary(hLibrary);
          return CO_E_DLLNOTFOUND;
      }
  
      EnterCriticalSection( &csOpenDllList );
  
      *ret = COMPOBJ_DllList_Get(library_name);
      if (*ret)
      {
          /* another caller to this function already added the dll while we
           * weren't in the critical section */
          FreeLibrary(hLibrary);
      }
      else
      {
          len = strlenW(library_name);
          entry = HeapAlloc(GetProcessHeap(),0, sizeof(OpenDll));
          if (entry)
              entry->library_name = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
          if (entry && entry->library_name)
          {
              memcpy(entry->library_name, library_name, (len + 1)*sizeof(WCHAR));
              entry->library = hLibrary;
              entry->refs = 1;
              entry->DllCanUnloadNow = DllCanUnloadNow;
              entry->DllGetClassObject = DllGetClassObject;
              list_add_tail(&openDllList, &entry->entry);
              *ret = entry;
          }
          else
          {
              HeapFree(GetProcessHeap(), 0, entry);
              hr = E_OUTOFMEMORY;
              FreeLibrary(hLibrary);
          }
      }
  
      LeaveCriticalSection( &csOpenDllList );
  
      return hr;
  }
  
  /* pass FALSE for free_entry to release a reference without destroying the
   * entry if it reaches zero or TRUE otherwise */
  static void COMPOBJ_DllList_ReleaseRef(OpenDll *entry, BOOL free_entry)
  {
      if (!InterlockedDecrement(&entry->refs) && free_entry)
      {
          EnterCriticalSection(&csOpenDllList);
          list_remove(&entry->entry);
          LeaveCriticalSection(&csOpenDllList);
  
          TRACE("freeing %p\n", entry->library);
          FreeLibrary(entry->library);
  
          HeapFree(GetProcessHeap(), 0, entry->library_name);
          HeapFree(GetProcessHeap(), 0, entry);
      }
  }
  
  /* frees memory associated with active dll list */
  static void COMPOBJ_DllList_Free(void)
  {
      OpenDll *entry, *cursor2;
      EnterCriticalSection(&csOpenDllList);
      LIST_FOR_EACH_ENTRY_SAFE(entry, cursor2, &openDllList, OpenDll, entry)
      {
          list_remove(&entry->entry);
  
          HeapFree(GetProcessHeap(), 0, entry->library_name);
          HeapFree(GetProcessHeap(), 0, entry);
      }
      LeaveCriticalSection(&csOpenDllList);
      DeleteCriticalSection(&csOpenDllList);
  }
  
  /******************************************************************************
   * Manage apartments.
   */
  
  static DWORD apartment_addref(struct apartment *apt)
  {
      DWORD refs = InterlockedIncrement(&apt->refs);
      TRACE("%s: before = %d\n", wine_dbgstr_longlong(apt->oxid), refs - 1);
      return refs;
  }
  
  /* allocates memory and fills in the necessary fields for a new apartment
   * object. must be called inside apartment cs */
  static APARTMENT *apartment_construct(DWORD model)
  {
      APARTMENT *apt;
  
      TRACE("creating new apartment, model=%d\n", model);
  
      apt = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*apt));
      apt->tid = GetCurrentThreadId();
  
      list_init(&apt->proxies);
      list_init(&apt->stubmgrs);
      list_init(&apt->loaded_dlls);
      apt->ipidc = 0;
      apt->refs = 1;
      apt->remunk_exported = FALSE;
      apt->oidc = 1;
      InitializeCriticalSection(&apt->cs);
      DEBUG_SET_CRITSEC_NAME(&apt->cs, "apartment");
  
      apt->multi_threaded = !(model & COINIT_APARTMENTTHREADED);
  
      if (apt->multi_threaded)
      {
          /* FIXME: should be randomly generated by in an RPC call to rpcss */
          apt->oxid = ((OXID)GetCurrentProcessId() << 32) | 0xcafe;
      }
      else
      {
          /* FIXME: should be randomly generated by in an RPC call to rpcss */
          apt->oxid = ((OXID)GetCurrentProcessId() << 32) | GetCurrentThreadId();
      }
  
      TRACE("Created apartment on OXID %s\n", wine_dbgstr_longlong(apt->oxid));
  
      list_add_head(&apts, &apt->entry);
  
      return apt;
  }
  
  /* gets and existing apartment if one exists or otherwise creates an apartment
   * structure which stores OLE apartment-local information and stores a pointer
   * to it in the thread-local storage */
  static APARTMENT *apartment_get_or_create(DWORD model)
  {
      APARTMENT *apt = COM_CurrentApt();
  
      if (!apt)
      {
          if (model & COINIT_APARTMENTTHREADED)
          {
              EnterCriticalSection(&csApartment);
  
              apt = apartment_construct(model);
              if (!MainApartment)
              {
                  MainApartment = apt;
                  apt->main = TRUE;
                  TRACE("Created main-threaded apartment with OXID %s\n", wine_dbgstr_longlong(apt->oxid));
              }
  
              LeaveCriticalSection(&csApartment);
  
              if (apt->main)
                  apartment_createwindowifneeded(apt);
          }
          else
          {
              EnterCriticalSection(&csApartment);
  
              /* The multi-threaded apartment (MTA) contains zero or more threads interacting
               * with free threaded (ie thread safe) COM objects. There is only ever one MTA
               * in a process */
              if (MTA)
              {
                  TRACE("entering the multithreaded apartment %s\n", wine_dbgstr_longlong(MTA->oxid));
                  apartment_addref(MTA);
              }
              else
                  MTA = apartment_construct(model);
  
              apt = MTA;
  
              LeaveCriticalSection(&csApartment);
          }
          COM_CurrentInfo()->apt = apt;
      }
  
      return apt;
  }
  
  static inline BOOL apartment_is_model(const APARTMENT *apt, DWORD model)
  {
      return (apt->multi_threaded == !(model & COINIT_APARTMENTTHREADED));
  }
  
  /* gets the multi-threaded apartment if it exists. The caller must
   * release the reference from the apartment as soon as the apartment pointer
   * is no longer required. */
  static APARTMENT *apartment_find_mta(void)
  {
      APARTMENT *apt;
  
      EnterCriticalSection(&csApartment);
  
      if ((apt = MTA))
          apartment_addref(apt);
  
      LeaveCriticalSection(&csApartment);
  
      return apt;
  }
  
  /* Return the current apartment if it exists, or, failing that, the MTA. Caller
   * must free the returned apartment in either case. */
  APARTMENT *apartment_get_current_or_mta(void)
  {
      APARTMENT *apt = COM_CurrentApt();
      if (apt)
      {
          apartment_addref(apt);
          return apt;
      }
      return apartment_find_mta();
  }
  
  static void COM_RevokeRegisteredClassObject(RegisteredClass *curClass)
  {
      list_remove(&curClass->entry);
  
      if (curClass->runContext & CLSCTX_LOCAL_SERVER)
          RPC_StopLocalServer(curClass->RpcRegistration);
  
      IUnknown_Release(curClass->classObject);
      HeapFree(GetProcessHeap(), 0, curClass);
  }
  
  static void COM_RevokeAllClasses(const struct apartment *apt)
  {
    RegisteredClass *curClass, *cursor;
  
    EnterCriticalSection( &csRegisteredClassList );
  
    LIST_FOR_EACH_ENTRY_SAFE(curClass, cursor, &RegisteredClassList, RegisteredClass, entry)
    {
      if (curClass->apartment_id == apt->oxid)
        COM_RevokeRegisteredClassObject(curClass);
    }
  
    LeaveCriticalSection( &csRegisteredClassList );
  }
  
  static void revoke_registered_psclsids(void)
  {
      struct registered_psclsid *psclsid, *psclsid2;
  
      EnterCriticalSection( &cs_registered_psclsid_list );
  
      LIST_FOR_EACH_ENTRY_SAFE(psclsid, psclsid2, &registered_psclsid_list, struct registered_psclsid, entry)
      {
          list_remove(&psclsid->entry);
          HeapFree(GetProcessHeap(), 0, psclsid);
      }
  
      LeaveCriticalSection( &cs_registered_psclsid_list );
  }
  
  /******************************************************************************
   * Implementation of the manual reset event object. (CLSID_ManualResetEvent)
   */
  
  typedef struct ManualResetEvent {
      ISynchronize        ISynchronize_iface;
      ISynchronizeHandle  ISynchronizeHandle_iface;
      LONG ref;
      HANDLE event;
  } MREImpl;
  
  static inline MREImpl *impl_from_ISynchronize(ISynchronize *iface)
  {
      return CONTAINING_RECORD(iface, MREImpl, ISynchronize_iface);
  }
  
  static HRESULT WINAPI ISynchronize_fnQueryInterface(ISynchronize *iface, REFIID riid, void **ppv)
  {
      MREImpl *This = impl_from_ISynchronize(iface);
  
      TRACE("%p (%s, %p)\n", This, debugstr_guid(riid), ppv);
  
      if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISynchronize)) {
          *ppv = &This->ISynchronize_iface;
      }else if(IsEqualGUID(riid, &IID_ISynchronizeHandle)) {
          *ppv = &This->ISynchronizeHandle_iface;
      }else {
          ERR("Unknown interface %s requested.\n", debugstr_guid(riid));
          *ppv = NULL;
          return E_NOINTERFACE;
      }
  
      IUnknown_AddRef((IUnknown*)*ppv);
      return S_OK;
  }
  
  static ULONG WINAPI ISynchronize_fnAddRef(ISynchronize *iface)
  {
      MREImpl *This = impl_from_ISynchronize(iface);
      LONG ref = InterlockedIncrement(&This->ref);
      TRACE("%p - ref %d\n", This, ref);
  
      return ref;
  }
  
  static ULONG WINAPI ISynchronize_fnRelease(ISynchronize *iface)
  {
      MREImpl *This = impl_from_ISynchronize(iface);
      LONG ref = InterlockedDecrement(&This->ref);
      TRACE("%p - ref %d\n", This, ref);
  
      if(!ref)
      {
          CloseHandle(This->event);
          HeapFree(GetProcessHeap(), 0, This);
      }
  
      return ref;
  }
  
  static HRESULT WINAPI ISynchronize_fnWait(ISynchronize *iface, DWORD dwFlags, DWORD dwMilliseconds)
  {
      MREImpl *This = impl_from_ISynchronize(iface);
      UINT index;
      TRACE("%p (%08x, %08x)\n", This, dwFlags, dwMilliseconds);
      return CoWaitForMultipleHandles(dwFlags, dwMilliseconds, 1, &This->event, &index);
  }
  
  static HRESULT WINAPI ISynchronize_fnSignal(ISynchronize *iface)
  {
      MREImpl *This = impl_from_ISynchronize(iface);
      TRACE("%p\n", This);
      SetEvent(This->event);
      return S_OK;
  }
  
  static HRESULT WINAPI ISynchronize_fnReset(ISynchronize *iface)
  {
      MREImpl *This = impl_from_ISynchronize(iface);
      TRACE("%p\n", This);
      ResetEvent(This->event);
      return S_OK;
  }
  
  static ISynchronizeVtbl vt_ISynchronize = {
      ISynchronize_fnQueryInterface,
      ISynchronize_fnAddRef,
      ISynchronize_fnRelease,
      ISynchronize_fnWait,
      ISynchronize_fnSignal,
      ISynchronize_fnReset
  };
  
  static inline MREImpl *impl_from_ISynchronizeHandle(ISynchronizeHandle *iface)
  {
      return CONTAINING_RECORD(iface, MREImpl, ISynchronizeHandle_iface);
  }
  
  static HRESULT WINAPI SynchronizeHandle_QueryInterface(ISynchronizeHandle *iface, REFIID riid, void **ppv)
  {
      MREImpl *This = impl_from_ISynchronizeHandle(iface);
      return ISynchronize_QueryInterface(&This->ISynchronize_iface, riid, ppv);
  }
  
  static ULONG WINAPI SynchronizeHandle_AddRef(ISynchronizeHandle *iface)
  {
      MREImpl *This = impl_from_ISynchronizeHandle(iface);
      return ISynchronize_AddRef(&This->ISynchronize_iface);
  }
  
  static ULONG WINAPI SynchronizeHandle_Release(ISynchronizeHandle *iface)
  {
      MREImpl *This = impl_from_ISynchronizeHandle(iface);
      return ISynchronize_Release(&This->ISynchronize_iface);
  }
  
  static HRESULT WINAPI SynchronizeHandle_GetHandle(ISynchronizeHandle *iface, HANDLE *ph)
  {
      MREImpl *This = impl_from_ISynchronizeHandle(iface);
  
      *ph = This->event;
      return S_OK;
  }
  
  static const ISynchronizeHandleVtbl SynchronizeHandleVtbl = {
      SynchronizeHandle_QueryInterface,
      SynchronizeHandle_AddRef,
      SynchronizeHandle_Release,
      SynchronizeHandle_GetHandle
  };
  
  static HRESULT ManualResetEvent_Construct(IUnknown *punkouter, REFIID iid, void **ppv)
  {
      MREImpl *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MREImpl));
      HRESULT hr;
  
      if(punkouter)
          FIXME("Aggregation not implemented.\n");
  
      This->ref = 1;
      This->ISynchronize_iface.lpVtbl = &vt_ISynchronize;
      This->ISynchronizeHandle_iface.lpVtbl = &SynchronizeHandleVtbl;
      This->event = CreateEventW(NULL, TRUE, FALSE, NULL);
  
      hr = ISynchronize_QueryInterface(&This->ISynchronize_iface, iid, ppv);
      ISynchronize_Release(&This->ISynchronize_iface);
      return hr;
  }
  
  static inline LocalServer *impl_from_IServiceProvider(IServiceProvider *iface)
  {
      return CONTAINING_RECORD(iface, LocalServer, IServiceProvider_iface);
  }
  
  static HRESULT WINAPI LocalServer_QueryInterface(IServiceProvider *iface, REFIID riid, void **ppv)
  {
      LocalServer *This = impl_from_IServiceProvider(iface);
  
      TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
  
      if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IServiceProvider)) {
          *ppv = &This->IServiceProvider_iface;
      }else {
          *ppv = NULL;
          return E_NOINTERFACE;
      }
  
      IUnknown_AddRef((IUnknown*)*ppv);
      return S_OK;
  }
  
  static ULONG WINAPI LocalServer_AddRef(IServiceProvider *iface)
  {
      LocalServer *This = impl_from_IServiceProvider(iface);
      LONG ref = InterlockedIncrement(&This->ref);
  
      TRACE("(%p) ref=%d\n", This, ref);
  
      return ref;
  }
  
  static ULONG WINAPI LocalServer_Release(IServiceProvider *iface)
  {
      LocalServer *This = impl_from_IServiceProvider(iface);
      LONG ref = InterlockedDecrement(&This->ref);
  
      TRACE("(%p) ref=%d\n", This, ref);
  
      if(!ref) {
          assert(!This->apt);
          HeapFree(GetProcessHeap(), 0, This);
      }
  
      return ref;
  }
  
  static HRESULT WINAPI LocalServer_QueryService(IServiceProvider *iface, REFGUID guid, REFIID riid, void **ppv)
  {
      LocalServer *This = impl_from_IServiceProvider(iface);
      APARTMENT *apt = COM_CurrentApt();
      RegisteredClass *iter;
      HRESULT hres = E_FAIL;
  
      TRACE("(%p)->(%s %s %p)\n", This, debugstr_guid(guid), debugstr_guid(riid), ppv);
  
      if(!This->apt)
          return E_UNEXPECTED;
  
      EnterCriticalSection(&csRegisteredClassList);
  
      LIST_FOR_EACH_ENTRY(iter, &RegisteredClassList, RegisteredClass, entry) {
          if(iter->apartment_id == apt->oxid
             && (iter->runContext & CLSCTX_LOCAL_SERVER)
             && IsEqualGUID(&iter->classIdentifier, guid)) {
              hres = IUnknown_QueryInterface(iter->classObject, riid, ppv);
              break;
          }
      }
  
      LeaveCriticalSection( &csRegisteredClassList );
  
      return hres;
  }
  
  static const IServiceProviderVtbl LocalServerVtbl = {
      LocalServer_QueryInterface,
      LocalServer_AddRef,
      LocalServer_Release,
      LocalServer_QueryService
  };
  
  static HRESULT get_local_server_stream(APARTMENT *apt, IStream **ret)
  {
      HRESULT hres = S_OK;
  
      EnterCriticalSection(&apt->cs);
  
      if(!apt->local_server) {
          LocalServer *obj;
  
          obj = heap_alloc(sizeof(*obj));
          if(obj) {
              obj->IServiceProvider_iface.lpVtbl = &LocalServerVtbl;
              obj->ref = 1;
              obj->apt = apt;
  
              hres = CreateStreamOnHGlobal(0, TRUE, &obj->marshal_stream);
              if(SUCCEEDED(hres)) {
                  hres = CoMarshalInterface(obj->marshal_stream, &IID_IServiceProvider, (IUnknown*)&obj->IServiceProvider_iface,
                          MSHCTX_LOCAL, NULL, MSHLFLAGS_TABLESTRONG);
                  if(FAILED(hres))
                      IStream_Release(obj->marshal_stream);
              }
  
              if(SUCCEEDED(hres))
                  apt->local_server = obj;
              else
                  heap_free(obj);
          }else {
              hres = E_OUTOFMEMORY;
          }
      }
  
      if(SUCCEEDED(hres))
          hres = IStream_Clone(apt->local_server->marshal_stream, ret);
  
      LeaveCriticalSection(&apt->cs);
  
      if(FAILED(hres))
          ERR("Failed: %08x\n", hres);
      return hres;
  }
  
  /***********************************************************************
   *           CoRevokeClassObject [OLE32.@]
   *
   * Removes a class object from the class registry.
   *
   * PARAMS
   *  dwRegister [I] Cookie returned from CoRegisterClassObject().
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: HRESULT code.
   *
   * NOTES
   *  Must be called from the same apartment that called CoRegisterClassObject(),
   *  otherwise it will fail with RPC_E_WRONG_THREAD.
   *
   * SEE ALSO
   *  CoRegisterClassObject
   */
  HRESULT WINAPI DECLSPEC_HOTPATCH CoRevokeClassObject(
          DWORD dwRegister)
  {
    HRESULT hr = E_INVALIDARG;
    RegisteredClass *curClass;
    APARTMENT *apt;
  
    TRACE("(%08x)\n",dwRegister);
  
    if (!(apt = apartment_get_current_or_mta()))
    {
      ERR("COM was not initialized\n");
      return CO_E_NOTINITIALIZED;
    }
  
    EnterCriticalSection( &csRegisteredClassList );
  
    LIST_FOR_EACH_ENTRY(curClass, &RegisteredClassList, RegisteredClass, entry)
    {
      /*
       * Check if we have a match on the cookie.
       */
      if (curClass->dwCookie == dwRegister)
      {
        if (curClass->apartment_id == apt->oxid)
        {
            COM_RevokeRegisteredClassObject(curClass);
            hr = S_OK;
        }
        else
        {
            ERR("called from wrong apartment, should be called from %s\n",
                wine_dbgstr_longlong(curClass->apartment_id));
            hr = RPC_E_WRONG_THREAD;
        }
        break;
      }
    }
  
    LeaveCriticalSection( &csRegisteredClassList );
    apartment_release(apt);
    return hr;
  }
  
  /* frees unused libraries loaded by apartment_getclassobject by calling the
   * DLL's DllCanUnloadNow entry point */
  static void apartment_freeunusedlibraries(struct apartment *apt, DWORD delay)
  {
      struct apartment_loaded_dll *entry, *next;
      EnterCriticalSection(&apt->cs);
      LIST_FOR_EACH_ENTRY_SAFE(entry, next, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
      {
  	if (entry->dll->DllCanUnloadNow && (entry->dll->DllCanUnloadNow() == S_OK))
          {
              DWORD real_delay = delay;
  
              if (real_delay == INFINITE)
              {
                  /* DLLs that return multi-threaded objects aren't unloaded
                   * straight away to cope for programs that have races between
                   * last object destruction and threads in the DLLs that haven't
                   * finished, despite DllCanUnloadNow returning S_OK */
                  if (entry->multi_threaded)
                      real_delay = 10 * 60 * 1000; /* 10 minutes */
                  else
                      real_delay = 0;
              }
  
              if (!real_delay || (entry->unload_time && ((int)(GetTickCount() - entry->unload_time) > 0)))
              {
                  list_remove(&entry->entry);
                  COMPOBJ_DllList_ReleaseRef(entry->dll, TRUE);
                  HeapFree(GetProcessHeap(), 0, entry);
              }
              else
              {
                  entry->unload_time = GetTickCount() + real_delay;
                  if (!entry->unload_time) entry->unload_time = 1;
              }
          }
          else if (entry->unload_time)
              entry->unload_time = 0;
      }
      LeaveCriticalSection(&apt->cs);
  }
  
  DWORD apartment_release(struct apartment *apt)
  {
      DWORD ret;
  
      EnterCriticalSection(&csApartment);
  
      ret = InterlockedDecrement(&apt->refs);
      TRACE("%s: after = %d\n", wine_dbgstr_longlong(apt->oxid), ret);
  
      if (apt->being_destroyed)
      {
          LeaveCriticalSection(&csApartment);
          return ret;
      }
  
      /* destruction stuff that needs to happen under csApartment CS */
      if (ret == 0)
      {
          apt->being_destroyed = TRUE;
          if (apt == MTA) MTA = NULL;
          else if (apt == MainApartment) MainApartment = NULL;
          list_remove(&apt->entry);
      }
  
      LeaveCriticalSection(&csApartment);
  
      if (ret == 0)
      {
          struct list *cursor, *cursor2;
  
          TRACE("destroying apartment %p, oxid %s\n", apt, wine_dbgstr_longlong(apt->oxid));
  
          if(apt->local_server) {
              LocalServer *local_server = apt->local_server;
              LARGE_INTEGER zero;
  
              memset(&zero, 0, sizeof(zero));
              IStream_Seek(local_server->marshal_stream, zero, STREAM_SEEK_SET, NULL);
              CoReleaseMarshalData(local_server->marshal_stream);
              IStream_Release(local_server->marshal_stream);
              local_server->marshal_stream = NULL;
  
              apt->local_server = NULL;
              local_server->apt = NULL;
              IServiceProvider_Release(&local_server->IServiceProvider_iface);
          }
  
          /* Release the references to the registered class objects */
          COM_RevokeAllClasses(apt);
  
          /* no locking is needed for this apartment, because no other thread
           * can access it at this point */
  
          apartment_disconnectproxies(apt);
  
          if (apt->win) DestroyWindow(apt->win);
          if (apt->host_apt_tid) PostThreadMessageW(apt->host_apt_tid, WM_QUIT, 0, 0);
  
          LIST_FOR_EACH_SAFE(cursor, cursor2, &apt->stubmgrs)
          {
              struct stub_manager *stubmgr = LIST_ENTRY(cursor, struct stub_manager, entry);
              /* release the implicit reference given by the fact that the
               * stub has external references (it must do since it is in the
               * stub manager list in the apartment and all non-apartment users
               * must have a ref on the apartment and so it cannot be destroyed).
               */
              stub_manager_int_release(stubmgr);
          }
  
          /* if this assert fires, then another thread took a reference to a
           * stub manager without taking a reference to the containing
           * apartment, which it must do. */
          assert(list_empty(&apt->stubmgrs));
  
          if (apt->filter) IMessageFilter_Release(apt->filter);
  
          /* free as many unused libraries as possible... */
          apartment_freeunusedlibraries(apt, 0);
  
          /* ... and free the memory for the apartment loaded dll entry and
           * release the dll list reference without freeing the library for the
           * rest */
          while ((cursor = list_head(&apt->loaded_dlls)))
          {
              struct apartment_loaded_dll *apartment_loaded_dll = LIST_ENTRY(cursor, struct apartment_loaded_dll, entry);
              COMPOBJ_DllList_ReleaseRef(apartment_loaded_dll->dll, FALSE);
              list_remove(cursor);
              HeapFree(GetProcessHeap(), 0, apartment_loaded_dll);
          }
  
          DEBUG_CLEAR_CRITSEC_NAME(&apt->cs);
          DeleteCriticalSection(&apt->cs);
  
          HeapFree(GetProcessHeap(), 0, apt);
      }
  
      return ret;
  }
  
  /* The given OXID must be local to this process: 
   *
   * The ref parameter is here mostly to ensure people remember that
   * they get one, you should normally take a ref for thread safety.
   */
  APARTMENT *apartment_findfromoxid(OXID oxid, BOOL ref)
  {
      APARTMENT *result = NULL;
      struct list *cursor;
  
      EnterCriticalSection(&csApartment);
      LIST_FOR_EACH( cursor, &apts )
      {
          struct apartment *apt = LIST_ENTRY( cursor, struct apartment, entry );
          if (apt->oxid == oxid)
          {
              result = apt;
              if (ref) apartment_addref(result);
              break;
          }
      }
      LeaveCriticalSection(&csApartment);
  
      return result;
  }
  
  /* gets the apartment which has a given creator thread ID. The caller must
   * release the reference from the apartment as soon as the apartment pointer
   * is no longer required. */
  APARTMENT *apartment_findfromtid(DWORD tid)
  {
      APARTMENT *result = NULL;
      struct list *cursor;
  
      EnterCriticalSection(&csApartment);
      LIST_FOR_EACH( cursor, &apts )
      {
          struct apartment *apt = LIST_ENTRY( cursor, struct apartment, entry );
          if (apt->tid == tid)
          {
              result = apt;
              apartment_addref(result);
              break;
          }
      }
      LeaveCriticalSection(&csApartment);
  
      return result;
  }
  
  /* gets the main apartment if it exists. The caller must
   * release the reference from the apartment as soon as the apartment pointer
   * is no longer required. */
  static APARTMENT *apartment_findmain(void)
  {
      APARTMENT *result;
  
      EnterCriticalSection(&csApartment);
  
      result = MainApartment;
      if (result) apartment_addref(result);
  
      LeaveCriticalSection(&csApartment);
  
      return result;
  }
  
  /* gets the specified class object by loading the appropriate DLL, if
   * necessary and calls the DllGetClassObject function for the DLL */
  static HRESULT apartment_getclassobject(struct apartment *apt, LPCWSTR dllpath,
                                          BOOL apartment_threaded,
                                          REFCLSID rclsid, REFIID riid, void **ppv)
  {
      static const WCHAR wszOle32[] = {'o','l','e','3','2','.','d','l','l',0};
      HRESULT hr = S_OK;
      BOOL found = FALSE;
      struct apartment_loaded_dll *apartment_loaded_dll;
  
      if (!strcmpiW(dllpath, wszOle32))
      {
          /* we don't need to control the lifetime of this dll, so use the local
           * implementation of DllGetClassObject directly */
          TRACE("calling ole32!DllGetClassObject\n");
          hr = DllGetClassObject(rclsid, riid, ppv);
  
          if (hr != S_OK)
              ERR("DllGetClassObject returned error 0x%08x for dll %s\n", hr, debugstr_w(dllpath));
  
          return hr;
      }
  
      EnterCriticalSection(&apt->cs);
  
      LIST_FOR_EACH_ENTRY(apartment_loaded_dll, &apt->loaded_dlls, struct apartment_loaded_dll, entry)
          if (!strcmpiW(dllpath, apartment_loaded_dll->dll->library_name))
          {
              TRACE("found %s already loaded\n", debugstr_w(dllpath));
              found = TRUE;
              break;
          }
  
      if (!found)
      {
          apartment_loaded_dll = HeapAlloc(GetProcessHeap(), 0, sizeof(*apartment_loaded_dll));
          if (!apartment_loaded_dll)
              hr = E_OUTOFMEMORY;
          if (SUCCEEDED(hr))
          {
              apartment_loaded_dll->unload_time = 0;
              apartment_loaded_dll->multi_threaded = FALSE;
              hr = COMPOBJ_DllList_Add( dllpath, &apartment_loaded_dll->dll );
              if (FAILED(hr))
                  HeapFree(GetProcessHeap(), 0, apartment_loaded_dll);
          }
          if (SUCCEEDED(hr))
          {
              TRACE("added new loaded dll %s\n", debugstr_w(dllpath));
              list_add_tail(&apt->loaded_dlls, &apartment_loaded_dll->entry);
          }
      }
  
      LeaveCriticalSection(&apt->cs);
  
      if (SUCCEEDED(hr))
      {
          /* one component being multi-threaded overrides any number of
           * apartment-threaded components */
          if (!apartment_threaded)
              apartment_loaded_dll->multi_threaded = TRUE;
  
          TRACE("calling DllGetClassObject %p\n", apartment_loaded_dll->dll->DllGetClassObject);
          /* OK: get the ClassObject */
          hr = apartment_loaded_dll->dll->DllGetClassObject(rclsid, riid, ppv);
  
          if (hr != S_OK)
              ERR("DllGetClassObject returned error 0x%08x for dll %s\n", hr, debugstr_w(dllpath));
      }
  
      return hr;
  }
  
  /* Returns expanded dll path from the registry or activation context. */
  static BOOL get_object_dll_path(const struct class_reg_data *regdata, WCHAR *dst, DWORD dstlen)
  {
      DWORD ret;
  
      if (regdata->hkey)
      {
  	DWORD keytype;
  	WCHAR src[MAX_PATH];
  	DWORD dwLength = dstlen * sizeof(WCHAR);
  
          if( (ret = RegQueryValueExW(regdata->u.hkey, NULL, NULL, &keytype, (BYTE*)src, &dwLength)) == ERROR_SUCCESS ) {
              if (keytype == REG_EXPAND_SZ) {
                if (dstlen <= ExpandEnvironmentStringsW(src, dst, dstlen)) ret = ERROR_MORE_DATA;
              } else {
                const WCHAR *quote_start;
                quote_start = strchrW(src, '\"');
                if (quote_start) {
                  const WCHAR *quote_end = strchrW(quote_start + 1, '\"');
                  if (quote_end) {
                    memmove(src, quote_start + 1,
                            (quote_end - quote_start - 1) * sizeof(WCHAR));
                    src[quote_end - quote_start - 1] = '\0';
                  }
                }
                lstrcpynW(dst, src, dstlen);
              }
          }
          return !ret;
      }
      else
      {
          static const WCHAR dllW[] = {'.','d','l','l',0};
          ULONG_PTR cookie;
          WCHAR *nameW;
  
          *dst = 0;
          nameW = (WCHAR*)((BYTE*)regdata->u.actctx.section + regdata->u.actctx.data->name_offset);
          ActivateActCtx(regdata->u.actctx.hactctx, &cookie);
          ret = SearchPathW(NULL, nameW, dllW, dstlen, dst, NULL);
          DeactivateActCtx(0, cookie);
          return *dst != 0;
      }
  }
  
  struct host_object_params
  {
      struct class_reg_data regdata;
      CLSID clsid; /* clsid of object to marshal */
      IID iid; /* interface to marshal */
      HANDLE event; /* event signalling when ready for multi-threaded case */
      HRESULT hr; /* result for multi-threaded case */
      IStream *stream; /* stream that the object will be marshaled into */
      BOOL apartment_threaded; /* is the component purely apartment-threaded? */
  };
  
  static HRESULT apartment_hostobject(struct apartment *apt,
                                      const struct host_object_params *params)
  {
      IUnknown *object;
      HRESULT hr;
      static const LARGE_INTEGER llZero;
      WCHAR dllpath[MAX_PATH+1];
  
      TRACE("clsid %s, iid %s\n", debugstr_guid(&params->clsid), debugstr_guid(&params->iid));
  
      if (!get_object_dll_path(&params->regdata, dllpath, ARRAY_SIZE(dllpath)))
      {
          /* failure: CLSID is not found in registry */
          WARN("class %s not registered inproc\n", debugstr_guid(&params->clsid));
          return REGDB_E_CLASSNOTREG;
      }
  
      hr = apartment_getclassobject(apt, dllpath, params->apartment_threaded,
                                    &params->clsid, &params->iid, (void **)&object);
      if (FAILED(hr))
          return hr;
  
      hr = CoMarshalInterface(params->stream, &params->iid, object, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
      if (FAILED(hr))
          IUnknown_Release(object);
      IStream_Seek(params->stream, llZero, STREAM_SEEK_SET, NULL);
  
      return hr;
  }
  
  static LRESULT CALLBACK apartment_wndproc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  {
      switch (msg)
      {
      case DM_EXECUTERPC:
          RPC_ExecuteCall((struct dispatch_params *)lParam);
          return 0;
      case DM_HOSTOBJECT:
          return apartment_hostobject(COM_CurrentApt(), (const struct host_object_params *)lParam);
      default:
          return DefWindowProcW(hWnd, msg, wParam, lParam);
      }
  }
  
  struct host_thread_params
  {
      COINIT threading_model;
      HANDLE ready_event;
      HWND apartment_hwnd;
  };
  
  /* thread for hosting an object to allow an object to appear to be created in
   * an apartment with an incompatible threading model */
  static DWORD CALLBACK apartment_hostobject_thread(LPVOID p)
  {
      struct host_thread_params *params = p;
      MSG msg;
      HRESULT hr;
      struct apartment *apt;
  
      TRACE("\n");
  
      hr = CoInitializeEx(NULL, params->threading_model);
      if (FAILED(hr)) return hr;
  
      apt = COM_CurrentApt();
      if (params->threading_model == COINIT_APARTMENTTHREADED)
      {
          apartment_createwindowifneeded(apt);
          params->apartment_hwnd = apartment_getwindow(apt);
      }
      else
          params->apartment_hwnd = NULL;
  
      /* force the message queue to be created before signaling parent thread */
      PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
  
      SetEvent(params->ready_event);
      params = NULL; /* can't touch params after here as it may be invalid */
  
      while (GetMessageW(&msg, NULL, 0, 0))
      {
          if (!msg.hwnd && (msg.message == DM_HOSTOBJECT))
          {
              struct host_object_params *obj_params = (struct host_object_params *)msg.lParam;
              obj_params->hr = apartment_hostobject(apt, obj_params);
              SetEvent(obj_params->event);
          }
          else
          {
              TranslateMessage(&msg);
              DispatchMessageW(&msg);
          }
      }
  
      TRACE("exiting\n");
  
      CoUninitialize();
  
      return S_OK;
  }
  
  /* finds or creates a host apartment, creates the object inside it and returns
   * a proxy to it so that the object can be used in the apartment of the
   * caller of this function */
  static HRESULT apartment_hostobject_in_hostapt(
      struct apartment *apt, BOOL multi_threaded, BOOL main_apartment,
      const struct class_reg_data *regdata, REFCLSID rclsid, REFIID riid, void **ppv)
  {
      struct host_object_params params;
      HWND apartment_hwnd = NULL;
      DWORD apartment_tid = 0;
      HRESULT hr;
  
      if (!multi_threaded && main_apartment)
      {
          APARTMENT *host_apt = apartment_findmain();
          if (host_apt)
          {
              apartment_hwnd = apartment_getwindow(host_apt);
              apartment_release(host_apt);
          }
      }
  
      if (!apartment_hwnd)
      {
          EnterCriticalSection(&apt->cs);
  
          if (!apt->host_apt_tid)
          {
              struct host_thread_params thread_params;
              HANDLE handles[2];
              DWORD wait_value;
  
              thread_params.threading_model = multi_threaded ? COINIT_MULTITHREADED : COINIT_APARTMENTTHREADED;
              handles[0] = thread_params.ready_event = CreateEventW(NULL, FALSE, FALSE, NULL);
              thread_params.apartment_hwnd = NULL;
              handles[1] = CreateThread(NULL, 0, apartment_hostobject_thread, &thread_params, 0, &apt->host_apt_tid);
              if (!handles[1])
              {
                  CloseHandle(handles[0]);
                  LeaveCriticalSection(&apt->cs);
                  return E_OUTOFMEMORY;
              }
              wait_value = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
              CloseHandle(handles[0]);
              CloseHandle(handles[1]);
              if (wait_value == WAIT_OBJECT_0)
                  apt->host_apt_hwnd = thread_params.apartment_hwnd;
              else
              {
                  LeaveCriticalSection(&apt->cs);
                  return E_OUTOFMEMORY;
              }
          }
  
          if (multi_threaded || !main_apartment)
          {
              apartment_hwnd = apt->host_apt_hwnd;
              apartment_tid = apt->host_apt_tid;
          }
  
          LeaveCriticalSection(&apt->cs);
      }
  
      /* another thread may have become the main apartment in the time it took
       * us to create the thread for the host apartment */
      if (!apartment_hwnd && !multi_threaded && main_apartment)
      {
          APARTMENT *host_apt = apartment_findmain();
          if (host_apt)
          {
              apartment_hwnd = apartment_getwindow(host_apt);
              apartment_release(host_apt);
          }
      }
  
      params.regdata = *regdata;
      params.clsid = *rclsid;
      params.iid = *riid;
      hr = CreateStreamOnHGlobal(NULL, TRUE, &params.stream);
      if (FAILED(hr))
          return hr;
      params.apartment_threaded = !multi_threaded;
      if (multi_threaded)
      {
          params.hr = S_OK;
          params.event = CreateEventW(NULL, FALSE, FALSE, NULL);
          if (!PostThreadMessageW(apartment_tid, DM_HOSTOBJECT, 0, (LPARAM)&params))
              hr = E_OUTOFMEMORY;
          else
          {
              WaitForSingleObject(params.event, INFINITE);
              hr = params.hr;
          }
          CloseHandle(params.event);
      }
      else
      {
          if (!apartment_hwnd)
          {
              ERR("host apartment didn't create window\n");
              hr = E_OUTOFMEMORY;
          }
          else
              hr = SendMessageW(apartment_hwnd, DM_HOSTOBJECT, 0, (LPARAM)&params);
      }
      if (SUCCEEDED(hr))
          hr = CoUnmarshalInterface(params.stream, riid, ppv);
      IStream_Release(params.stream);
      return hr;
  }
  
  static BOOL WINAPI register_class( INIT_ONCE *once, void *param, void **context )
  {
      WNDCLASSW wclass;
  
      /* Dispatching to the correct thread in an apartment is done through
       * window messages rather than RPC transports. When an interface is
       * marshalled into another apartment in the same process, a window of the
       * following class is created. The *caller* of CoMarshalInterface (i.e., the
       * application) is responsible for pumping the message loop in that thread.
       * The WM_USER messages which point to the RPCs are then dispatched to
       * apartment_wndproc by the user's code from the apartment in which the
       * interface was unmarshalled.
       */
      memset(&wclass, 0, sizeof(wclass));
      wclass.lpfnWndProc = apartment_wndproc;
      wclass.hInstance = hProxyDll;
      wclass.lpszClassName = wszAptWinClass;
      apt_win_class = RegisterClassW(&wclass);
      return TRUE;
  }
  
  /* create a window for the apartment or return the current one if one has
   * already been created */
  HRESULT apartment_createwindowifneeded(struct apartment *apt)
  {
      static INIT_ONCE class_init_once = INIT_ONCE_STATIC_INIT;
  
      if (apt->multi_threaded)
          return S_OK;
  
      if (!apt->win)
      {
          HWND hwnd;
  
          InitOnceExecuteOnce( &class_init_once, register_class, NULL, NULL );
  
          hwnd = CreateWindowW(wszAptWinClass, NULL, 0, 0, 0, 0, 0,
                               HWND_MESSAGE, 0, hProxyDll, NULL);
          if (!hwnd)
          {
              ERR("CreateWindow failed with error %d\n", GetLastError());
              return HRESULT_FROM_WIN32(GetLastError());
          }
          if (InterlockedCompareExchangePointer((PVOID *)&apt->win, hwnd, NULL))
              /* someone beat us to it */
              DestroyWindow(hwnd);
      }
  
      return S_OK;
  }
  
  /* retrieves the window for the main- or apartment-threaded apartment */
  HWND apartment_getwindow(const struct apartment *apt)
  {
      assert(!apt->multi_threaded);
      return apt->win;
  }
  
  static void COM_TlsDestroy(void)
  {
      struct oletls *info = NtCurrentTeb()->ReservedForOle;
      if (info)
      {
          if (info->apt) apartment_release(info->apt);
          if (info->errorinfo) IErrorInfo_Release(info->errorinfo);
          if (info->state) IUnknown_Release(info->state);
          if (info->spy) IInitializeSpy_Release(info->spy);
          if (info->context_token) IObjContext_Release(info->context_token);
          HeapFree(GetProcessHeap(), 0, info);
          NtCurrentTeb()->ReservedForOle = NULL;
      }
  }
  
  /******************************************************************************
   *           CoBuildVersion [OLE32.@]
   *
   * Gets the build version of the DLL.
   *
   * PARAMS
   *
   * RETURNS
   *	Current build version, hiword is majornumber, loword is minornumber
   */
  DWORD WINAPI CoBuildVersion(void)
  {
      TRACE("Returning version %d, build %d.\n", rmm, rup);
      return (rmm<<16)+rup;
  }
  
  /******************************************************************************
   *              CoRegisterInitializeSpy [OLE32.@]
   *
   * Add a Spy that watches CoInitializeEx calls
   *
   * PARAMS
   *  spy [I] Pointer to IUnknown interface that will be QueryInterface'd.
   *  cookie [II] cookie receiver
   *
   * RETURNS
   *  Success: S_OK if not already initialized, S_FALSE otherwise.
   *  Failure: HRESULT code.
   *
   * SEE ALSO
   *   CoInitializeEx
   */
  HRESULT WINAPI CoRegisterInitializeSpy(IInitializeSpy *spy, ULARGE_INTEGER *cookie)
  {
      struct oletls *info = COM_CurrentInfo();
      HRESULT hr;
  
      TRACE("(%p, %p)\n", spy, cookie);
  
      if (!spy || !cookie || !info)
      {
          if (!info)
              WARN("Could not allocate tls\n");
          return E_INVALIDARG;
      }
  
      if (info->spy)
      {
          FIXME("Already registered?\n");
          return E_UNEXPECTED;
      }
  
      hr = IInitializeSpy_QueryInterface(spy, &IID_IInitializeSpy, (void **) &info->spy);
      if (SUCCEEDED(hr))
      {
          cookie->QuadPart = (DWORD_PTR)spy;
          return S_OK;
      }
      return hr;
  }
  
  /******************************************************************************
   *              CoRevokeInitializeSpy [OLE32.@]
   *
   * Remove a spy that previously watched CoInitializeEx calls
   *
   * PARAMS
   *  cookie [I] The cookie obtained from a previous CoRegisterInitializeSpy call
   *
   * RETURNS
   *  Success: S_OK if a spy is removed
   *  Failure: E_INVALIDARG
   *
   * SEE ALSO
   *   CoInitializeEx
   */
  HRESULT WINAPI CoRevokeInitializeSpy(ULARGE_INTEGER cookie)
  {
      struct oletls *info = COM_CurrentInfo();
      TRACE("(%s)\n", wine_dbgstr_longlong(cookie.QuadPart));
  
      if (!info || !info->spy || cookie.QuadPart != (DWORD_PTR)info->spy)
          return E_INVALIDARG;
  
      IInitializeSpy_Release(info->spy);
      info->spy = NULL;
      return S_OK;
  }
  
  HRESULT enter_apartment( struct oletls *info, DWORD model )
  {
      HRESULT hr = S_OK;
  
      if (!info->apt)
      {
          if (!apartment_get_or_create( model ))
              return E_OUTOFMEMORY;
      }
      else if (!apartment_is_model( info->apt, model ))
      {
          WARN( "Attempt to change threading model of this apartment from %s to %s\n",
                info->apt->multi_threaded ? "multi-threaded" : "apartment threaded",
                model & COINIT_APARTMENTTHREADED ? "apartment threaded" : "multi-threaded" );
          return RPC_E_CHANGED_MODE;
      }
      else
          hr = S_FALSE;
  
      info->inits++;
  
      return hr;
  }
  
  void leave_apartment( struct oletls *info )
  {
      if (!--info->inits)
      {
          if (info->ole_inits)
              WARN( "Uninitializing apartment while Ole is still initialized\n" );
          apartment_release( info->apt );
          info->apt = NULL;
      }
  }
  
  /******************************************************************************
   *		CoInitialize	[OLE32.@]
   *
   * Initializes the COM libraries by calling CoInitializeEx with
   * COINIT_APARTMENTTHREADED, ie it enters a STA thread.
   *
   * PARAMS
   *  lpReserved [I] Pointer to IMalloc interface (obsolete, should be NULL).
   *
   * RETURNS
   *  Success: S_OK if not already initialized, S_FALSE otherwise.
   *  Failure: HRESULT code.
   *
   * SEE ALSO
   *   CoInitializeEx
   */
  HRESULT WINAPI CoInitialize(LPVOID lpReserved)
  {
    /*
     * Just delegate to the newer method.
     */
    return CoInitializeEx(lpReserved, COINIT_APARTMENTTHREADED);
  }
  
  /******************************************************************************
   *		CoInitializeEx	[OLE32.@]
   *
   * Initializes the COM libraries.
   *
   * PARAMS
   *  lpReserved [I] Pointer to IMalloc interface (obsolete, should be NULL).
   *  dwCoInit   [I] One or more flags from the COINIT enumeration. See notes.
   *
   * RETURNS
   *  S_OK               if successful,
   *  S_FALSE            if this function was called already.
   *  RPC_E_CHANGED_MODE if a previous call to CoInitializeEx specified another
   *                     threading model.
   *
   * NOTES
   *
   * The behavior used to set the IMalloc used for memory management is
   * obsolete.
   * The dwCoInit parameter must specify one of the following apartment
   * threading models:
   *| COINIT_APARTMENTTHREADED - A single-threaded apartment (STA).
   *| COINIT_MULTITHREADED - A multi-threaded apartment (MTA).
   * The parameter may also specify zero or more of the following flags:
   *| COINIT_DISABLE_OLE1DDE - Don't use DDE for OLE1 support.
   *| COINIT_SPEED_OVER_MEMORY - Trade memory for speed.
   *
   * SEE ALSO
   *   CoUninitialize
   */
  HRESULT WINAPI DECLSPEC_HOTPATCH CoInitializeEx(LPVOID lpReserved, DWORD dwCoInit)
  {
    struct oletls *info = COM_CurrentInfo();
    HRESULT hr;
  
    TRACE("(%p, %x)\n", lpReserved, (int)dwCoInit);
  
    if (lpReserved!=NULL)
    {
      ERR("(%p, %x) - Bad parameter passed-in %p, must be an old Windows Application\n", lpReserved, (int)dwCoInit, lpReserved);
    }
  
    /*
     * Check the lock count. If this is the first time going through the initialize
     * process, we have to initialize the libraries.
     *
     * And crank-up that lock count.
     */
    if (InterlockedExchangeAdd(&s_COMLockCount,1)==0)
    {
      /*
       * Initialize the various COM libraries and data structures.
       */
      TRACE("() - Initializing the COM libraries\n");
  
      /* we may need to defer this until after apartment initialisation */
      RunningObjectTableImpl_Initialize();
    }
  
    if (info->spy)
        IInitializeSpy_PreInitialize(info->spy, dwCoInit, info->inits);
  
    hr = enter_apartment( info, dwCoInit );
  
    if (info->spy)
        IInitializeSpy_PostInitialize(info->spy, hr, dwCoInit, info->inits);
  
    return hr;
  }
  
  /***********************************************************************
   *           CoUninitialize   [OLE32.@]
   *
   * This method will decrement the refcount on the current apartment, freeing
   * the resources associated with it if it is the last thread in the apartment.
   * If the last apartment is freed, the function will additionally release
   * any COM resources associated with the process.
   *
   * PARAMS
   *
   * RETURNS
   *  Nothing.
   *
   * SEE ALSO
   *   CoInitializeEx
   */
  void WINAPI DECLSPEC_HOTPATCH CoUninitialize(void)
  {
    struct oletls * info = COM_CurrentInfo();
    LONG lCOMRefCnt;
  
    TRACE("()\n");
  
    /* will only happen on OOM */
    if (!info) return;
  
    if (info->spy)
        IInitializeSpy_PreUninitialize(info->spy, info->inits);
  
    /* sanity check */
    if (!info->inits)
    {
      ERR("Mismatched CoUninitialize\n");
  
      if (info->spy)
          IInitializeSpy_PostUninitialize(info->spy, info->inits);
      return;
    }
  
    leave_apartment( info );
  
    /*
     * Decrease the reference count.
     * If we are back to 0 locks on the COM library, make sure we free
     * all the associated data structures.
     */
    lCOMRefCnt = InterlockedExchangeAdd(&s_COMLockCount,-1);
    if (lCOMRefCnt==1)
    {
      TRACE("() - Releasing the COM libraries\n");
  
      revoke_registered_psclsids();
      RunningObjectTableImpl_UnInitialize();
    }
    else if (lCOMRefCnt<1) {
      ERR( "CoUninitialize() - not CoInitialized.\n" );
      InterlockedExchangeAdd(&s_COMLockCount,1); /* restore the lock count. */
    }
    if (info->spy)
        IInitializeSpy_PostUninitialize(info->spy, info->inits);
  }
  
  /******************************************************************************
   *		CoDisconnectObject	[OLE32.@]
   *
   * Disconnects all connections to this object from remote processes. Dispatches
   * pending RPCs while blocking new RPCs from occurring, and then calls
   * IMarshal::DisconnectObject on the given object.
   *
   * Typically called when the object server is forced to shut down, for instance by
   * the user.
   *
   * PARAMS
   *  lpUnk    [I] The object whose stub should be disconnected.
   *  reserved [I] Reserved. Should be set to 0.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: HRESULT code.
   *
   * SEE ALSO
   *  CoMarshalInterface, CoReleaseMarshalData, CoLockObjectExternal
   */
  HRESULT WINAPI CoDisconnectObject( LPUNKNOWN lpUnk, DWORD reserved )
  {
      struct stub_manager *manager;
      HRESULT hr;
      IMarshal *marshal;
      APARTMENT *apt;
  
      TRACE("(%p, 0x%08x)\n", lpUnk, reserved);
  
      if (!lpUnk) return E_INVALIDARG;
  
      hr = IUnknown_QueryInterface(lpUnk, &IID_IMarshal, (void **)&marshal);
      if (hr == S_OK)
      {
          hr = IMarshal_DisconnectObject(marshal, reserved);
          IMarshal_Release(marshal);
          return hr;
      }
  
      if (!(apt = apartment_get_current_or_mta()))
      {
          ERR("apartment not initialised\n");
          return CO_E_NOTINITIALIZED;
      }
  
      manager = get_stub_manager_from_object(apt, lpUnk, FALSE);
      if (manager) {
          stub_manager_disconnect(manager);
          /* Release stub manager twice, to remove the apartment reference. */
          stub_manager_int_release(manager);
          stub_manager_int_release(manager);
      }
  
      /* Note: native is pretty broken here because it just silently
       * fails, without returning an appropriate error code if the object was
       * not found, making apps think that the object was disconnected, when
       * it actually wasn't */
  
      apartment_release(apt);
      return S_OK;
  }
  
  /******************************************************************************
   *		CoCreateGuid [OLE32.@]
   *
   * Simply forwards to UuidCreate in RPCRT4.
   *
   * PARAMS
   *  pguid [O] Points to the GUID to initialize.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: HRESULT code.
   *
   * SEE ALSO
   *   UuidCreate
   */
  HRESULT WINAPI CoCreateGuid(GUID *pguid)
  {
      DWORD status;
  
      if(!pguid) return E_INVALIDARG;
  
      status = UuidCreate(pguid);
      if (status == RPC_S_OK || status == RPC_S_UUID_LOCAL_ONLY) return S_OK;
      return HRESULT_FROM_WIN32( status );
  }
  
  static inline BOOL is_valid_hex(WCHAR c)
  {
      if (!(((c >= '0') && (c <= '9'))  ||
            ((c >= 'a') && (c <= 'f'))  ||
            ((c >= 'A') && (c <= 'F'))))
          return FALSE;
      return TRUE;
  }
  
  static const BYTE guid_conv_table[256] =
  {
    0,   0,   0,   0,   0,   0,   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 */
    0,   0,   0,   0,   0,   0,   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 */
    0,   0,   0,   0,   0,   0,   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 */
    0,   1,   2,   3,   4,   5,   6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 0x30 */
    0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 */
    0,   0,   0,   0,   0,   0,   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 */
    0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf                             /* 0x60 */
  };
  
  /* conversion helper for CLSIDFromString/IIDFromString */
  static BOOL guid_from_string(LPCWSTR s, GUID *id)
  {
    int	i;
  
    if (!s || s[0]!='{') {
      memset( id, 0, sizeof (CLSID) );
      if(!s) return TRUE;
      return FALSE;
    }
  
    TRACE("%s -> %p\n", debugstr_w(s), id);
  
    /* in form {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} */
  
    id->Data1 = 0;
    for (i = 1; i < 9; i++) {
      if (!is_valid_hex(s[i])) return FALSE;
      id->Data1 = (id->Data1 << 4) | guid_conv_table[s[i]];
    }
    if (s[9]!='-') return FALSE;
  
    id->Data2 = 0;
    for (i = 10; i < 14; i++) {
      if (!is_valid_hex(s[i])) return FALSE;
      id->Data2 = (id->Data2 << 4) | guid_conv_table[s[i]];
    }
    if (s[14]!='-') return FALSE;
  
    id->Data3 = 0;
    for (i = 15; i < 19; i++) {
      if (!is_valid_hex(s[i])) return FALSE;
      id->Data3 = (id->Data3 << 4) | guid_conv_table[s[i]];
    }
    if (s[19]!='-') return FALSE;
  
    for (i = 20; i < 37; i+=2) {
      if (i == 24) {
        if (s[i]!='-') return FALSE;
        i++;
      }
      if (!is_valid_hex(s[i]) || !is_valid_hex(s[i+1])) return FALSE;
      id->Data4[(i-20)/2] = guid_conv_table[s[i]] << 4 | guid_conv_table[s[i+1]];
    }
  
    if (s[37] == '}' && s[38] == '\0')
      return TRUE;
  
    return FALSE;
  }
  
  /*****************************************************************************/
  
  static HRESULT clsid_from_string_reg(LPCOLESTR progid, CLSID *clsid)
  {
      static const WCHAR clsidW[] = { '\\','C','L','S','I','D',0 };
      WCHAR buf2[CHARS_IN_GUID];
      LONG buf2len = sizeof(buf2);
      HKEY xhkey;
      WCHAR *buf;
  
      memset(clsid, 0, sizeof(*clsid));
      buf = HeapAlloc( GetProcessHeap(),0,(strlenW(progid)+8) * sizeof(WCHAR) );
      if (!buf) return E_OUTOFMEMORY;
      strcpyW( buf, progid );
      strcatW( buf, clsidW );
      if (open_classes_key(HKEY_CLASSES_ROOT, buf, MAXIMUM_ALLOWED, &xhkey))
      {
          HeapFree(GetProcessHeap(),0,buf);
          WARN("couldn't open key for ProgID %s\n", debugstr_w(progid));
          return CO_E_CLASSSTRING;
      }
      HeapFree(GetProcessHeap(),0,buf);
  
      if (RegQueryValueW(xhkey,NULL,buf2,&buf2len))
      {
          RegCloseKey(xhkey);
          WARN("couldn't query clsid value for ProgID %s\n", debugstr_w(progid));
          return CO_E_CLASSSTRING;
      }
      RegCloseKey(xhkey);
      return guid_from_string(buf2, clsid) ? S_OK : CO_E_CLASSSTRING;
  }
  
  /******************************************************************************
   *		CLSIDFromString	[OLE32.@]
   *
   * Converts a unique identifier from its string representation into
   * the GUID struct.
   *
   * PARAMS
   *  idstr [I] The string representation of the GUID.
   *  id    [O] GUID converted from the string.
   *
   * RETURNS
   *   S_OK on success
   *   CO_E_CLASSSTRING if idstr is not a valid CLSID
   *
   * SEE ALSO
   *  StringFromCLSID
   */
  HRESULT WINAPI CLSIDFromString(LPCOLESTR idstr, LPCLSID id )
  {
      HRESULT ret = CO_E_CLASSSTRING;
      CLSID tmp_id;
  
      if (!id)
          return E_INVALIDARG;
  
      if (guid_from_string(idstr, id))
          return S_OK;
  
      /* It appears a ProgID is also valid */
      ret = clsid_from_string_reg(idstr, &tmp_id);
      if(SUCCEEDED(ret))
          *id = tmp_id;
  
      return ret;
  }
  
  /******************************************************************************
   *		IIDFromString   [OLE32.@]
   *
   * Converts an interface identifier from its string representation to
   * the IID struct.
   *
   * PARAMS
   *  idstr [I] The string representation of the GUID.
   *  id    [O] IID converted from the string.
   *
   * RETURNS
   *   S_OK on success
   *   CO_E_IIDSTRING if idstr is not a valid IID
   *
   * SEE ALSO
   *  StringFromIID
   */
  HRESULT WINAPI IIDFromString(LPCOLESTR s, IID *iid)
  {
    TRACE("%s -> %p\n", debugstr_w(s), iid);
  
    if (!s)
    {
        memset(iid, 0, sizeof(*iid));
        return S_OK;
    }
  
    /* length mismatch is a special case */
    if (strlenW(s) + 1 != CHARS_IN_GUID)
        return E_INVALIDARG;
  
    if (s[0] != '{')
        return CO_E_IIDSTRING;
  
    return guid_from_string(s, iid) ? S_OK : CO_E_IIDSTRING;
  }
  
  /******************************************************************************
   *		StringFromCLSID	[OLE32.@]
   *		StringFromIID   [OLE32.@]
   *
   * Converts a GUID into the respective string representation.
   * The target string is allocated using the OLE IMalloc.
   *
   * PARAMS
   *  id    [I] the GUID to be converted.
   *  idstr [O] A pointer to a to-be-allocated pointer pointing to the resulting string.
   *
   * RETURNS
   *   S_OK
   *   E_FAIL
   *
   * SEE ALSO
   *  StringFromGUID2, CLSIDFromString
   */
  HRESULT WINAPI StringFromCLSID(REFCLSID id, LPOLESTR *idstr)
  {
      if (!(*idstr = CoTaskMemAlloc(CHARS_IN_GUID * sizeof(WCHAR)))) return E_OUTOFMEMORY;
      StringFromGUID2( id, *idstr, CHARS_IN_GUID );
      return S_OK;
  }
  
  /******************************************************************************
   *		StringFromGUID2	[OLE32.@]
   *
   * Modified version of StringFromCLSID that allows you to specify max
   * buffer size.
   *
   * PARAMS
   *  id   [I] GUID to convert to string.
   *  str  [O] Buffer where the result will be stored.
   *  cmax [I] Size of the buffer in characters.
   *
   * RETURNS
   *	Success: The length of the resulting string in characters.
   *  Failure: 0.
   */
  INT WINAPI StringFromGUID2(REFGUID id, LPOLESTR str, INT cmax)
  {
      static const WCHAR formatW[] = { '{','%','0','8','X','-','%','0','4','X','-',
                                       '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
                                       '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
                                       '%','0','2','X','%','0','2','X','}',0 };
      if (!id || cmax < CHARS_IN_GUID) return 0;
      sprintfW( str, formatW, id->Data1, id->Data2, id->Data3,
                id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
                id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7] );
      return CHARS_IN_GUID;
  }
  
  /* open HKCR\\CLSID\\{string form of clsid}\\{keyname} key */
  HRESULT COM_OpenKeyForCLSID(REFCLSID clsid, LPCWSTR keyname, REGSAM access, HKEY *subkey)
  {
      static const WCHAR wszCLSIDSlash[] = {'C','L','S','I','D','\\',0};
      WCHAR path[CHARS_IN_GUID + ARRAY_SIZE(wszCLSIDSlash) - 1];
      LONG res;
      HKEY key;
  
      strcpyW(path, wszCLSIDSlash);
      StringFromGUID2(clsid, path + strlenW(wszCLSIDSlash), CHARS_IN_GUID);
      res = open_classes_key(HKEY_CLASSES_ROOT, path, keyname ? KEY_READ : access, &key);
      if (res == ERROR_FILE_NOT_FOUND)
          return REGDB_E_CLASSNOTREG;
      else if (res != ERROR_SUCCESS)
          return REGDB_E_READREGDB;
  
      if (!keyname)
      {
          *subkey = key;
          return S_OK;
      }
  
      res = open_classes_key(key, keyname, access, subkey);
      RegCloseKey(key);
      if (res == ERROR_FILE_NOT_FOUND)
          return REGDB_E_KEYMISSING;
      else if (res != ERROR_SUCCESS)
          return REGDB_E_READREGDB;
  
      return S_OK;
  }
  
  /* open HKCR\\AppId\\{string form of appid clsid} key */
  HRESULT COM_OpenKeyForAppIdFromCLSID(REFCLSID clsid, REGSAM access, HKEY *subkey)
  {
      static const WCHAR szAppId[] = { 'A','p','p','I','d',0 };
      static const WCHAR szAppIdKey[] = { 'A','p','p','I','d','\\',0 };
      DWORD res;
      WCHAR buf[CHARS_IN_GUID];
      WCHAR keyname[ARRAY_SIZE(szAppIdKey) + CHARS_IN_GUID];
      DWORD size;
      HKEY hkey;
      DWORD type;
      HRESULT hr;
  
      /* read the AppID value under the class's key */
      hr = COM_OpenKeyForCLSID(clsid, NULL, KEY_READ, &hkey);
      if (FAILED(hr))
          return hr;
  
      size = sizeof(buf);
      res = RegQueryValueExW(hkey, szAppId, NULL, &type, (LPBYTE)buf, &size);
      RegCloseKey(hkey);
      if (res == ERROR_FILE_NOT_FOUND)
          return REGDB_E_KEYMISSING;
      else if (res != ERROR_SUCCESS || type!=REG_SZ)
          return REGDB_E_READREGDB;
  
      strcpyW(keyname, szAppIdKey);
      strcatW(keyname, buf);
      res = open_classes_key(HKEY_CLASSES_ROOT, keyname, access, subkey);
      if (res == ERROR_FILE_NOT_FOUND)
          return REGDB_E_KEYMISSING;
      else if (res != ERROR_SUCCESS)
          return REGDB_E_READREGDB;
  
      return S_OK;
  }
  
  /******************************************************************************
   *               ProgIDFromCLSID [OLE32.@]
   *
   * Converts a class id into the respective program ID.
   *
   * PARAMS
   *  clsid        [I] Class ID, as found in registry.
   *  ppszProgID [O] Associated ProgID.
   *
   * RETURNS
   *   S_OK
   *   E_OUTOFMEMORY
   *   REGDB_E_CLASSNOTREG if the given clsid has no associated ProgID
   */
  HRESULT WINAPI DECLSPEC_HOTPATCH ProgIDFromCLSID(REFCLSID clsid, LPOLESTR *ppszProgID)
  {
      static const WCHAR wszProgID[] = {'P','r','o','g','I','D',0};
      ACTCTX_SECTION_KEYED_DATA data;
      HKEY     hkey;
      HRESULT  ret;
      LONG progidlen = 0;
  
      if (!ppszProgID)
          return E_INVALIDARG;
  
      *ppszProgID = NULL;
  
      data.cbSize = sizeof(data);
      if (FindActCtxSectionGuid(0, NULL, ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION,
                                clsid, &data))
      {
          struct comclassredirect_data *comclass = (struct comclassredirect_data*)data.lpData;
          if (comclass->progid_len)
          {
              WCHAR *ptrW;
  
              *ppszProgID = CoTaskMemAlloc(comclass->progid_len + sizeof(WCHAR));
              if (!*ppszProgID) return E_OUTOFMEMORY;
  
              ptrW = (WCHAR*)((BYTE*)comclass + comclass->progid_offset);
              memcpy(*ppszProgID, ptrW, comclass->progid_len + sizeof(WCHAR));
              return S_OK;
          }
          else
              return REGDB_E_CLASSNOTREG;
      }
  
      ret = COM_OpenKeyForCLSID(clsid, wszProgID, KEY_READ, &hkey);
      if (FAILED(ret))
          return ret;
  
      if (RegQueryValueW(hkey, NULL, NULL, &progidlen))
        ret = REGDB_E_CLASSNOTREG;
  
      if (ret == S_OK)
      {
        *ppszProgID = CoTaskMemAlloc(progidlen * sizeof(WCHAR));
        if (*ppszProgID)
        {
          if (RegQueryValueW(hkey, NULL, *ppszProgID, &progidlen)) {
            ret = REGDB_E_CLASSNOTREG;
            CoTaskMemFree(*ppszProgID);
            *ppszProgID = NULL;
          }
        }
        else
          ret = E_OUTOFMEMORY;
      }
  
      RegCloseKey(hkey);
      return ret;
  }
  
  /******************************************************************************
   *		CLSIDFromProgID	[OLE32.@]
   *
   * Converts a program id into the respective GUID.
   *
   * PARAMS
   *  progid [I] Unicode program ID, as found in registry.
   *  clsid  [O] Associated CLSID.
   *
   * RETURNS
   *	Success: S_OK
   *  Failure: CO_E_CLASSSTRING - the given ProgID cannot be found.
   */
  HRESULT WINAPI DECLSPEC_HOTPATCH CLSIDFromProgID(LPCOLESTR progid, LPCLSID clsid)
  {
      ACTCTX_SECTION_KEYED_DATA data;
  
      if (!progid || !clsid)
          return E_INVALIDARG;
  
      data.cbSize = sizeof(data);
      if (FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_COM_PROGID_REDIRECTION,
                                   progid, &data))
      {
          struct progidredirect_data *progiddata = (struct progidredirect_data*)data.lpData;
          CLSID *alias = (CLSID*)((BYTE*)data.lpSectionBase + progiddata->clsid_offset);
          *clsid = *alias;
          return S_OK;
      }
  
      return clsid_from_string_reg(progid, clsid);
  }
  
  /******************************************************************************
   *              CLSIDFromProgIDEx [OLE32.@]
   */
  HRESULT WINAPI CLSIDFromProgIDEx(LPCOLESTR progid, LPCLSID clsid)
  {
      FIXME("%s,%p: semi-stub\n", debugstr_w(progid), clsid);
  
      return CLSIDFromProgID(progid, clsid);
  }
  
  static HRESULT get_ps_clsid_from_registry(const WCHAR* path, REGSAM access, CLSID *pclsid)
  {
      HKEY hkey;
      WCHAR value[CHARS_IN_GUID];
      DWORD len;
  
      access |= KEY_READ;
  
      if (open_classes_key(HKEY_CLASSES_ROOT, path, access, &hkey))
          return REGDB_E_IIDNOTREG;
  
      len = sizeof(value);
      if (ERROR_SUCCESS != RegQueryValueExW(hkey, NULL, NULL, NULL, (BYTE *)value, &len))
          return REGDB_E_IIDNOTREG;
      RegCloseKey(hkey);
  
      if (CLSIDFromString(value, pclsid) != NOERROR)
          return REGDB_E_IIDNOTREG;
  
      return S_OK;
  }
  
  /*****************************************************************************
   *             CoGetPSClsid [OLE32.@]
   *
   * Retrieves the CLSID of the proxy/stub factory that implements
   * IPSFactoryBuffer for the specified interface.
   *
   * PARAMS
   *  riid   [I] Interface whose proxy/stub CLSID is to be returned.
   *  pclsid [O] Where to store returned proxy/stub CLSID.
   * 
   * RETURNS
   *   S_OK
   *   E_OUTOFMEMORY
   *   REGDB_E_IIDNOTREG if no PSFactoryBuffer is associated with the IID, or it could not be parsed
   *
   * NOTES
   *
   * The standard marshaller activates the object with the CLSID
   * returned and uses the CreateProxy and CreateStub methods on its
   * IPSFactoryBuffer interface to construct the proxies and stubs for a
   * given object.
   *
   * CoGetPSClsid determines this CLSID by searching the
   * HKEY_CLASSES_ROOT\Interface\{string form of riid}\ProxyStubClsid32
   * in the registry and any interface id registered by
   * CoRegisterPSClsid within the current process.
   *
   * BUGS
   *
   * Native returns S_OK for interfaces with a key in HKCR\Interface, but
   * without a ProxyStubClsid32 key and leaves garbage in pclsid. This should be
   * considered a bug in native unless an application depends on this (unlikely).
   *
   * SEE ALSO
   *  CoRegisterPSClsid.
   */
  HRESULT WINAPI CoGetPSClsid(REFIID riid, CLSID *pclsid)
  {
      static const WCHAR wszInterface[] = {'I','n','t','e','r','f','a','c','e','\\',0};
      static const WCHAR wszPSC[] = {'\\','P','r','o','x','y','S','t','u','b','C','l','s','i','d','3','2',0};
      WCHAR path[ARRAY_SIZE(wszInterface) - 1 + CHARS_IN_GUID - 1 + ARRAY_SIZE(wszPSC)];
      APARTMENT *apt;
      struct registered_psclsid *registered_psclsid;
      ACTCTX_SECTION_KEYED_DATA data;
      HRESULT hr;
      REGSAM opposite = (sizeof(void*) > sizeof(int)) ? KEY_WOW64_32KEY : KEY_WOW64_64KEY;
      BOOL is_wow64;
  
      TRACE("() riid=%s, pclsid=%p\n", debugstr_guid(riid), pclsid);
  
      if (!(apt = apartment_get_current_or_mta()))
      {
          ERR("apartment not initialised\n");
          return CO_E_NOTINITIALIZED;
      }
      apartment_release(apt);
  
      if (!pclsid)
          return E_INVALIDARG;
  
      EnterCriticalSection(&cs_registered_psclsid_list);
  
      LIST_FOR_EACH_ENTRY(registered_psclsid, &registered_psclsid_list, struct registered_psclsid, entry)
          if (IsEqualIID(&registered_psclsid->iid, riid))
          {
              *pclsid = registered_psclsid->clsid;
              LeaveCriticalSection(&cs_registered_psclsid_list);
              return S_OK;
          }
  
      LeaveCriticalSection(&cs_registered_psclsid_list);
  
      data.cbSize = sizeof(data);
      if (FindActCtxSectionGuid(0, NULL, ACTIVATION_CONTEXT_SECTION_COM_INTERFACE_REDIRECTION,
                                riid, &data))
      {
          struct ifacepsredirect_data *ifaceps = (struct ifacepsredirect_data*)data.lpData;
          *pclsid = ifaceps->iid;
          return S_OK;
      }
  
      /* Interface\\{string form of riid}\\ProxyStubClsid32 */
      strcpyW(path, wszInterface);
      StringFromGUID2(riid, path + ARRAY_SIZE(wszInterface) - 1, CHARS_IN_GUID);
      strcpyW(path + ARRAY_SIZE(wszInterface) - 1 + CHARS_IN_GUID - 1, wszPSC);
  
      hr = get_ps_clsid_from_registry(path, 0, pclsid);
      if (FAILED(hr) && (opposite == KEY_WOW64_32KEY ||
                         (IsWow64Process(GetCurrentProcess(), &is_wow64) && is_wow64)))
          hr = get_ps_clsid_from_registry(path, opposite, pclsid);
  
      if (hr == S_OK)
          TRACE ("() Returning CLSID=%s\n", debugstr_guid(pclsid));
      else
          WARN("No PSFactoryBuffer object is registered for IID %s\n", debugstr_guid(riid));
  
      return hr;
  }
  
  /*****************************************************************************
   *             CoRegisterPSClsid [OLE32.@]
   *
   * Register a proxy/stub CLSID for the given interface in the current process
   * only.
   *
   * PARAMS
   *  riid   [I] Interface whose proxy/stub CLSID is to be registered.
   *  rclsid [I] CLSID of the proxy/stub.
   * 
   * RETURNS
   *   Success: S_OK
   *   Failure: E_OUTOFMEMORY
   *
   * NOTES
   *
   * Unlike CoRegisterClassObject(), CLSIDs registered with CoRegisterPSClsid()
   * will be returned from other apartments in the same process.
   *
   * This function does not add anything to the registry and the effects are
   * limited to the lifetime of the current process.
   *
   * SEE ALSO
   *  CoGetPSClsid.
   */
  HRESULT WINAPI CoRegisterPSClsid(REFIID riid, REFCLSID rclsid)
  {
      APARTMENT *apt;
      struct registered_psclsid *registered_psclsid;
  
      TRACE("(%s, %s)\n", debugstr_guid(riid), debugstr_guid(rclsid));
  
      if (!(apt = apartment_get_current_or_mta()))
      {
          ERR("apartment not initialised\n");
          return CO_E_NOTINITIALIZED;
      }
      apartment_release(apt);
  
      EnterCriticalSection(&cs_registered_psclsid_list);
  
      LIST_FOR_EACH_ENTRY(registered_psclsid, &registered_psclsid_list, struct registered_psclsid, entry)
          if (IsEqualIID(&registered_psclsid->iid, riid))
          {
              registered_psclsid->clsid = *rclsid;
              LeaveCriticalSection(&cs_registered_psclsid_list);
              return S_OK;
          }
  
      registered_psclsid = HeapAlloc(GetProcessHeap(), 0, sizeof(struct registered_psclsid));
      if (!registered_psclsid)
      {
          LeaveCriticalSection(&cs_registered_psclsid_list);
          return E_OUTOFMEMORY;
      }
  
      registered_psclsid->iid = *riid;
      registered_psclsid->clsid = *rclsid;
      list_add_head(&registered_psclsid_list, &registered_psclsid->entry);
  
      LeaveCriticalSection(&cs_registered_psclsid_list);
  
      return S_OK;
  }
  
  
  /***
   * COM_GetRegisteredClassObject
   *
   * This internal method is used to scan the registered class list to
   * find a class object.
   *
   * Params:
   *   rclsid        Class ID of the class to find.
   *   dwClsContext  Class context to match.
   *   ppv           [out] returns a pointer to the class object. Complying
   *                 to normal COM usage, this method will increase the
   *                 reference count on this object.
   */
  static HRESULT COM_GetRegisteredClassObject(const struct apartment *apt, REFCLSID rclsid,
                                              DWORD dwClsContext, LPUNKNOWN* ppUnk)
  {
    HRESULT hr = S_FALSE;
    RegisteredClass *curClass;
  
    EnterCriticalSection( &csRegisteredClassList );
  
    LIST_FOR_EACH_ENTRY(curClass, &RegisteredClassList, RegisteredClass, entry)
    {
      /*
       * Check if we have a match on the class ID and context.
       */
      if ((apt->oxid == curClass->apartment_id) &&
          (dwClsContext & curClass->runContext) &&
          IsEqualGUID(&(curClass->classIdentifier), rclsid))
      {
        /*
         * We have a match, return the pointer to the class object.
         */
        *ppUnk = curClass->classObject;
  
        IUnknown_AddRef(curClass->classObject);
  
        hr = S_OK;
        break;
      }
    }
  
    LeaveCriticalSection( &csRegisteredClassList );
  
    return hr;
  }
  
  /******************************************************************************
   *		CoRegisterClassObject	[OLE32.@]
   *
   * Registers the class object for a given class ID. Servers housed in EXE
   * files use this method instead of exporting DllGetClassObject to allow
   * other code to connect to their objects.
   *
   * PARAMS
   *  rclsid       [I] CLSID of the object to register.
   *  pUnk         [I] IUnknown of the object.
   *  dwClsContext [I] CLSCTX flags indicating the context in which to run the executable.
   *  flags        [I] REGCLS flags indicating how connections are made.
   *  lpdwRegister [I] A unique cookie that can be passed to CoRevokeClassObject.
   *
   * RETURNS
   *   S_OK on success,
   *   E_INVALIDARG if lpdwRegister or pUnk are NULL,
   *   CO_E_OBJISREG if the object is already registered. We should not return this.
   *
   * SEE ALSO
   *   CoRevokeClassObject, CoGetClassObject
   *
   * NOTES
   *  In-process objects are only registered for the current apartment.
   *  CoGetClassObject() and CoCreateInstance() will not return objects registered
   *  in other apartments.
   *
   * BUGS
   *  MSDN claims that multiple interface registrations are legal, but we
   *  can't do that with our current implementation.
   */
  HRESULT WINAPI CoRegisterClassObject(
      REFCLSID rclsid,
      LPUNKNOWN pUnk,
      DWORD dwClsContext,
      DWORD flags,
      LPDWORD lpdwRegister)
  {
    static LONG next_cookie;
    RegisteredClass* newClass;
    LPUNKNOWN        foundObject;
    HRESULT          hr;
    APARTMENT *apt;
  
    TRACE("(%s,%p,0x%08x,0x%08x,%p)\n",
  	debugstr_guid(rclsid),pUnk,dwClsContext,flags,lpdwRegister);
  
    if ( (lpdwRegister==0) || (pUnk==0) )
      return E_INVALIDARG;
  
    if (!(apt = apartment_get_current_or_mta()))
    {
        ERR("COM was not initialized\n");
        return CO_E_NOTINITIALIZED;
    }
  
    *lpdwRegister = 0;
  
    /* REGCLS_MULTIPLEUSE implies registering as inproc server. This is what
     * differentiates the flag from REGCLS_MULTI_SEPARATE. */
    if (flags & REGCLS_MULTIPLEUSE)
      dwClsContext |= CLSCTX_INPROC_SERVER;
  
    /*
     * First, check if the class is already registered.
     * If it is, this should cause an error.
     */
    hr = COM_GetRegisteredClassObject(apt, rclsid, dwClsContext, &foundObject);
    if (hr == S_OK) {
      if (flags & REGCLS_MULTIPLEUSE) {
        if (dwClsContext & CLSCTX_LOCAL_SERVER)
          hr = CoLockObjectExternal(foundObject, TRUE, FALSE);
        IUnknown_Release(foundObject);
        apartment_release(apt);
        return hr;
      }
      IUnknown_Release(foundObject);
      ERR("object already registered for class %s\n", debugstr_guid(rclsid));
      apartment_release(apt);
      return CO_E_OBJISREG;
    }
  
    newClass = HeapAlloc(GetProcessHeap(), 0, sizeof(RegisteredClass));
    if ( newClass == NULL )
    {
      apartment_release(apt);
      return E_OUTOFMEMORY;
    }
  
    newClass->classIdentifier = *rclsid;
    newClass->apartment_id    = apt->oxid;
    newClass->runContext      = dwClsContext;
    newClass->connectFlags    = flags;
    newClass->RpcRegistration = NULL;
  
    if (!(newClass->dwCookie = InterlockedIncrement( &next_cookie )))
        newClass->dwCookie = InterlockedIncrement( &next_cookie );
  
    /*
     * Since we're making a copy of the object pointer, we have to increase its
     * reference count.
     */
    newClass->classObject     = pUnk;
    IUnknown_AddRef(newClass->classObject);
  
    EnterCriticalSection( &csRegisteredClassList );
    list_add_tail(&RegisteredClassList, &newClass->entry);
    LeaveCriticalSection( &csRegisteredClassList );
  
    *lpdwRegister = newClass->dwCookie;
  
    if (dwClsContext & CLSCTX_LOCAL_SERVER) {
        IStream *marshal_stream;
  
        hr = get_local_server_stream(apt, &marshal_stream);
        if(FAILED(hr))
        {
            apartment_release(apt);
            return hr;
        }
  
        hr = RPC_StartLocalServer(&newClass->classIdentifier,
                                  marshal_stream,
                                  flags & (REGCLS_MULTIPLEUSE|REGCLS_MULTI_SEPARATE),
                                  &newClass->RpcRegistration);
        IStream_Release(marshal_stream);
    }
    apartment_release(apt);
    return S_OK;
  }
  
  static enum comclass_threadingmodel get_threading_model(const struct class_reg_data *data)
  {
      if (data->hkey)
      {
          static const WCHAR wszThreadingModel[] = {'T','h','r','e','a','d','i','n','g','M','o','d','e','l',0};
          static const WCHAR wszApartment[] = {'A','p','a','r','t','m','e','n','t',0};
          static const WCHAR wszFree[] = {'F','r','e','e',0};
          static const WCHAR wszBoth[] = {'B','o','t','h',0};
          WCHAR threading_model[10 /* strlenW(L"apartment")+1 */];
          DWORD dwLength = sizeof(threading_model);
          DWORD keytype;
          DWORD ret;
  
          ret = RegQueryValueExW(data->u.hkey, wszThreadingModel, NULL, &keytype, (BYTE*)threading_model, &dwLength);
          if ((ret != ERROR_SUCCESS) || (keytype != REG_SZ))
              threading_model[0] = '\0';
  
          if (!strcmpiW(threading_model, wszApartment)) return ThreadingModel_Apartment;
          if (!strcmpiW(threading_model, wszFree)) return ThreadingModel_Free;
          if (!strcmpiW(threading_model, wszBoth)) return ThreadingModel_Both;
  
          /* there's not specific handling for this case */
          if (threading_model[0]) return ThreadingModel_Neutral;
          return ThreadingModel_No;
      }
      else
          return data->u.actctx.data->model;
  }
  
  static HRESULT get_inproc_class_object(APARTMENT *apt, const struct class_reg_data *regdata,
                                         REFCLSID rclsid, REFIID riid,
                                         BOOL hostifnecessary, void **ppv)
  {
      WCHAR dllpath[MAX_PATH+1];
      BOOL apartment_threaded;
  
      if (hostifnecessary)
      {
          enum comclass_threadingmodel model = get_threading_model(regdata);
  
          if (model == ThreadingModel_Apartment)
          {
              apartment_threaded = TRUE;
              if (apt->multi_threaded)
                  return apartment_hostobject_in_hostapt(apt, FALSE, FALSE, regdata, rclsid, riid, ppv);
          }
          else if (model == ThreadingModel_Free)
          {
              apartment_threaded = FALSE;
              if (!apt->multi_threaded)
                  return apartment_hostobject_in_hostapt(apt, TRUE, FALSE, regdata, rclsid, riid, ppv);
          }
          /* everything except "Apartment", "Free" and "Both" */
          else if (model != ThreadingModel_Both)
          {
              apartment_threaded = TRUE;
              /* everything else is main-threaded */
              if (model != ThreadingModel_No)
                  FIXME("unrecognised threading model %d for object %s, should be main-threaded?\n", model, debugstr_guid(rclsid));
  
              if (apt->multi_threaded || !apt->main)
                  return apartment_hostobject_in_hostapt(apt, FALSE, TRUE, regdata, rclsid, riid, ppv);
          }
          else
              apartment_threaded = FALSE;
      }
      else
          apartment_threaded = !apt->multi_threaded;
  
      if (!get_object_dll_path(regdata, dllpath, ARRAY_SIZE(dllpath)))
      {
          /* failure: CLSID is not found in registry */
          WARN("class %s not registered inproc\n", debugstr_guid(rclsid));
          return REGDB_E_CLASSNOTREG;
      }
  
      return apartment_getclassobject(apt, dllpath, apartment_threaded,
                                      rclsid, riid, ppv);
  }
  
  /***********************************************************************
   *           CoGetClassObject [OLE32.@]
   *
   * Creates an object of the specified class.
   *
   * PARAMS
   *  rclsid       [I] Class ID to create an instance of.
   *  dwClsContext [I] Flags to restrict the location of the created instance.
   *  pServerInfo  [I] Optional. Details for connecting to a remote server.
   *  iid          [I] The ID of the interface of the instance to return.
   *  ppv          [O] On returns, contains a pointer to the specified interface of the object.
   *
   * RETURNS
   *  Success: S_OK
   *  Failure: HRESULT code.
   *
   * NOTES
   *  The dwClsContext parameter can be one or more of the following:
   *| CLSCTX_INPROC_SERVER - Use an in-process server, such as from a DLL.
   *| CLSCTX_INPROC_HANDLER - Use an in-process object which handles certain functions for an object running in another process.
   *| CLSCTX_LOCAL_SERVER - Connect to an object running in another process.
   *| CLSCTX_REMOTE_SERVER - Connect to an object running on another machine.
   *
   * SEE ALSO
   *  CoCreateInstance()
   */
  HRESULT WINAPI DECLSPEC_HOTPATCH CoGetClassObject(
      REFCLSID rclsid, DWORD dwClsContext, COSERVERINFO *pServerInfo,
      REFIID iid, LPVOID *ppv)
  {
      struct class_reg_data clsreg;
      IUnknown *regClassObject;
      HRESULT	hres = E_UNEXPECTED;
      APARTMENT  *apt;
  
      TRACE("CLSID: %s,IID: %s\n", debugstr_guid(rclsid), debugstr_guid(iid));
  
      if (!ppv)
          return E_INVALIDARG;
  
      *ppv = NULL;
  
      if (!(apt = apartment_get_current_or_mta()))
      {
          ERR("apartment not initialised\n");
          return CO_E_NOTINITIALIZED;
      }
  
      if (pServerInfo) {
  	FIXME("pServerInfo->name=%s pAuthInfo=%p\n",
                debugstr_w(pServerInfo->pwszName), pServerInfo->pAuthInfo);
      }
  
      if (CLSCTX_INPROC_SERVER & dwClsContext)
      {
          if (IsEqualCLSID(rclsid, &CLSID_InProcFreeMarshaler))
          {
              apartment_release(apt);
              return FTMarshalCF_Create(iid, ppv);
          }
          if (IsEqualCLSID(rclsid, &CLSID_GlobalOptions))
              return IClassFactory_QueryInterface(&GlobalOptionsCF, iid, ppv);
      }
  
      if (CLSCTX_INPROC & dwClsContext)
      {
          ACTCTX_SECTION_KEYED_DATA data;
  
          data.cbSize = sizeof(data);
          /* search activation context first */
          if (FindActCtxSectionGuid(FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX, NULL,
                                    ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION,
                                    rclsid, &data))
          {
              struct comclassredirect_data *comclass = (struct comclassredirect_data*)data.lpData;
  
              clsreg.u.actctx.hactctx = data.hActCtx;
              clsreg.u.actctx.data = data.lpData;
              clsreg.u.actctx.section = data.lpSectionBase;
              clsreg.hkey = FALSE;
  
              hres = get_inproc_class_object(apt, &clsreg, &comclass->clsid, iid, !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv);
              ReleaseActCtx(data.hActCtx);
              apartment_release(apt);
              return hres;
          }
      }
  
      /*
       * First, try and see if we can't match the class ID with one of the
       * registered classes.
       */
      if (S_OK == COM_GetRegisteredClassObject(apt, rclsid, dwClsContext,
                                               &regClassObject))
      {
        /* Get the required interface from the retrieved pointer. */
        hres = IUnknown_QueryInterface(regClassObject, iid, ppv);
  
        /*
         * Since QI got another reference on the pointer, we want to release the
         * one we already have. If QI was unsuccessful, this will release the object. This
         * is good since we are not returning it in the "out" parameter.
         */
        IUnknown_Release(regClassObject);
        apartment_release(apt);
        return hres;
      }
  
      /* First try in-process server */
      if (CLSCTX_INPROC_SERVER & dwClsContext)
      {
          static const WCHAR wszInprocServer32[] = {'I','n','p','r','o','c','S','e','r','v','e','r','3','2',0};
          HKEY hkey;
  
          hres = COM_OpenKeyForCLSID(rclsid, wszInprocServer32, KEY_READ, &hkey);
          if (FAILED(hres))
          {
              if (hres == REGDB_E_CLASSNOTREG)
                  ERR("class %s not registered\n", debugstr_guid(rclsid));
              else if (hres == REGDB_E_KEYMISSING)
              {
                  WARN("class %s not registered as in-proc server\n", debugstr_guid(rclsid));
                  hres = REGDB_E_CLASSNOTREG;
              }
          }
  
          if (SUCCEEDED(hres))
          {
              clsreg.u.hkey = hkey;
              clsreg.hkey = TRUE;
  
              hres = get_inproc_class_object(apt, &clsreg, rclsid, iid, !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv);
              RegCloseKey(hkey);
          }
  
          /* return if we got a class, otherwise fall through to one of the
           * other types */
          if (SUCCEEDED(hres))
          {
              apartment_release(apt);
              return hres;
          }
      }
  
      /* Next try in-process handler */
      if (CLSCTX_INPROC_HANDLER & dwClsContext)
      {
          static const WCHAR wszInprocHandler32[] = {'I','n','p','r','o','c','H','a','n','d','l','e','r','3','2',0};
          HKEY hkey;
  
          hres = COM_OpenKeyForCLSID(rclsid, wszInprocHandler32, KEY_READ, &hkey);
          if (FAILED(hres))
          {
              if (hres == REGDB_E_CLASSNOTREG)
                  ERR("class %s not registered\n", debugstr_guid(rclsid));
              else if (hres == REGDB_E_KEYMISSING)
              {
                  WARN("class %s not registered in-proc handler\n", debugstr_guid(rclsid));
                  hres = REGDB_E_CLASSNOTREG;
              }
          }
  
          if (SUCCEEDED(hres))
          {
              clsreg.u.hkey = hkey;
              clsreg.hkey = TRUE;
  
              hres = get_inproc_class_object(apt, &clsreg, rclsid, iid, !(dwClsContext & WINE_CLSCTX_DONT_HOST), ppv);
              RegCloseKey(hkey);
          }
  
          /* return if we got a class, otherwise fall through to one of the
           * other types */
          if (SUCCEEDED(hres))
          {
              apartment_release(apt);
              return hres;
          }
      }
      apartment_release(apt);
  
      /* Next try out of process */
      if (CLSCTX_LOCAL_SERVER & dwClsContext)
      {
          hres = RPC_GetLocalClassObject(rclsid,iid,ppv);
          if (SUCCEEDED(hres))
              return hres;
      }
  
      /* Finally try remote: this requires networked DCOM (a lot of work) */
      if (CLSCTX_REMOTE_SERVER & dwClsContext)
      {
          FIXME ("CLSCTX_REMOTE_SERVER not supported\n");
          hres = REGDB_E_CLASSNOTREG;
      }
  
      if (FAILED(hres))
          ERR("no class object %s could be created for context 0x%x\n",
              debugstr_guid(rclsid), dwClsContext);
      return hres;
  }
  
  /***********************************************************************
   *        CoResumeClassObjects (OLE32.@)
   *
   * Resumes all class objects registered with REGCLS_SUSPENDED.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: HRESULT code.
   */
  HRESULT WINAPI CoResumeClassObjects(void)
  {
         FIXME("stub\n");
  	return S_OK;
  }
  
  /***********************************************************************
   *           CoCreateInstance [OLE32.@]
   *
   * Creates an instance of the specified class.
   *
   * PARAMS
   *  rclsid       [I] Class ID to create an instance of.
   *  pUnkOuter    [I] Optional outer unknown to allow aggregation with another object.
   *  dwClsContext [I] Flags to restrict the location of the created instance.
   *  iid          [I] The ID of the interface of the instance to return.
   *  ppv          [O] On returns, contains a pointer to the specified interface of the instance.
   *
   * RETURNS
   *  Success: S_OK
   *  Failure: HRESULT code.
   *
   * NOTES
   *  The dwClsContext parameter can be one or more of the following:
   *| CLSCTX_INPROC_SERVER - Use an in-process server, such as from a DLL.
   *| CLSCTX_INPROC_HANDLER - Use an in-process object which handles certain functions for an object running in another process.
   *| CLSCTX_LOCAL_SERVER - Connect to an object running in another process.
   *| CLSCTX_REMOTE_SERVER - Connect to an object running on another machine.
   *
   * Aggregation is the concept of deferring the IUnknown of an object to another
   * object. This allows a separate object to behave as though it was part of
   * the object and to allow this the pUnkOuter parameter can be set. Note that
   * not all objects support having an outer of unknown.
   *
   * SEE ALSO
   *  CoGetClassObject()
   */
  HRESULT WINAPI DECLSPEC_HOTPATCH CoCreateInstance(
      REFCLSID rclsid,
      LPUNKNOWN pUnkOuter,
      DWORD dwClsContext,
      REFIID iid,
      LPVOID *ppv)
  {
      MULTI_QI multi_qi = { iid };
      HRESULT hres;
  
      TRACE("(rclsid=%s, pUnkOuter=%p, dwClsContext=%08x, riid=%s, ppv=%p)\n", debugstr_guid(rclsid),
            pUnkOuter, dwClsContext, debugstr_guid(iid), ppv);
  
      if (ppv==0)
          return E_POINTER;
  
      hres = CoCreateInstanceEx(rclsid, pUnkOuter, dwClsContext, NULL, 1, &multi_qi);
      *ppv = multi_qi.pItf;
      return hres;
  }
  
  static void init_multi_qi(DWORD count, MULTI_QI *mqi, HRESULT hr)
  {
    ULONG i;
  
    for (i = 0; i < count; i++)
    {
        mqi[i].pItf = NULL;
        mqi[i].hr = hr;
    }
  }
  
  static HRESULT return_multi_qi(IUnknown *unk, DWORD count, MULTI_QI *mqi, BOOL include_unk)
  {
    ULONG index = 0, fetched = 0;
  
    if (include_unk)
    {
      mqi[0].hr = S_OK;
      mqi[0].pItf = unk;
      index = fetched = 1;
    }
  
    for (; index < count; index++)
    {
      mqi[index].hr = IUnknown_QueryInterface(unk, mqi[index].pIID, (void**)&mqi[index].pItf);
      if (mqi[index].hr == S_OK)
        fetched++;
    }
  
    if (!include_unk)
        IUnknown_Release(unk);
  
    if (fetched == 0)
      return E_NOINTERFACE;
  
    return fetched == count ? S_OK : CO_S_NOTALLINTERFACES;
  }
  
  /***********************************************************************
   *           CoCreateInstanceEx [OLE32.@]
   */
  HRESULT WINAPI DECLSPEC_HOTPATCH CoCreateInstanceEx(
    REFCLSID      rclsid,
    LPUNKNOWN     pUnkOuter,
    DWORD         dwClsContext,
    COSERVERINFO* pServerInfo,
    ULONG         cmq,
    MULTI_QI*     pResults)
  {
      IUnknown *unk = NULL;
      IClassFactory *cf;
      APARTMENT *apt;
      CLSID clsid;
      HRESULT hres;
  
      TRACE("(%s %p %x %p %u %p)\n", debugstr_guid(rclsid), pUnkOuter, dwClsContext, pServerInfo, cmq, pResults);
  
      if (!cmq || !pResults)
          return E_INVALIDARG;
  
      if (pServerInfo)
          FIXME("() non-NULL pServerInfo not supported!\n");
  
      init_multi_qi(cmq, pResults, E_NOINTERFACE);
  
      hres = CoGetTreatAsClass(rclsid, &clsid);
      if(FAILED(hres))
          clsid = *rclsid;
  
      if (!(apt = apartment_get_current_or_mta()))
      {
          ERR("apartment not initialised\n");
          return CO_E_NOTINITIALIZED;
      }
      apartment_release(apt);
  
      /*
       * The Standard Global Interface Table (GIT) object is a process-wide singleton.
       */
      if (IsEqualIID(&clsid, &CLSID_StdGlobalInterfaceTable))
      {
          IGlobalInterfaceTable *git = get_std_git();
          TRACE("Retrieving GIT\n");
          return return_multi_qi((IUnknown*)git, cmq, pResults, FALSE);
      }
  
      if (IsEqualCLSID(&clsid, &CLSID_ManualResetEvent)) {
          hres = ManualResetEvent_Construct(pUnkOuter, pResults[0].pIID, (void**)&unk);
          if (FAILED(hres))
              return hres;
          return return_multi_qi(unk, cmq, pResults, TRUE);
      }
  
      /*
       * Get a class factory to construct the object we want.
       */
      hres = CoGetClassObject(&clsid, dwClsContext, NULL, &IID_IClassFactory, (void**)&cf);
      if (FAILED(hres))
          return hres;
  
      /*
       * Create the object and don't forget to release the factory
       */
      hres = IClassFactory_CreateInstance(cf, pUnkOuter, pResults[0].pIID, (void**)&unk);
      IClassFactory_Release(cf);
      if (FAILED(hres))
      {
          if (hres == CLASS_E_NOAGGREGATION && pUnkOuter)
              FIXME("Class %s does not support aggregation\n", debugstr_guid(&clsid));
          else
              FIXME("no instance created for interface %s of class %s, hres is 0x%08x\n",
                    debugstr_guid(pResults[0].pIID),
                    debugstr_guid(&clsid),hres);
          return hres;
      }
  
      return return_multi_qi(unk, cmq, pResults, TRUE);
  }
  
  /***********************************************************************
   *           CoGetInstanceFromFile [OLE32.@]
   */
  HRESULT WINAPI DECLSPEC_HOTPATCH CoGetInstanceFromFile(
    COSERVERINFO *server_info,
    CLSID        *rclsid,
    IUnknown     *outer,
    DWORD         cls_context,
    DWORD         grfmode,
    OLECHAR      *filename,
    DWORD         count,
    MULTI_QI     *results
  )
  {
    IPersistFile *pf = NULL;
    IUnknown* unk = NULL;
    CLSID clsid;
    HRESULT hr;
  
    if (count == 0 || !results)
      return E_INVALIDARG;
  
    if (server_info)
      FIXME("() non-NULL server_info not supported\n");
  
    init_multi_qi(count, results, E_NOINTERFACE);
  
    /* optionally get CLSID from a file */
    if (!rclsid)
    {
      hr = GetClassFile(filename, &clsid);
      if (FAILED(hr))
      {
        ERR("failed to get CLSID from a file\n");
        return hr;
      }
  
      rclsid = &clsid;
    }
  
    hr = CoCreateInstance(rclsid,
  			outer,
  			cls_context,
  			&IID_IUnknown,
  			(void**)&unk);
  
    if (hr != S_OK)
    {
        init_multi_qi(count, results, hr);
        return hr;
    }
  
    /* init from file */
    hr = IUnknown_QueryInterface(unk, &IID_IPersistFile, (void**)&pf);
    if (FAILED(hr))
    {
        init_multi_qi(count, results, hr);
        IUnknown_Release(unk);
        return hr;
    }
  
    hr = IPersistFile_Load(pf, filename, grfmode);
    IPersistFile_Release(pf);
    if (SUCCEEDED(hr))
        return return_multi_qi(unk, count, results, FALSE);
    else
    {
        init_multi_qi(count, results, hr);
        IUnknown_Release(unk);
        return hr;
    }
  }
  
  /***********************************************************************
   *           CoGetInstanceFromIStorage [OLE32.@]
   */
  HRESULT WINAPI CoGetInstanceFromIStorage(
    COSERVERINFO *server_info,
    CLSID        *rclsid,
    IUnknown     *outer,
    DWORD         cls_context,
    IStorage     *storage,
    DWORD         count,
    MULTI_QI     *results
  )
  {
    IPersistStorage *ps = NULL;
    IUnknown* unk = NULL;
    STATSTG stat;
    HRESULT hr;
  
    if (count == 0 || !results || !storage)
      return E_INVALIDARG;
  
    if (server_info)
      FIXME("() non-NULL server_info not supported\n");
  
    init_multi_qi(count, results, E_NOINTERFACE);
  
    /* optionally get CLSID from a file */
    if (!rclsid)
    {
      memset(&stat.clsid, 0, sizeof(stat.clsid));
      hr = IStorage_Stat(storage, &stat, STATFLAG_NONAME);
      if (FAILED(hr))
      {
        ERR("failed to get CLSID from a file\n");
        return hr;
      }
  
      rclsid = &stat.clsid;
    }
  
    hr = CoCreateInstance(rclsid,
  			outer,
  			cls_context,
  			&IID_IUnknown,
  			(void**)&unk);
  
    if (hr != S_OK)
      return hr;
  
    /* init from IStorage */
    hr = IUnknown_QueryInterface(unk, &IID_IPersistStorage, (void**)&ps);
    if (FAILED(hr))
        ERR("failed to get IPersistStorage\n");
  
    if (ps)
    {
        IPersistStorage_Load(ps, storage);
        IPersistStorage_Release(ps);
    }
  
    return return_multi_qi(unk, count, results, FALSE);
  }
  
  /***********************************************************************
   *           CoLoadLibrary (OLE32.@)
   *
   * Loads a library.
   *
   * PARAMS
   *  lpszLibName [I] Path to library.
   *  bAutoFree   [I] Whether the library should automatically be freed.
   *
   * RETURNS
   *  Success: Handle to loaded library.
   *  Failure: NULL.
   *
   * SEE ALSO
   *  CoFreeLibrary, CoFreeAllLibraries, CoFreeUnusedLibraries
   */
  HINSTANCE WINAPI CoLoadLibrary(LPOLESTR lpszLibName, BOOL bAutoFree)
  {
      TRACE("(%s, %d)\n", debugstr_w(lpszLibName), bAutoFree);
  
      return LoadLibraryExW(lpszLibName, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
  }
  
  /***********************************************************************
   *           CoFreeLibrary [OLE32.@]
   *
   * Unloads a library from memory.
   *
   * PARAMS
   *  hLibrary [I] Handle to library to unload.
   *
   * RETURNS
   *  Nothing
   *
   * SEE ALSO
   *  CoLoadLibrary, CoFreeAllLibraries, CoFreeUnusedLibraries
   */
  void WINAPI CoFreeLibrary(HINSTANCE hLibrary)
  {
      FreeLibrary(hLibrary);
  }
  
  
  /***********************************************************************
   *           CoFreeAllLibraries [OLE32.@]
   *
   * Function for backwards compatibility only. Does nothing.
   *
   * RETURNS
   *  Nothing.
   *
   * SEE ALSO
   *  CoLoadLibrary, CoFreeLibrary, CoFreeUnusedLibraries
   */
  void WINAPI CoFreeAllLibraries(void)
  {
      /* NOP */
  }
  
  /***********************************************************************
   *           CoFreeUnusedLibrariesEx [OLE32.@]
   *
   * Frees any previously unused libraries whose delay has expired and marks
   * currently unused libraries for unloading. Unused are identified as those that
   * return S_OK from their DllCanUnloadNow function.
   *
   * PARAMS
   *  dwUnloadDelay [I] Unload delay in milliseconds.
   *  dwReserved    [I] Reserved. Set to 0.
   *
   * RETURNS
   *  Nothing.
   *
   * SEE ALSO
   *  CoLoadLibrary, CoFreeAllLibraries, CoFreeLibrary
   */
  void WINAPI DECLSPEC_HOTPATCH CoFreeUnusedLibrariesEx(DWORD dwUnloadDelay, DWORD dwReserved)
  {
      struct apartment *apt = COM_CurrentApt();
      if (!apt)
      {
          ERR("apartment not initialised\n");
          return;
      }
  
      apartment_freeunusedlibraries(apt, dwUnloadDelay);
  }
  
  /***********************************************************************
   *           CoFreeUnusedLibraries [OLE32.@]
   *
   * Frees any unused libraries. Unused are identified as those that return
   * S_OK from their DllCanUnloadNow function.
   *
   * RETURNS
   *  Nothing.
   *
   * SEE ALSO
   *  CoLoadLibrary, CoFreeAllLibraries, CoFreeLibrary
   */
  void WINAPI DECLSPEC_HOTPATCH CoFreeUnusedLibraries(void)
  {
      CoFreeUnusedLibrariesEx(INFINITE, 0);
  }
  
  /***********************************************************************
   *           CoFileTimeNow [OLE32.@]
   *
   * Retrieves the current time in FILETIME format.
   *
   * PARAMS
   *  lpFileTime [O] The current time.
   *
   * RETURNS
   *	S_OK.
   */
  HRESULT WINAPI CoFileTimeNow( FILETIME *lpFileTime )
  {
      GetSystemTimeAsFileTime( lpFileTime );
      return S_OK;
  }
  
  /******************************************************************************
   *		CoLockObjectExternal	[OLE32.@]
   *
   * Increments or decrements the external reference count of a stub object.
   *
   * PARAMS
   *  pUnk                [I] Stub object.
   *  fLock               [I] If TRUE then increments the external ref-count,
   *                          otherwise decrements.
   *  fLastUnlockReleases [I] If TRUE then the last unlock has the effect of
   *                          calling CoDisconnectObject.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: HRESULT code.
   *
   * NOTES
   *  If fLock is TRUE and an object is passed in that doesn't have a stub
   *  manager then a new stub manager is created for the object.
   */
  HRESULT WINAPI CoLockObjectExternal(
      LPUNKNOWN pUnk,
      BOOL fLock,
      BOOL fLastUnlockReleases)
  {
      struct stub_manager *stubmgr;
      struct apartment *apt;
  
      TRACE("pUnk=%p, fLock=%s, fLastUnlockReleases=%s\n",
            pUnk, fLock ? "TRUE" : "FALSE", fLastUnlockReleases ? "TRUE" : "FALSE");
  
      if (!(apt = apartment_get_current_or_mta()))
      {
          ERR("apartment not initialised\n");
          return CO_E_NOTINITIALIZED;
      }
  
      stubmgr = get_stub_manager_from_object(apt, pUnk, fLock);
      if (!stubmgr)
      {
          WARN("stub object not found %p\n", pUnk);
          /* Note: native is pretty broken here because it just silently
           * fails, without returning an appropriate error code, making apps
           * think that the object was disconnected, when it actually wasn't */
          apartment_release(apt);
          return S_OK;
      }
  
      if (fLock)
          stub_manager_ext_addref(stubmgr, 1, FALSE);
      else
          stub_manager_ext_release(stubmgr, 1, FALSE, fLastUnlockReleases);
  
      stub_manager_int_release(stubmgr);
      apartment_release(apt);
      return S_OK;
  }
  
  /***********************************************************************
   *           CoInitializeWOW (OLE32.@)
   *
   * WOW equivalent of CoInitialize?
   *
   * PARAMS
   *  x [I] Unknown.
   *  y [I] Unknown.
   *
   * RETURNS
   *  Unknown.
   */
  HRESULT WINAPI CoInitializeWOW(DWORD x,DWORD y)
  {
      FIXME("(0x%08x,0x%08x),stub!\n",x,y);
      return 0;
  }
  
  /***********************************************************************
   *           CoGetState [OLE32.@]
   *
   * Retrieves the thread state object previously stored by CoSetState().
   *
   * PARAMS
   *  ppv [I] Address where pointer to object will be stored.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: E_OUTOFMEMORY.
   *
   * NOTES
   *  Crashes on all invalid ppv addresses, including NULL.
   *  If the function returns a non-NULL object then the caller must release its
   *  reference on the object when the object is no longer required.
   *
   * SEE ALSO
   *  CoSetState().
   */
  HRESULT WINAPI CoGetState(IUnknown ** ppv)
  {
      struct oletls *info = COM_CurrentInfo();
      if (!info) return E_OUTOFMEMORY;
  
      *ppv = NULL;
  
      if (info->state)
      {
          IUnknown_AddRef(info->state);
          *ppv = info->state;
          TRACE("apt->state=%p\n", info->state);
      }
  
      return S_OK;
  }
  
  /***********************************************************************
   *           CoSetState [OLE32.@]
   *
   * Sets the thread state object.
   *
   * PARAMS
   *  pv [I] Pointer to state object to be stored.
   *
   * NOTES
   *  The system keeps a reference on the object while the object stored.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: E_OUTOFMEMORY.
   */
  HRESULT WINAPI CoSetState(IUnknown * pv)
  {
      struct oletls *info = COM_CurrentInfo();
      if (!info) return E_OUTOFMEMORY;
  
      if (pv) IUnknown_AddRef(pv);
  
      if (info->state)
      {
          TRACE("-- release %p now\n", info->state);
          IUnknown_Release(info->state);
      }
  
      info->state = pv;
  
      return S_OK;
  }
  
  
  /******************************************************************************
   *              CoTreatAsClass        [OLE32.@]
   *
   * Sets the TreatAs value of a class.
   *
   * PARAMS
   *  clsidOld [I] Class to set TreatAs value on.
   *  clsidNew [I] The class the clsidOld should be treated as.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: HRESULT code.
   *
   * SEE ALSO
   *  CoGetTreatAsClass
   */
  HRESULT WINAPI CoTreatAsClass(REFCLSID clsidOld, REFCLSID clsidNew)
  {
      static const WCHAR wszAutoTreatAs[] = {'A','u','t','o','T','r','e','a','t','A','s',0};
      static const WCHAR wszTreatAs[] = {'T','r','e','a','t','A','s',0};
      HKEY hkey = NULL;
      WCHAR szClsidNew[CHARS_IN_GUID];
      HRESULT res = S_OK;
      WCHAR auto_treat_as[CHARS_IN_GUID];
      LONG auto_treat_as_size = sizeof(auto_treat_as);
      CLSID id;
  
      res = COM_OpenKeyForCLSID(clsidOld, NULL, KEY_READ | KEY_WRITE, &hkey);
      if (FAILED(res))
          goto done;
  
      if (IsEqualGUID( clsidOld, clsidNew ))
      {
         if (!RegQueryValueW(hkey, wszAutoTreatAs, auto_treat_as, &auto_treat_as_size) &&
             CLSIDFromString(auto_treat_as, &id) == S_OK)
         {
             if (RegSetValueW(hkey, wszTreatAs, REG_SZ, auto_treat_as, sizeof(auto_treat_as)))
             {
                 res = REGDB_E_WRITEREGDB;
                 goto done;
             }
         }
         else
         {
             if(RegDeleteKeyW(hkey, wszTreatAs))
                 res = REGDB_E_WRITEREGDB;
             goto done;
         }
      }
      else
      {
          if(IsEqualGUID(clsidNew, &CLSID_NULL)){
             RegDeleteKeyW(hkey, wszTreatAs);
          }else{
              if(!StringFromGUID2(clsidNew, szClsidNew, ARRAY_SIZE(szClsidNew))){
                  WARN("StringFromGUID2 failed\n");
                  res = E_FAIL;
                  goto done;
              }
  
              if(RegSetValueW(hkey, wszTreatAs, REG_SZ, szClsidNew, sizeof(szClsidNew)) != ERROR_SUCCESS){
                  WARN("RegSetValue failed\n");
                  res = REGDB_E_WRITEREGDB;
                  goto done;
              }
          }
      }
  
  done:
      if (hkey) RegCloseKey(hkey);
      return res;
  }
  
  /******************************************************************************
   *              CoGetTreatAsClass        [OLE32.@]
   *
   * Gets the TreatAs value of a class.
   *
   * PARAMS
   *  clsidOld [I] Class to get the TreatAs value of.
   *  clsidNew [I] The class the clsidOld should be treated as.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: HRESULT code.
   *
   * SEE ALSO
   *  CoSetTreatAsClass
   */
  HRESULT WINAPI CoGetTreatAsClass(REFCLSID clsidOld, LPCLSID clsidNew)
  {
      static const WCHAR wszTreatAs[] = {'T','r','e','a','t','A','s',0};
      HKEY hkey = NULL;
      WCHAR szClsidNew[CHARS_IN_GUID];
      HRESULT res = S_OK;
      LONG len = sizeof(szClsidNew);
  
      TRACE("(%s,%p)\n", debugstr_guid(clsidOld), clsidNew);
  
      if (!clsidOld || !clsidNew)
          return E_INVALIDARG;
  
      *clsidNew = *clsidOld; /* copy over old value */
  
      res = COM_OpenKeyForCLSID(clsidOld, wszTreatAs, KEY_READ, &hkey);
      if (FAILED(res))
      {
          res = S_FALSE;
          goto done;
      }
      if (RegQueryValueW(hkey, NULL, szClsidNew, &len))
      {
          res = S_FALSE;
  	goto done;
      }
      res = CLSIDFromString(szClsidNew,clsidNew);
      if (FAILED(res))
          ERR("Failed CLSIDFromStringA(%s), hres 0x%08x\n", debugstr_w(szClsidNew), res);
  done:
      if (hkey) RegCloseKey(hkey);
      return res;
  }
  
  /******************************************************************************
   *		CoGetCurrentProcess	[OLE32.@]
   *
   * Gets the current process ID.
   *
   * RETURNS
   *  The current process ID.
   *
   * NOTES
   *   Is DWORD really the correct return type for this function?
   */
  DWORD WINAPI CoGetCurrentProcess(void)
  {
  	return GetCurrentProcessId();
  }
  
  /***********************************************************************
   *              CoGetCurrentLogicalThreadId        [OLE32.@]
   */
  HRESULT WINAPI CoGetCurrentLogicalThreadId(GUID *id)
  {
      TRACE("(%p)\n", id);
  
      if (!id)
          return E_INVALIDARG;
  
      *id = COM_CurrentCausalityId();
      return S_OK;
  }
  
  /******************************************************************************
   *		CoRegisterMessageFilter	[OLE32.@]
   *
   * Registers a message filter.
   *
   * PARAMS
   *  lpMessageFilter [I] Pointer to interface.
   *  lplpMessageFilter [O] Indirect pointer to prior instance if non-NULL.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: HRESULT code.
   *
   * NOTES
   *  Both lpMessageFilter and lplpMessageFilter are optional. Passing in a NULL
   *  lpMessageFilter removes the message filter.
   *
   *  If lplpMessageFilter is not NULL the previous message filter will be
   *  returned in the memory pointer to this parameter and the caller is
   *  responsible for releasing the object.
   *
   *  The current thread be in an apartment otherwise the function will crash.
   */
  HRESULT WINAPI CoRegisterMessageFilter(
      LPMESSAGEFILTER lpMessageFilter,
      LPMESSAGEFILTER *lplpMessageFilter)
  {
      struct apartment *apt;
      IMessageFilter *lpOldMessageFilter;
  
      TRACE("(%p, %p)\n", lpMessageFilter, lplpMessageFilter);
  
      apt = COM_CurrentApt();
  
      /* can't set a message filter in a multi-threaded apartment */
      if (!apt || apt->multi_threaded)
      {
          WARN("can't set message filter in MTA or uninitialized apt\n");
          return CO_E_NOT_SUPPORTED;
      }
  
      if (lpMessageFilter)
          IMessageFilter_AddRef(lpMessageFilter);
  
      EnterCriticalSection(&apt->cs);
  
      lpOldMessageFilter = apt->filter;
      apt->filter = lpMessageFilter;
  
      LeaveCriticalSection(&apt->cs);
  
      if (lplpMessageFilter)
          *lplpMessageFilter = lpOldMessageFilter;
      else if (lpOldMessageFilter)
          IMessageFilter_Release(lpOldMessageFilter);
  
      return S_OK;
  }
  
  /***********************************************************************
   *           CoIsOle1Class [OLE32.@]
   *
   * Determines whether the specified class an OLE v1 class.
   *
   * PARAMS
   *  clsid [I] Class to test.
   *
   * RETURNS
   *  TRUE if the class is an OLE v1 class, or FALSE otherwise.
   */
  BOOL WINAPI CoIsOle1Class(REFCLSID clsid)
  {
    FIXME("%s\n", debugstr_guid(clsid));
    return FALSE;
  }
  
  /***********************************************************************
   *           IsEqualGUID [OLE32.@]
   *
   * Compares two Unique Identifiers.
   *
   * PARAMS
   *  rguid1 [I] The first GUID to compare.
   *  rguid2 [I] The other GUID to compare.
   *
   * RETURNS
   *	TRUE if equal
   */
  #undef IsEqualGUID
  BOOL WINAPI IsEqualGUID(
       REFGUID rguid1,
       REFGUID rguid2)
  {
      return !memcmp(rguid1,rguid2,sizeof(GUID));
  }
  
  /***********************************************************************
   *           CoInitializeSecurity [OLE32.@]
   */
  HRESULT WINAPI CoInitializeSecurity(PSECURITY_DESCRIPTOR pSecDesc, LONG cAuthSvc,
                                      SOLE_AUTHENTICATION_SERVICE* asAuthSvc,
                                      void* pReserved1, DWORD dwAuthnLevel,
                                      DWORD dwImpLevel, void* pReserved2,
                                      DWORD dwCapabilities, void* pReserved3)
  {
    FIXME("(%p,%d,%p,%p,%d,%d,%p,%d,%p) - stub!\n", pSecDesc, cAuthSvc,
          asAuthSvc, pReserved1, dwAuthnLevel, dwImpLevel, pReserved2,
          dwCapabilities, pReserved3);
    return S_OK;
  }
  
  /***********************************************************************
   *           CoSuspendClassObjects [OLE32.@]
   *
   * Suspends all registered class objects to prevent further requests coming in
   * for those objects.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: HRESULT code.
   */
  HRESULT WINAPI CoSuspendClassObjects(void)
  {
      FIXME("\n");
      return S_OK;
  }
  
  /***********************************************************************
   *           CoAddRefServerProcess [OLE32.@]
   *
   * Helper function for incrementing the reference count of a local-server
   * process.
   *
   * RETURNS
   *  New reference count.
   *
   * SEE ALSO
   *  CoReleaseServerProcess().
   */
  ULONG WINAPI CoAddRefServerProcess(void)
  {
      ULONG refs;
  
      TRACE("\n");
  
      EnterCriticalSection(&csRegisteredClassList);
      refs = ++s_COMServerProcessReferences;
      LeaveCriticalSection(&csRegisteredClassList);
  
      TRACE("refs before: %d\n", refs - 1);
  
      return refs;
  }
  
  /***********************************************************************
   *           CoReleaseServerProcess [OLE32.@]
   *
   * Helper function for decrementing the reference count of a local-server
   * process.
   *
   * RETURNS
   *  New reference count.
   *
   * NOTES
   *  When reference count reaches 0, this function suspends all registered
   *  classes so no new connections are accepted.
   *
   * SEE ALSO
   *  CoAddRefServerProcess(), CoSuspendClassObjects().
   */
  ULONG WINAPI CoReleaseServerProcess(void)
  {
      ULONG refs;
  
      TRACE("\n");
  
      EnterCriticalSection(&csRegisteredClassList);
  
      refs = --s_COMServerProcessReferences;
      /* FIXME: if (!refs) COM_SuspendClassObjects(); */
  
      LeaveCriticalSection(&csRegisteredClassList);
  
      TRACE("refs after: %d\n", refs);
  
      return refs;
  }
  
  /***********************************************************************
   *           CoIsHandlerConnected [OLE32.@]
   *
   * Determines whether a proxy is connected to a remote stub.
   *
   * PARAMS
   *  pUnk [I] Pointer to object that may or may not be connected.
   *
   * RETURNS
   *  TRUE if pUnk is not a proxy or if pUnk is connected to a remote stub, or
   *  FALSE otherwise.
   */
  BOOL WINAPI CoIsHandlerConnected(IUnknown *pUnk)
  {
      FIXME("%p\n", pUnk);
  
      return TRUE;
  }
  
  /***********************************************************************
   *           CoAllowSetForegroundWindow [OLE32.@]
   *
   */
  HRESULT WINAPI CoAllowSetForegroundWindow(IUnknown *pUnk, void *pvReserved)
  {
      FIXME("(%p, %p): stub\n", pUnk, pvReserved);
      return S_OK;
  }
   
  /***********************************************************************
   *           CoQueryProxyBlanket [OLE32.@]
   *
   * Retrieves the security settings being used by a proxy.
   *
   * PARAMS
   *  pProxy        [I] Pointer to the proxy object.
   *  pAuthnSvc     [O] The type of authentication service.
   *  pAuthzSvc     [O] The type of authorization service.
   *  ppServerPrincName [O] Optional. The server prinicple name.
   *  pAuthnLevel   [O] The authentication level.
   *  pImpLevel     [O] The impersonation level.
   *  ppAuthInfo    [O] Information specific to the authorization/authentication service.
   *  pCapabilities [O] Flags affecting the security behaviour.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: HRESULT code.
   *
   * SEE ALSO
   *  CoCopyProxy, CoSetProxyBlanket.
   */
  HRESULT WINAPI CoQueryProxyBlanket(IUnknown *pProxy, DWORD *pAuthnSvc,
      DWORD *pAuthzSvc, OLECHAR **ppServerPrincName, DWORD *pAuthnLevel,
      DWORD *pImpLevel, void **ppAuthInfo, DWORD *pCapabilities)
  {
      IClientSecurity *pCliSec;
      HRESULT hr;
  
      TRACE("%p\n", pProxy);
  
      hr = IUnknown_QueryInterface(pProxy, &IID_IClientSecurity, (void **)&pCliSec);
      if (SUCCEEDED(hr))
      {
          hr = IClientSecurity_QueryBlanket(pCliSec, pProxy, pAuthnSvc,
                                            pAuthzSvc, ppServerPrincName,
                                            pAuthnLevel, pImpLevel, ppAuthInfo,
                                            pCapabilities);
          IClientSecurity_Release(pCliSec);
      }
  
      if (FAILED(hr)) ERR("-- failed with 0x%08x\n", hr);
      return hr;
  }
  
  /***********************************************************************
   *           CoSetProxyBlanket [OLE32.@]
   *
   * Sets the security settings for a proxy.
   *
   * PARAMS
   *  pProxy       [I] Pointer to the proxy object.
   *  AuthnSvc     [I] The type of authentication service.
   *  AuthzSvc     [I] The type of authorization service.
   *  pServerPrincName [I] The server prinicple name.
   *  AuthnLevel   [I] The authentication level.
   *  ImpLevel     [I] The impersonation level.
   *  pAuthInfo    [I] Information specific to the authorization/authentication service.
   *  Capabilities [I] Flags affecting the security behaviour.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: HRESULT code.
   *
   * SEE ALSO
   *  CoQueryProxyBlanket, CoCopyProxy.
   */
  HRESULT WINAPI CoSetProxyBlanket(IUnknown *pProxy, DWORD AuthnSvc,
      DWORD AuthzSvc, OLECHAR *pServerPrincName, DWORD AuthnLevel,
      DWORD ImpLevel, void *pAuthInfo, DWORD Capabilities)
  {
      IClientSecurity *pCliSec;
      HRESULT hr;
  
      TRACE("%p\n", pProxy);
  
      hr = IUnknown_QueryInterface(pProxy, &IID_IClientSecurity, (void **)&pCliSec);
      if (SUCCEEDED(hr))
      {
          hr = IClientSecurity_SetBlanket(pCliSec, pProxy, AuthnSvc,
                                          AuthzSvc, pServerPrincName,
                                          AuthnLevel, ImpLevel, pAuthInfo,
                                          Capabilities);
          IClientSecurity_Release(pCliSec);
      }
  
      if (FAILED(hr)) ERR("-- failed with 0x%08x\n", hr);
      return hr;
  }
  
  /***********************************************************************
   *           CoCopyProxy [OLE32.@]
   *
   * Copies a proxy.
   *
   * PARAMS
   *  pProxy [I] Pointer to the proxy object.
   *  ppCopy [O] Copy of the proxy.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: HRESULT code.
   *
   * SEE ALSO
   *  CoQueryProxyBlanket, CoSetProxyBlanket.
   */
  HRESULT WINAPI CoCopyProxy(IUnknown *pProxy, IUnknown **ppCopy)
  {
      IClientSecurity *pCliSec;
      HRESULT hr;
  
      TRACE("%p\n", pProxy);
  
      hr = IUnknown_QueryInterface(pProxy, &IID_IClientSecurity, (void **)&pCliSec);
      if (SUCCEEDED(hr))
      {
          hr = IClientSecurity_CopyProxy(pCliSec, pProxy, ppCopy);
          IClientSecurity_Release(pCliSec);
      }
  
      if (FAILED(hr)) ERR("-- failed with 0x%08x\n", hr);
      return hr;
  }
  
  
  /***********************************************************************
   *           CoGetCallContext [OLE32.@]
   *
   * Gets the context of the currently executing server call in the current
   * thread.
   *
   * PARAMS
   *  riid [I] Context interface to return.
   *  ppv  [O] Pointer to memory that will receive the context on return.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: HRESULT code.
   */
  HRESULT WINAPI CoGetCallContext(REFIID riid, void **ppv)
  {
      struct oletls *info = COM_CurrentInfo();
  
      TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
  
      if (!info)
          return E_OUTOFMEMORY;
  
      if (!info->call_state)
          return RPC_E_CALL_COMPLETE;
  
      return IUnknown_QueryInterface(info->call_state, riid, ppv);
  }
  
  /***********************************************************************
   *           CoSwitchCallContext [OLE32.@]
   *
   * Switches the context of the currently executing server call in the current
   * thread.
   *
   * PARAMS
   *  pObject     [I] Pointer to new context object
   *  ppOldObject [O] Pointer to memory that will receive old context object pointer
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: HRESULT code.
   */
  HRESULT WINAPI CoSwitchCallContext(IUnknown *pObject, IUnknown **ppOldObject)
  {
      struct oletls *info = COM_CurrentInfo();
  
      TRACE("(%p, %p)\n", pObject, ppOldObject);
  
      if (!info)
          return E_OUTOFMEMORY;
  
      *ppOldObject = info->call_state;
      info->call_state = pObject; /* CoSwitchCallContext does not addref nor release objects */
  
      return S_OK;
  }
  
  /***********************************************************************
   *           CoQueryClientBlanket [OLE32.@]
   *
   * Retrieves the authentication information about the client of the currently
   * executing server call in the current thread.
   *
   * PARAMS
   *  pAuthnSvc     [O] Optional. The type of authentication service.
   *  pAuthzSvc     [O] Optional. The type of authorization service.
   *  pServerPrincName [O] Optional. The server prinicple name.
   *  pAuthnLevel   [O] Optional. The authentication level.
   *  pImpLevel     [O] Optional. The impersonation level.
   *  pPrivs        [O] Optional. Information about the privileges of the client.
   *  pCapabilities [IO] Optional. Flags affecting the security behaviour.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: HRESULT code.
   *
   * SEE ALSO
   *  CoImpersonateClient, CoRevertToSelf, CoGetCallContext.
   */
  HRESULT WINAPI CoQueryClientBlanket(
      DWORD *pAuthnSvc,
      DWORD *pAuthzSvc,
      OLECHAR **pServerPrincName,
      DWORD *pAuthnLevel,
      DWORD *pImpLevel,
      RPC_AUTHZ_HANDLE *pPrivs,
      DWORD *pCapabilities)
  {
      IServerSecurity *pSrvSec;
      HRESULT hr;
  
      TRACE("(%p, %p, %p, %p, %p, %p, %p)\n",
          pAuthnSvc, pAuthzSvc, pServerPrincName, pAuthnLevel, pImpLevel,
          pPrivs, pCapabilities);
  
      hr = CoGetCallContext(&IID_IServerSecurity, (void **)&pSrvSec);
      if (SUCCEEDED(hr))
      {
          hr = IServerSecurity_QueryBlanket(
              pSrvSec, pAuthnSvc, pAuthzSvc, pServerPrincName, pAuthnLevel,
              pImpLevel, pPrivs, pCapabilities);
          IServerSecurity_Release(pSrvSec);
      }
  
      return hr;
  }
  
  /***********************************************************************
   *           CoImpersonateClient [OLE32.@]
   *
   * Impersonates the client of the currently executing server call in the
   * current thread.
   *
   * PARAMS
   *  None.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: HRESULT code.
   *
   * NOTES
   *  If this function fails then the current thread will not be impersonating
   *  the client and all actions will take place on behalf of the server.
   *  Therefore, it is important to check the return value from this function.
   *
   * SEE ALSO
   *  CoRevertToSelf, CoQueryClientBlanket, CoGetCallContext.
   */
  HRESULT WINAPI CoImpersonateClient(void)
  {
      IServerSecurity *pSrvSec;
      HRESULT hr;
  
      TRACE("\n");
  
      hr = CoGetCallContext(&IID_IServerSecurity, (void **)&pSrvSec);
      if (SUCCEEDED(hr))
      {
          hr = IServerSecurity_ImpersonateClient(pSrvSec);
          IServerSecurity_Release(pSrvSec);
      }
  
      return hr;
  }
  
  /***********************************************************************
   *           CoRevertToSelf [OLE32.@]
   *
   * Ends the impersonation of the client of the currently executing server
   * call in the current thread.
   *
   * PARAMS
   *  None.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: HRESULT code.
   *
   * SEE ALSO
   *  CoImpersonateClient, CoQueryClientBlanket, CoGetCallContext.
   */
  HRESULT WINAPI CoRevertToSelf(void)
  {
      IServerSecurity *pSrvSec;
      HRESULT hr;
  
      TRACE("\n");
  
      hr = CoGetCallContext(&IID_IServerSecurity, (void **)&pSrvSec);
      if (SUCCEEDED(hr))
      {
          hr = IServerSecurity_RevertToSelf(pSrvSec);
          IServerSecurity_Release(pSrvSec);
      }
  
      return hr;
  }
  
  static BOOL COM_PeekMessage(struct apartment *apt, MSG *msg)
  {
      /* first try to retrieve messages for incoming COM calls to the apartment window */
      return (apt->win && PeekMessageW(msg, apt->win, 0, 0, PM_REMOVE|PM_NOYIELD)) ||
             /* next retrieve other messages necessary for the app to remain responsive */
             PeekMessageW(msg, NULL, WM_DDE_FIRST, WM_DDE_LAST, PM_REMOVE|PM_NOYIELD) ||
             PeekMessageW(msg, NULL, 0, 0, PM_QS_PAINT|PM_QS_SENDMESSAGE|PM_REMOVE|PM_NOYIELD);
  }
  
  /***********************************************************************
   *           CoWaitForMultipleHandles [OLE32.@]
   *
   * Waits for one or more handles to become signaled.
   *
   * PARAMS
   *  dwFlags   [I] Flags. See notes.
   *  dwTimeout [I] Timeout in milliseconds.
   *  cHandles  [I] Number of handles pointed to by pHandles.
   *  pHandles  [I] Handles to wait for.
   *  lpdwindex [O] Index of handle that was signaled.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: RPC_S_CALLPENDING on timeout.
   *
   * NOTES
   *
   * The dwFlags parameter can be zero or more of the following:
   *| COWAIT_WAITALL - Wait for all of the handles to become signaled.
   *| COWAIT_ALERTABLE - Allows a queued APC to run during the wait.
   *
   * SEE ALSO
   *  MsgWaitForMultipleObjects, WaitForMultipleObjects.
   */
  HRESULT WINAPI CoWaitForMultipleHandles(DWORD dwFlags, DWORD dwTimeout,
      ULONG cHandles, LPHANDLE pHandles, LPDWORD lpdwindex)
  {
      HRESULT hr = S_OK;
      DWORD start_time = GetTickCount();
      APARTMENT *apt = COM_CurrentApt();
      BOOL message_loop = apt && !apt->multi_threaded;
      BOOL check_apc = (dwFlags & COWAIT_ALERTABLE) != 0;
      BOOL post_quit = FALSE;
      UINT exit_code;
  
      TRACE("(0x%08x, 0x%08x, %d, %p, %p)\n", dwFlags, dwTimeout, cHandles,
          pHandles, lpdwindex);
  
      if (!lpdwindex)
          return E_INVALIDARG;
  
      *lpdwindex = 0;
  
      if (!pHandles)
          return E_INVALIDARG;
  
      if (!cHandles)
          return RPC_E_NO_SYNC;
  
      while (TRUE)
      {
          DWORD now = GetTickCount();
          DWORD res;
  
          if (now - start_time > dwTimeout)
          {
              hr = RPC_S_CALLPENDING;
              break;
          }
  
          if (message_loop)
          {
              DWORD wait_flags = ((dwFlags & COWAIT_WAITALL) ? MWMO_WAITALL : 0) |
                      ((dwFlags & COWAIT_ALERTABLE ) ? MWMO_ALERTABLE : 0);
  
              TRACE("waiting for rpc completion or window message\n");
  
              res = WAIT_TIMEOUT;
  
              if (check_apc)
              {
                  res = WaitForMultipleObjectsEx(cHandles, pHandles,
                      (dwFlags & COWAIT_WAITALL) != 0, 0, TRUE);
                  check_apc = FALSE;
              }
  
              if (res == WAIT_TIMEOUT)
                  res = MsgWaitForMultipleObjectsEx(cHandles, pHandles,
                      (dwTimeout == INFINITE) ? INFINITE : start_time + dwTimeout - now,
                      QS_SENDMESSAGE | QS_ALLPOSTMESSAGE | QS_PAINT, wait_flags);
  
              if (res == WAIT_OBJECT_0 + cHandles)  /* messages available */
              {
                  MSG msg;
                  int count = 0;
  
                  /* call message filter */
  
                  if (COM_CurrentApt()->filter)
                  {
                      PENDINGTYPE pendingtype =
                          COM_CurrentInfo()->pending_call_count_server ?
                              PENDINGTYPE_NESTED : PENDINGTYPE_TOPLEVEL;
                      DWORD be_handled = IMessageFilter_MessagePending(
                          COM_CurrentApt()->filter, 0 /* FIXME */,
                          now - start_time, pendingtype);
                      TRACE("IMessageFilter_MessagePending returned %d\n", be_handled);
                      switch (be_handled)
                      {
                      case PENDINGMSG_CANCELCALL:
                          WARN("call canceled\n");
                          hr = RPC_E_CALL_CANCELED;
                          break;
                      case PENDINGMSG_WAITNOPROCESS:
                      case PENDINGMSG_WAITDEFPROCESS:
                      default:
                          /* FIXME: MSDN is very vague about the difference
                           * between WAITNOPROCESS and WAITDEFPROCESS - there
                           * appears to be none, so it is possibly a left-over
                           * from the 16-bit world. */
                          break;
                      }
                  }
  
                  if (!apt->win)
                  {
                      /* If window is NULL on apartment, peek at messages so that it will not trigger
                       * MsgWaitForMultipleObjects next time. */
                      PeekMessageW(NULL, NULL, 0, 0, PM_QS_POSTMESSAGE | PM_NOREMOVE | PM_NOYIELD);
                  }
                  /* some apps (e.g. Visio 2010) don't handle WM_PAINT properly and loop forever,
                   * so after processing 100 messages we go back to checking the wait handles */
                  while (count++ < 100 && COM_PeekMessage(apt, &msg))
                  {
                      if (msg.message == WM_QUIT)
                      {
                          TRACE("received WM_QUIT message\n");
                          post_quit = TRUE;
                          exit_code = msg.wParam;
                      }
                      else
                      {
                          TRACE("received message whilst waiting for RPC: 0x%04x\n", msg.message);
                          TranslateMessage(&msg);
                          DispatchMessageW(&msg);
                      }
                  }
                  continue;
              }
          }
          else
          {
              TRACE("waiting for rpc completion\n");
  
              res = WaitForMultipleObjectsEx(cHandles, pHandles, (dwFlags & COWAIT_WAITALL) != 0,
                  (dwTimeout == INFINITE) ? INFINITE : start_time + dwTimeout - now,
                  (dwFlags & COWAIT_ALERTABLE) != 0);
          }
  
          switch (res)
          {
          case WAIT_TIMEOUT:
              hr = RPC_S_CALLPENDING;
              break;
          case WAIT_FAILED:
              hr = HRESULT_FROM_WIN32( GetLastError() );
              break;
          default:
              *lpdwindex = res;
              break;
          }
          break;
      }
      if (post_quit) PostQuitMessage(exit_code);
      TRACE("-- 0x%08x\n", hr);
      return hr;
  }
  
  
  /***********************************************************************
   *           CoGetObject [OLE32.@]
   *
   * Gets the object named by converting the name to a moniker and binding to it.
   *
   * PARAMS
   *  pszName      [I] String representing the object.
   *  pBindOptions [I] Parameters affecting the binding to the named object.
   *  riid         [I] Interface to bind to on the objecct.
   *  ppv          [O] On output, the interface riid of the object represented
   *                   by pszName.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: HRESULT code.
   *
   * SEE ALSO
   *  MkParseDisplayName.
   */
  HRESULT WINAPI CoGetObject(LPCWSTR pszName, BIND_OPTS *pBindOptions,
      REFIID riid, void **ppv)
  {
      IBindCtx *pbc;
      HRESULT hr;
  
      *ppv = NULL;
  
      hr = CreateBindCtx(0, &pbc);
      if (SUCCEEDED(hr))
      {
          if (pBindOptions)
              hr = IBindCtx_SetBindOptions(pbc, pBindOptions);
  
          if (SUCCEEDED(hr))
          {
              ULONG chEaten;
              IMoniker *pmk;
  
              hr = MkParseDisplayName(pbc, pszName, &chEaten, &pmk);
              if (SUCCEEDED(hr))
              {
                  hr = IMoniker_BindToObject(pmk, pbc, NULL, riid, ppv);
                  IMoniker_Release(pmk);
              }
          }
  
          IBindCtx_Release(pbc);
      }
      return hr;
  }
  
  /***********************************************************************
   *           CoRegisterChannelHook [OLE32.@]
   *
   * Registers a process-wide hook that is called during ORPC calls.
   *
   * PARAMS
   *  guidExtension [I] GUID of the channel hook to register.
   *  pChannelHook  [I] Channel hook object to register.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: HRESULT code.
   */
  HRESULT WINAPI CoRegisterChannelHook(REFGUID guidExtension, IChannelHook *pChannelHook)
  {
      TRACE("(%s, %p)\n", debugstr_guid(guidExtension), pChannelHook);
  
      return RPC_RegisterChannelHook(guidExtension, pChannelHook);
  }
  
  typedef struct Context
  {
      IComThreadingInfo IComThreadingInfo_iface;
      IContextCallback IContextCallback_iface;
      IObjContext IObjContext_iface;
      LONG refs;
  } Context;
  
  static inline Context *impl_from_IComThreadingInfo( IComThreadingInfo *iface )
  {
          return CONTAINING_RECORD(iface, Context, IComThreadingInfo_iface);
  }
  
  static inline Context *impl_from_IContextCallback( IContextCallback *iface )
  {
          return CONTAINING_RECORD(iface, Context, IContextCallback_iface);
  }
  
  static inline Context *impl_from_IObjContext( IObjContext *iface )
  {
          return CONTAINING_RECORD(iface, Context, IObjContext_iface);
  }
  
  static HRESULT Context_QueryInterface(Context *iface, REFIID riid, LPVOID *ppv)
  {
      *ppv = NULL;
  
      if (IsEqualIID(riid, &IID_IComThreadingInfo) ||
          IsEqualIID(riid, &IID_IUnknown))
      {
          *ppv = &iface->IComThreadingInfo_iface;
      }
      else if (IsEqualIID(riid, &IID_IContextCallback))
      {
          *ppv = &iface->IContextCallback_iface;
      }
      else if (IsEqualIID(riid, &IID_IObjContext))
      {
          *ppv = &iface->IObjContext_iface;
      }
  
      if (*ppv)
      {
          IUnknown_AddRef((IUnknown*)*ppv);
          return S_OK;
      }
  
      FIXME("interface not implemented %s\n", debugstr_guid(riid));
      return E_NOINTERFACE;
  }
  
  static ULONG Context_AddRef(Context *This)
  {
      return InterlockedIncrement(&This->refs);
  }
  
  static ULONG Context_Release(Context *This)
  {
      /* Context instance is initially created with CoGetContextToken() with refcount set to 0,
         releasing context while refcount is at 0 destroys it. */
      if (!This->refs)
      {
          HeapFree(GetProcessHeap(), 0, This);
          return 0;
      }
  
      return InterlockedDecrement(&This->refs);
  }
  
  static HRESULT WINAPI Context_CTI_QueryInterface(IComThreadingInfo *iface, REFIID riid, LPVOID *ppv)
  {
      Context *This = impl_from_IComThreadingInfo(iface);
      return Context_QueryInterface(This, riid, ppv);
  }
  
  static ULONG WINAPI Context_CTI_AddRef(IComThreadingInfo *iface)
  {
      Context *This = impl_from_IComThreadingInfo(iface);
      return Context_AddRef(This);
  }
  
  static ULONG WINAPI Context_CTI_Release(IComThreadingInfo *iface)
  {
      Context *This = impl_from_IComThreadingInfo(iface);
      return Context_Release(This);
  }
  
  static HRESULT WINAPI Context_CTI_GetCurrentApartmentType(IComThreadingInfo *iface, APTTYPE *apttype)
  {
      APTTYPEQUALIFIER qualifier;
  
      TRACE("(%p)\n", apttype);
  
      return CoGetApartmentType(apttype, &qualifier);
  }
  
  static HRESULT WINAPI Context_CTI_GetCurrentThreadType(IComThreadingInfo *iface, THDTYPE *thdtype)
  {
      APTTYPEQUALIFIER qualifier;
      APTTYPE apttype;
      HRESULT hr;
  
      hr = CoGetApartmentType(&apttype, &qualifier);
      if (FAILED(hr))
          return hr;
  
      TRACE("(%p)\n", thdtype);
  
      switch (apttype)
      {
      case APTTYPE_STA:
      case APTTYPE_MAINSTA:
          *thdtype = THDTYPE_PROCESSMESSAGES;
          break;
      default:
          *thdtype = THDTYPE_BLOCKMESSAGES;
          break;
      }
      return S_OK;
  }
  
  static HRESULT WINAPI Context_CTI_GetCurrentLogicalThreadId(IComThreadingInfo *iface, GUID *logical_thread_id)
  {
      TRACE("(%p)\n", logical_thread_id);
      return CoGetCurrentLogicalThreadId(logical_thread_id);
  }
  
  static HRESULT WINAPI Context_CTI_SetCurrentLogicalThreadId(IComThreadingInfo *iface, REFGUID logical_thread_id)
  {
      FIXME("(%s): stub\n", debugstr_guid(logical_thread_id));
      return E_NOTIMPL;
  }
  
  static const IComThreadingInfoVtbl Context_Threading_Vtbl =
  {
      Context_CTI_QueryInterface,
      Context_CTI_AddRef,
      Context_CTI_Release,
      Context_CTI_GetCurrentApartmentType,
      Context_CTI_GetCurrentThreadType,
      Context_CTI_GetCurrentLogicalThreadId,
      Context_CTI_SetCurrentLogicalThreadId
  };
  
  static HRESULT WINAPI Context_CC_QueryInterface(IContextCallback *iface, REFIID riid, LPVOID *ppv)
  {
      Context *This = impl_from_IContextCallback(iface);
      return Context_QueryInterface(This, riid, ppv);
  }
  
  static ULONG WINAPI Context_CC_AddRef(IContextCallback *iface)
  {
      Context *This = impl_from_IContextCallback(iface);
      return Context_AddRef(This);
  }
  
  static ULONG WINAPI Context_CC_Release(IContextCallback *iface)
  {
      Context *This = impl_from_IContextCallback(iface);
      return Context_Release(This);
  }
  
  static HRESULT WINAPI Context_CC_ContextCallback(IContextCallback *iface, PFNCONTEXTCALL pCallback,
                              ComCallData *param, REFIID riid, int method, IUnknown *punk)
  {
      Context *This = impl_from_IContextCallback(iface);
  
      FIXME("(%p/%p)->(%p, %p, %s, %d, %p)\n", This, iface, pCallback, param, debugstr_guid(riid), method, punk);
      return E_NOTIMPL;
  }
  
  static const IContextCallbackVtbl Context_Callback_Vtbl =
  {
      Context_CC_QueryInterface,
      Context_CC_AddRef,
      Context_CC_Release,
      Context_CC_ContextCallback
  };
  
  static HRESULT WINAPI Context_OC_QueryInterface(IObjContext *iface, REFIID riid, LPVOID *ppv)
  {
      Context *This = impl_from_IObjContext(iface);
      return Context_QueryInterface(This, riid, ppv);
  }
  
  static ULONG WINAPI Context_OC_AddRef(IObjContext *iface)
  {
      Context *This = impl_from_IObjContext(iface);
      return Context_AddRef(This);
  }
  
  static ULONG WINAPI Context_OC_Release(IObjContext *iface)
  {
      Context *This = impl_from_IObjContext(iface);
      return Context_Release(This);
  }
  
  static HRESULT WINAPI Context_OC_SetProperty(IObjContext *iface, REFGUID propid, CPFLAGS flags, IUnknown *punk)
  {
      Context *This = impl_from_IObjContext(iface);
  
      FIXME("(%p/%p)->(%s, %x, %p)\n", This, iface, debugstr_guid(propid), flags, punk);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI Context_OC_RemoveProperty(IObjContext *iface, REFGUID propid)
  {
      Context *This = impl_from_IObjContext(iface);
  
      FIXME("(%p/%p)->(%s)\n", This, iface, debugstr_guid(propid));
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI Context_OC_GetProperty(IObjContext *iface, REFGUID propid, CPFLAGS *flags, IUnknown **punk)
  {
      Context *This = impl_from_IObjContext(iface);
  
      FIXME("(%p/%p)->(%s, %p, %p)\n", This, iface, debugstr_guid(propid), flags, punk);
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI Context_OC_EnumContextProps(IObjContext *iface, IEnumContextProps **props)
  {
      Context *This = impl_from_IObjContext(iface);
  
      FIXME("(%p/%p)->(%p)\n", This, iface, props);
      return E_NOTIMPL;
  }
  
  static void WINAPI Context_OC_Reserved1(IObjContext *iface)
  {
      Context *This = impl_from_IObjContext(iface);
      FIXME("(%p/%p)\n", This, iface);
  }
  
  static void WINAPI Context_OC_Reserved2(IObjContext *iface)
  {
      Context *This = impl_from_IObjContext(iface);
      FIXME("(%p/%p)\n", This, iface);
  }
  
  static void WINAPI Context_OC_Reserved3(IObjContext *iface)
  {
      Context *This = impl_from_IObjContext(iface);
      FIXME("(%p/%p)\n", This, iface);
  }
  
  static void WINAPI Context_OC_Reserved4(IObjContext *iface)
  {
      Context *This = impl_from_IObjContext(iface);
      FIXME("(%p/%p)\n", This, iface);
  }
  
  static void WINAPI Context_OC_Reserved5(IObjContext *iface)
  {
      Context *This = impl_from_IObjContext(iface);
      FIXME("(%p/%p)\n", This, iface);
  }
  
  static void WINAPI Context_OC_Reserved6(IObjContext *iface)
  {
      Context *This = impl_from_IObjContext(iface);
      FIXME("(%p/%p)\n", This, iface);
  }
  
  static void WINAPI Context_OC_Reserved7(IObjContext *iface)
  {
      Context *This = impl_from_IObjContext(iface);
      FIXME("(%p/%p)\n", This, iface);
  }
  
  static const IObjContextVtbl Context_Object_Vtbl =
  {
      Context_OC_QueryInterface,
      Context_OC_AddRef,
      Context_OC_Release,
      Context_OC_SetProperty,
      Context_OC_RemoveProperty,
      Context_OC_GetProperty,
      Context_OC_EnumContextProps,
      Context_OC_Reserved1,
      Context_OC_Reserved2,
      Context_OC_Reserved3,
      Context_OC_Reserved4,
      Context_OC_Reserved5,
      Context_OC_Reserved6,
      Context_OC_Reserved7
  };
  
  /***********************************************************************
   *           CoGetObjectContext [OLE32.@]
   *
   * Retrieves an object associated with the current context (i.e. apartment).
   *
   * PARAMS
   *  riid [I] ID of the interface of the object to retrieve.
   *  ppv  [O] Address where object will be stored on return.
   *
   * RETURNS
   *  Success: S_OK.
   *  Failure: HRESULT code.
   */
  HRESULT WINAPI CoGetObjectContext(REFIID riid, void **ppv)
  {
      IObjContext *context;
      HRESULT hr;
  
      TRACE("(%s, %p)\n", debugstr_guid(riid), ppv);
  
      *ppv = NULL;
      hr = CoGetContextToken((ULONG_PTR*)&context);
      if (FAILED(hr))
          return hr;
  
      return IObjContext_QueryInterface(context, riid, ppv);
  }
  
  /***********************************************************************
   *           CoGetContextToken [OLE32.@]
   */
  HRESULT WINAPI CoGetContextToken( ULONG_PTR *token )
  {
      struct oletls *info = COM_CurrentInfo();
      APARTMENT *apt;
  
      TRACE("(%p)\n", token);
  
      if (!info)
          return E_OUTOFMEMORY;
  
      if (!(apt = apartment_get_current_or_mta()))
      {
          ERR("apartment not initialised\n");
          return CO_E_NOTINITIALIZED;
      }
      apartment_release(apt);
  
      if (!token)
          return E_POINTER;
  
      if (!info->context_token)
      {
          Context *context;
  
          context = HeapAlloc(GetProcessHeap(), 0, sizeof(*context));
          if (!context)
              return E_OUTOFMEMORY;
  
          context->IComThreadingInfo_iface.lpVtbl = &Context_Threading_Vtbl;
          context->IContextCallback_iface.lpVtbl = &Context_Callback_Vtbl;
          context->IObjContext_iface.lpVtbl = &Context_Object_Vtbl;
          /* Context token does not take a reference, it's always zero until the
             interface is explicitly requested with CoGetObjectContext(). */
          context->refs = 0;
  
          info->context_token = &context->IObjContext_iface;
      }
  
      *token = (ULONG_PTR)info->context_token;
      TRACE("context_token=%p\n", info->context_token);
  
      return S_OK;
  }
  
  /***********************************************************************
   *           CoGetDefaultContext [OLE32.@]
   */
  HRESULT WINAPI CoGetDefaultContext(APTTYPE type, REFIID riid, LPVOID *ppv)
  {
      FIXME("%d %s %p stub\n", type, debugstr_guid(riid), ppv);
      return E_NOINTERFACE;
  }
  
  HRESULT Handler_DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
  {
      static const WCHAR wszInprocHandler32[] = {'I','n','p','r','o','c','H','a','n','d','l','e','r','3','2',0};
      HKEY hkey;
      HRESULT hres;
  
      hres = COM_OpenKeyForCLSID(rclsid, wszInprocHandler32, KEY_READ, &hkey);
      if (SUCCEEDED(hres))
      {
          struct class_reg_data regdata;
          WCHAR dllpath[MAX_PATH+1];
  
          regdata.u.hkey = hkey;
          regdata.hkey = TRUE;
  
          if (get_object_dll_path(&regdata, dllpath, ARRAY_SIZE(dllpath)))
          {
              static const WCHAR wszOle32[] = {'o','l','e','3','2','.','d','l','l',0};
              if (!strcmpiW(dllpath, wszOle32))
              {
                  RegCloseKey(hkey);
                  return HandlerCF_Create(rclsid, riid, ppv);
              }
          }
          else
              WARN("not creating object for inproc handler path %s\n", debugstr_w(dllpath));
          RegCloseKey(hkey);
      }
  
      return CLASS_E_CLASSNOTAVAILABLE;
  }
  
  /***********************************************************************
   *           CoGetApartmentType [OLE32.@]
   */
  HRESULT WINAPI CoGetApartmentType(APTTYPE *type, APTTYPEQUALIFIER *qualifier)
  {
      struct oletls *info = COM_CurrentInfo();
      APARTMENT *apt;
  
      TRACE("(%p, %p)\n", type, qualifier);
  
      if (!type || !qualifier)
          return E_INVALIDARG;
  
      if (!info)
          return E_OUTOFMEMORY;
  
      if (!info->apt)
          *type = APTTYPE_CURRENT;
      else if (info->apt->multi_threaded)
          *type = APTTYPE_MTA;
      else if (info->apt->main)
          *type = APTTYPE_MAINSTA;
      else
          *type = APTTYPE_STA;
  
      *qualifier = APTTYPEQUALIFIER_NONE;
  
      if (!info->apt && (apt = apartment_find_mta()))
      {
          apartment_release(apt);
          *type = APTTYPE_MTA;
          *qualifier = APTTYPEQUALIFIER_IMPLICIT_MTA;
      }
  
      return info->apt ? S_OK : CO_E_NOTINITIALIZED;
  }
  
  /***********************************************************************
   *           CoRegisterSurrogate [OLE32.@]
   */
  HRESULT WINAPI CoRegisterSurrogate(ISurrogate *surrogate)
  {
      FIXME("(%p): stub\n", surrogate);
  
      return E_NOTIMPL;
  }
  
  /***********************************************************************
   *           CoRegisterSurrogateEx [OLE32.@]
   */
  HRESULT WINAPI CoRegisterSurrogateEx(REFGUID guid, void *reserved)
  {
      FIXME("(%s %p): stub\n", debugstr_guid(guid), reserved);
  
      return E_NOTIMPL;
  }
  
  typedef struct {
      IGlobalOptions IGlobalOptions_iface;
      LONG ref;
  } GlobalOptions;
  
  static inline GlobalOptions *impl_from_IGlobalOptions(IGlobalOptions *iface)
  {
      return CONTAINING_RECORD(iface, GlobalOptions, IGlobalOptions_iface);
  }
  
  static HRESULT WINAPI GlobalOptions_QueryInterface(IGlobalOptions *iface, REFIID riid, void **ppv)
  {
      GlobalOptions *This = impl_from_IGlobalOptions(iface);
  
      TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
  
      if (IsEqualGUID(&IID_IGlobalOptions, riid) || IsEqualGUID(&IID_IUnknown, riid))
      {
          *ppv = iface;
      }
      else
      {
          *ppv = NULL;
          return E_NOINTERFACE;
      }
  
      IUnknown_AddRef((IUnknown*)*ppv);
      return S_OK;
  }
  
  static ULONG WINAPI GlobalOptions_AddRef(IGlobalOptions *iface)
  {
      GlobalOptions *This = impl_from_IGlobalOptions(iface);
      LONG ref = InterlockedIncrement(&This->ref);
  
      TRACE("(%p) ref=%d\n", This, ref);
  
      return ref;
  }
  
  static ULONG WINAPI GlobalOptions_Release(IGlobalOptions *iface)
  {
      GlobalOptions *This = impl_from_IGlobalOptions(iface);
      LONG ref = InterlockedDecrement(&This->ref);
  
      TRACE("(%p) ref=%d\n", This, ref);
  
      if (!ref)
          heap_free(This);
  
      return ref;
  }
  
  static HRESULT WINAPI GlobalOptions_Set(IGlobalOptions *iface, GLOBALOPT_PROPERTIES property, ULONG_PTR value)
  {
      GlobalOptions *This = impl_from_IGlobalOptions(iface);
      FIXME("(%p)->(%u %lx)\n", This, property, value);
      return S_OK;
  }
  
  static HRESULT WINAPI GlobalOptions_Query(IGlobalOptions *iface, GLOBALOPT_PROPERTIES property, ULONG_PTR *value)
  {
      GlobalOptions *This = impl_from_IGlobalOptions(iface);
      FIXME("(%p)->(%u %p)\n", This, property, value);
      return E_NOTIMPL;
  }
  
  static const IGlobalOptionsVtbl GlobalOptionsVtbl = {
      GlobalOptions_QueryInterface,
      GlobalOptions_AddRef,
      GlobalOptions_Release,
      GlobalOptions_Set,
      GlobalOptions_Query
  };
  
  HRESULT WINAPI GlobalOptions_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID riid, void **ppv)
  {
      GlobalOptions *global_options;
      HRESULT hres;
  
      TRACE("(%p %s %p)\n", outer, debugstr_guid(riid), ppv);
  
      if (outer)
          return E_INVALIDARG;
  
      global_options = heap_alloc(sizeof(*global_options));
      if (!global_options)
          return E_OUTOFMEMORY;
      global_options->IGlobalOptions_iface.lpVtbl = &GlobalOptionsVtbl;
      global_options->ref = 1;
  
      hres = IGlobalOptions_QueryInterface(&global_options->IGlobalOptions_iface, riid, ppv);
      IGlobalOptions_Release(&global_options->IGlobalOptions_iface);
      return hres;
  }
  
  /***********************************************************************
   *		DllMain (OLE32.@)
   */
  BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID reserved)
  {
      TRACE("%p 0x%x %p\n", hinstDLL, fdwReason, reserved);
  
      switch(fdwReason) {
      case DLL_PROCESS_ATTACH:
          hProxyDll = hinstDLL;
  	break;
  
      case DLL_PROCESS_DETACH:
          if (reserved) break;
          release_std_git();
          if(apt_win_class)
              UnregisterClassW( (const WCHAR*)MAKEINTATOM(apt_win_class), hProxyDll );
          RPC_UnregisterAllChannelHooks();
          COMPOBJ_DllList_Free();
          DeleteCriticalSection(&csRegisteredClassList);
          DeleteCriticalSection(&csApartment);
  	break;
  
      case DLL_THREAD_DETACH:
          COM_TlsDestroy();
          break;
      }
      return TRUE;
  }
  
  /***********************************************************************
   *		DllRegisterServer (OLE32.@)
   */
  HRESULT WINAPI DllRegisterServer(void)
  {
      return OLE32_DllRegisterServer();
  }
  
  /***********************************************************************
   *		DllUnregisterServer (OLE32.@)
   */
  HRESULT WINAPI DllUnregisterServer(void)
  {
      return OLE32_DllUnregisterServer();
  }
  