initial apartment patch

Ove Kaaven ovek at arcticnet.no
Thu May 22 21:46:45 CDT 2003


This patch implements some support for tracking apartments, and installs
the structure types I've used for handling COM object export and import
between them. Some of the shortcuts I've taken may be evident, such as
the imported objects themselves handling IRemUnknown, when it's the
apartment object that should do it through some midl-generated
IRemUnknown proxy/stub code. I can try to change that, but thought I
should get this patch out quickly first, since someone else on
wine-devel have requested the feature this patch implements (and I
incorporated his idea of using a TEB field, I originally used a TLS
entry).

Log:
Ove Kaaven <ovek at transgaming.com>
Preliminary support for COM apartments.

Index: dlls/ole32/compobj.c
===================================================================
RCS file: /home/wine/wine/dlls/ole32/compobj.c,v
retrieving revision 1.64
diff -u -r1.64 compobj.c
--- dlls/ole32/compobj.c	3 Aug 2002 00:17:10 -0000	1.64
+++ dlls/ole32/compobj.c	23 May 2003 02:29:16 -0000
@@ -62,45 +62,9 @@
 static void COM_RevokeAllClasses();
 static void COM_ExternalLockFreeList();
 
-/*****************************************************************************
- * Appartment management stuff
- *
- * NOTE:
- *  per Thread values are stored in the TEB on offset 0xF80
- *
- *  see www.microsoft.com/msj/1099/bugslayer/bugslayer1099.htm
- *
- */
-
-typedef struct {
-	unsigned char		threadingModell;	// we use the COINIT flags
-        unsigned long		threadID;
-	long			AppartmentLockCount;
-} OleAppartmentData;
-
-typedef struct {
-	OleAppartmentData 	*AppartmentData;
-} OleThreadData;
-
-/* not jet used
-static CRITICAL_SECTION csAppartmentData = CRITICAL_SECTION_INIT("csAppartmentData");
-*/
-/*
- * the first STA created in a process is the main STA
- */
-
-/* not jet used
-static OleAppartmentData * mainSTA;
-*/
-
-/*
- * a Process can only have one MTA
- */
-
-/* not jet used
-static OleAppartmentData * processMTA;
-*/
 
+APARTMENT MTA, *apts;
+static CRITICAL_SECTION csApartment = CRITICAL_SECTION_INIT("csApartment");
 
 /*
  * This lock count counts the number of times CoInitialize is called. It is
@@ -150,21 +114,114 @@
 static CRITICAL_SECTION csOpenDllList = CRITICAL_SECTION_INIT("csOpenDllList");
 static OpenDll *openDllList = NULL; /* linked list of open dlls */
 
+static const char aptWinClass[] = "WINE_OLE32_APT_CLASS";
+static LRESULT CALLBACK COM_AptWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
 static void COMPOBJ_DLLList_Add(HANDLE hLibrary);
 static void COMPOBJ_DllList_FreeUnused(int Timeout);
 
 
 /******************************************************************************
- * Initialize/Uninitialize critical sections.
+ * Initialize/Unitialize threading stuff.
  */
 void COMPOBJ_InitProcess( void )
 {
+    WNDCLASSA wclass;
+
+    memset(&wclass, 0, sizeof(wclass));
+    wclass.lpfnWndProc = &COM_AptWndProc;
+    wclass.hInstance = OLE32_hInstance;
+    wclass.lpszClassName = aptWinClass;
+    RegisterClassA(&wclass);
 }
 
 void COMPOBJ_UninitProcess( void )
 {
+    UnregisterClassA(aptWinClass, OLE32_hInstance);
+}
+
+/******************************************************************************
+ * Manage apartments.
+ */
+static void COM_InitMTA(void)
+{
+    /* FIXME: how does windoze create OXIDs?
+     * this method will only work for local RPC */
+    MTA.oxid = ((OXID)GetCurrentProcessId() << 32);
+    InitializeCriticalSection(&MTA.cs);
+}
+
+static void COM_UninitMTA(void)
+{
+    DeleteCriticalSection(&MTA.cs);
+    MTA.oxid = 0;
+}
+
+static APARTMENT* COM_CreateApartment(DWORD model)
+{
+    APARTMENT *apt;
+
+    apt = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(APARTMENT));
+    apt->model = model;
+    apt->tid = GetCurrentThreadId();
+    DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
+                    GetCurrentProcess(), &apt->thread,
+                    THREAD_ALL_ACCESS, FALSE, 0);
+    if (model & COINIT_APARTMENTTHREADED) {
+      /* FIXME: how does windoze create OXIDs? */
+      apt->oxid = MTA.oxid | GetCurrentThreadId();
+      apt->win = CreateWindowA(aptWinClass, NULL, 0,
+			       0, 0, 0, 0,
+			       0, 0, OLE32_hInstance, NULL);
+      InitializeCriticalSection(&apt->cs);
+    }
+    else {
+      apt->parent = &MTA;
+      apt->oxid = MTA.oxid;
+    }
+    EnterCriticalSection(&csApartment);
+    if (apts) apts->prev = apt;
+    apt->next = apts;
+    apts = apt;
+    LeaveCriticalSection(&csApartment);
+    NtCurrentTeb()->ErrorInfo = apt;
+    return apt;
+}
+
+static void COM_DestroyApartment(APARTMENT *apt)
+{
+    EnterCriticalSection(&csApartment);
+    if (apt->prev) apt->prev->next = apt->next;
+    if (apt->next) apt->next->prev = apt->prev;
+    if (apts == apt) apts = apt->next;
+    apt->prev = NULL; apt->next = NULL;
+    LeaveCriticalSection(&csApartment);
+    if (apt->model & COINIT_APARTMENTTHREADED) {
+      if (apt->win) DestroyWindow(apt->win);
+      DeleteCriticalSection(&apt->cs);
+    }
+    CloseHandle(apt->thread);
+    HeapFree(GetProcessHeap(), 0, apt);
 }
 
+HWND COM_GetApartmentWin(OXID oxid)
+{
+    APARTMENT *apt;
+    HWND win = 0;
+
+    EnterCriticalSection(&csApartment);
+    apt = apts;
+    while (apt && apt->oxid != oxid) apt = apt->next;
+    if (apt) win = apt->win;
+    LeaveCriticalSection(&csApartment);
+    return win;
+}
+
+static LRESULT CALLBACK COM_AptWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+  return DefWindowProcA(hWnd, msg, wParam, lParam);
+} 
+
 /*****************************************************************************
  * This section contains OpenDllList implemantation
  */
@@ -282,12 +339,6 @@
  *  S_FALSE            if this function was called already.
  *  RPC_E_CHANGED_MODE if a previous call to CoInitialize specified another
  *                      threading model.
- *
- * BUGS
- * Only the single threaded model is supported. As a result RPC_E_CHANGED_MODE
- * is never returned.
- *
- * See the windows documentation for more details.
  */
 HRESULT WINAPI CoInitializeEx(
 	LPVOID lpReserved,	/* [in] pointer to win32 malloc interface
@@ -295,7 +346,8 @@
 	DWORD dwCoInit		/* [in] A value from COINIT specifies the threading model */
 )
 {
-  HRESULT hr;
+  HRESULT hr = S_OK;
+  APARTMENT *apt;
 
   TRACE("(%p, %x)\n", lpReserved, (int)dwCoInit);
 
@@ -304,14 +356,9 @@
     ERR("(%p, %x) - Bad parameter passed-in %p, must be an old Windows Application\n", lpReserved, (int)dwCoInit, lpReserved);
   }
 
-  /*
-   * Check for unsupported features.
-   */
-  if (dwCoInit!=COINIT_APARTMENTTHREADED)
-  {
-    FIXME(":(%p,%x): unsupported flag %x\n", lpReserved, (int)dwCoInit, (int)dwCoInit);
-    /* Hope for the best and continue anyway */
-  }
+  apt = NtCurrentTeb()->ErrorInfo;
+  if (apt && dwCoInit != apt->model) return RPC_E_CHANGED_MODE;
+  hr = apt ? S_FALSE : S_OK;
 
   /*
    * Check the lock count. If this is the first time going through the initialize
@@ -326,13 +373,15 @@
      */
     TRACE("() - Initializing the COM libraries\n");
 
+    COM_InitMTA();
 
     RunningObjectTableImpl_Initialize();
-
-    hr = S_OK;
   }
-  else
-    hr = S_FALSE;
+
+  if (!apt) apt = COM_CreateApartment(dwCoInit);
+
+  InterlockedIncrement(&apt->inits);
+  if (hr == S_OK) NtCurrentTeb()->ErrorInfo = apt;
 
   return hr;
 }
@@ -347,8 +396,18 @@
 void WINAPI CoUninitialize(void)
 {
   LONG lCOMRefCnt;
+  APARTMENT *apt;
+
   TRACE("()\n");
 
+  apt = NtCurrentTeb()->ErrorInfo;
+  if (!apt) return;
+  if (InterlockedDecrement(&apt->inits)==0) {
+    NtCurrentTeb()->ErrorInfo = NULL;
+    COM_DestroyApartment(apt);
+    apt = NULL;
+  }
+
   /*
    * Decrease the reference count.
    * If we are back to 0 locks on the COM library, make sure we free
@@ -378,6 +437,7 @@
      */
     COM_ExternalLockFreeList();
 
+    COM_UninitMTA();
   }
   else if (lCOMRefCnt<1) {
     ERR( "CoUninitialize() - not CoInitialized.\n" );
Index: dlls/ole32/compobj_private.h
===================================================================
RCS file: /home/wine/wine/dlls/ole32/compobj_private.h,v
retrieving revision 1.7
diff -u -r1.7 compobj_private.h
--- dlls/ole32/compobj_private.h	13 May 2003 00:41:58 -0000	1.7
+++ dlls/ole32/compobj_private.h	23 May 2003 02:29:17 -0000
@@ -4,6 +4,7 @@
  * Copyright 1999 Francis Beaudet
  * Copyright 1999 Sylvain St-Germain
  * Copyright 2002 Marcus Meissner
+ * Copyright 2003 Ove Kåven, TransGaming Technologies
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -26,6 +27,74 @@
 /* All private prototype functions used by OLE will be added to this header file */
 
 #include "wtypes.h"
+#include "dcom.h"
+#include "thread.h"
+
+/* exported interface */
+typedef struct tagXIF {
+  struct tagXIF *next;
+  LPVOID iface;            /* interface pointer */
+  IID iid;                 /* interface ID */
+  IPID ipid;               /* exported interface ID */
+  LPRPCSTUBBUFFER stub;    /* interface stub */
+  DWORD refs;              /* external reference count */
+  HRESULT hres;            /* result of stub creation attempt */
+} XIF;
+
+/* exported object */
+typedef struct tagXOBJECT {
+  ICOM_VTABLE(IRpcStubBuffer) *lpVtbl;
+  struct tagAPARTMENT *parent;
+  struct tagXOBJECT *next;
+  LPUNKNOWN obj;           /* object identity (IUnknown) */
+  OID oid;                 /* object ID */
+  DWORD ifc;               /* interface ID counter */
+  XIF *ifaces;             /* exported interfaces */
+  DWORD refs;              /* external reference count */
+} XOBJECT;
+
+/* imported interface */
+typedef struct tagIIF {
+  struct tagIIF *next;
+  LPVOID iface;            /* interface pointer */
+  IID iid;                 /* interface ID */
+  IPID ipid;               /* imported interface ID */
+  LPRPCPROXYBUFFER proxy;  /* interface proxy */
+  DWORD refs;              /* imported (public) references */
+  HRESULT hres;            /* result of proxy creation attempt */
+} IIF;
+
+/* imported object */
+typedef struct tagIOBJECT {
+  ICOM_VTABLE(IRemUnknown) *lpVtbl;
+  struct tagAPARTMENT *parent;
+  struct tagIOBJECT *next;
+  LPRPCCHANNELBUFFER chan; /* channel to object */
+  OXID oxid;               /* object exported ID */
+  OID oid;                 /* object ID */
+  IPID ipid;               /* first imported interface ID */
+  IIF *ifaces;             /* imported interfaces */
+  DWORD refs;              /* proxy reference count */
+} IOBJECT;
+
+/* apartment */
+typedef struct tagAPARTMENT {
+  struct tagAPARTMENT *next, *prev, *parent;
+  DWORD model;             /* threading model */
+  DWORD inits;             /* CoInitialize count */
+  DWORD tid;               /* thread id */
+  HANDLE thread;           /* thread handle */
+  OXID oxid;               /* object exporter ID */
+  OID oidc;                /* object ID counter */
+  HWND win;                /* message window */
+  CRITICAL_SECTION cs;     /* thread safety */
+  LPMESSAGEFILTER filter;  /* message filter */
+  XOBJECT *objs;           /* exported objects */
+  IOBJECT *proxies;        /* imported objects */
+  LPVOID ErrorInfo;        /* thread error info */
+} APARTMENT;
+
+extern APARTMENT MTA, *apts;
 
 extern void* StdGlobalInterfaceTable_Construct();
 extern void  StdGlobalInterfaceTable_Destroy(void* self);
@@ -120,5 +189,26 @@
 int WINAPI FileMonikerImpl_DecomposePath(LPCOLESTR str, LPOLESTR** stringTable);
 
 HRESULT WINAPI __CLSIDFromStringA(LPCSTR idstr, CLSID *id);
+
+/*
+ * Per-thread values are stored in the TEB on offset 0xF80,
+ * see http://www.microsoft.com/msj/1099/bugslayer/bugslayer1099.htm
+ */
+static inline APARTMENT* COM_CurrentInfo(void) WINE_UNUSED;
+static inline APARTMENT* COM_CurrentInfo(void)
+{
+  APARTMENT* apt = NtCurrentTeb()->ErrorInfo;
+  return apt;
+}
+static inline APARTMENT* COM_CurrentApt(void) WINE_UNUSED;
+static inline APARTMENT* COM_CurrentApt(void)
+{
+  APARTMENT* apt = COM_CurrentInfo();
+  if (apt && apt->parent) apt = apt->parent;
+  return apt;
+}
+
+/* compobj.c */
+HWND COM_GetApartmentWin(OXID oxid);
 
 #endif /* __WINE_OLE_COMPOBJ_H */
Index: dlls/ole32/errorinfo.c
===================================================================
RCS file: /home/wine/wine/dlls/ole32/errorinfo.c,v
retrieving revision 1.13
diff -u -r1.13 errorinfo.c
--- dlls/ole32/errorinfo.c	5 Dec 2002 20:33:08 -0000	1.13
+++ dlls/ole32/errorinfo.c	23 May 2003 02:29:17 -0000
@@ -483,13 +483,13 @@
  */
 HRESULT WINAPI GetErrorInfo(ULONG dwReserved, IErrorInfo **pperrinfo)
 {
-	TRACE("(%ld, %p, %p): stub:\n", dwReserved, pperrinfo, NtCurrentTeb()->ErrorInfo);
+	TRACE("(%ld, %p, %p): stub:\n", dwReserved, pperrinfo, COM_CurrentInfo()->ErrorInfo);
 
 	if(! pperrinfo ) return E_INVALIDARG;
-	if(!(*pperrinfo = (IErrorInfo*)(NtCurrentTeb()->ErrorInfo))) return S_FALSE;
+	if(!(*pperrinfo = (IErrorInfo*)(COM_CurrentInfo()->ErrorInfo))) return S_FALSE;
 
 	/* clear thread error state */
-	NtCurrentTeb()->ErrorInfo = NULL;
+	COM_CurrentInfo()->ErrorInfo = NULL;
 	return S_OK;
 }
 
@@ -502,11 +502,11 @@
 	TRACE("(%ld, %p): stub:\n", dwReserved, perrinfo);
 
 	/* release old errorinfo */
-	pei = (IErrorInfo*)NtCurrentTeb()->ErrorInfo;
+	pei = (IErrorInfo*)COM_CurrentInfo()->ErrorInfo;
 	if(pei) IErrorInfo_Release(pei);
 
 	/* set to new value */
-	NtCurrentTeb()->ErrorInfo = perrinfo;
+	COM_CurrentInfo()->ErrorInfo = perrinfo;
 	if(perrinfo) IErrorInfo_AddRef(perrinfo);
 	return S_OK;
 }






More information about the wine-patches mailing list