crypt32: Always set *pcbStructInfo.
[wine] / dlls / ole32 / stubmanager.c
index a7e5523..9918fbc 100644 (file)
@@ -20,7 +20,7 @@
  *
  * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 #define COBJMACROS
@@ -35,9 +35,8 @@
 #include "winbase.h"
 #include "winuser.h"
 #include "objbase.h"
-#include "ole2.h"
-#include "ole2ver.h"
 #include "rpc.h"
+
 #include "wine/debug.h"
 #include "compobj_private.h"
 
@@ -49,7 +48,7 @@ static struct ifstub *stub_manager_ipid_to_ifstub(struct stub_manager *m, const
 /* creates a new stub manager and adds it into the apartment. caller must
  * release stub manager when it is no longer required. the apartment and
  * external refs together take one implicit ref */
-struct stub_manager *new_stub_manager(APARTMENT *apt, IUnknown *object, MSHLFLAGS mshlflags)
+struct stub_manager *new_stub_manager(APARTMENT *apt, IUnknown *object)
 {
     struct stub_manager *sm;
 
@@ -59,7 +58,10 @@ struct stub_manager *new_stub_manager(APARTMENT *apt, IUnknown *object, MSHLFLAG
     if (!sm) return NULL;
 
     list_init(&sm->ifstubs);
+
     InitializeCriticalSection(&sm->lock);
+    DEBUG_SET_CRITSEC_NAME(&sm->lock, "stub_manager");
+
     IUnknown_AddRef(object);
     sm->object = object;
     sm->apt    = apt;
@@ -67,24 +69,35 @@ struct stub_manager *new_stub_manager(APARTMENT *apt, IUnknown *object, MSHLFLAG
     /* start off with 2 references because the stub is in the apartment
      * and the caller will also hold a reference */
     sm->refs   = 2;
-
-    /* yes, that's right, this starts at zero. that's zero EXTERNAL
-     * refs, ie nobody has unmarshalled anything yet. we can't have
+    sm->weakrefs = 0;
+
+    sm->oxid_info.dwPid = GetCurrentProcessId();
+    sm->oxid_info.dwTid = GetCurrentThreadId();
+    /*
+     * FIXME: this is a hack for marshalling IRemUnknown. In real
+     * DCOM, the IPID of the IRemUnknown interface is generated like
+     * any other and passed to the OXID resolver which then returns it
+     * when queried. We don't have an OXID resolver yet so instead we
+     * use a magic IPID reserved for IRemUnknown.
+     */
+    sm->oxid_info.ipidRemUnknown.Data1 = 0xffffffff;
+    sm->oxid_info.ipidRemUnknown.Data2 = 0xffff;
+    sm->oxid_info.ipidRemUnknown.Data3 = 0xffff;
+    assert(sizeof(sm->oxid_info.ipidRemUnknown.Data4) == sizeof(apt->oxid));
+    memcpy(sm->oxid_info.ipidRemUnknown.Data4, &apt->oxid, sizeof(OXID));
+    sm->oxid_info.dwAuthnHint = RPC_C_AUTHN_LEVEL_NONE;
+    sm->oxid_info.psa = NULL /* FIXME */;
+
+    /* Yes, that's right, this starts at zero. that's zero EXTERNAL
+     * refs, i.e., nobody has unmarshalled anything yet. We can't have
      * negative refs because the stub manager cannot be explicitly
      * killed, it has to die by somebody unmarshalling then releasing
      * the marshalled ifptr.
      */
     sm->extrefs = 0;
 
-    if (mshlflags & MSHLFLAGS_TABLESTRONG)
-        sm->state = STUBSTATE_TABLE_STRONG;
-    else if (mshlflags & MSHLFLAGS_TABLEWEAK)
-        sm->state = STUBSTATE_TABLE_WEAK_UNMARSHALED;
-    else
-        sm->state = STUBSTATE_NORMAL_MARSHALED;
-    
     EnterCriticalSection(&apt->cs);
-    sm->oid    = apt->oidc++;
+    sm->oid = apt->oidc++;
     list_add_head(&apt->stubmgrs, &sm->entry);
     LeaveCriticalSection(&apt->cs);
 
@@ -93,15 +106,13 @@ struct stub_manager *new_stub_manager(APARTMENT *apt, IUnknown *object, MSHLFLAG
     return sm;
 }
 
-/* m->apt->cs must be held on entry to this function */
+/* caller must remove stub manager from apartment prior to calling this function */
 static void stub_manager_delete(struct stub_manager *m)
 {
     struct list *cursor;
 
     TRACE("destroying %p (oid=%s)\n", m, wine_dbgstr_longlong(m->oid));
 
-    list_remove(&m->entry);
-
     /* release every ifstub */
     while ((cursor = list_head(&m->ifstubs)))
     {
@@ -109,8 +120,10 @@ static void stub_manager_delete(struct stub_manager *m)
         stub_manager_delete_ifstub(m, ifstub);
     }
 
+    CoTaskMemFree(m->oxid_info.psa);
     IUnknown_Release(m->object);
 
+    DEBUG_CLEAR_CRITSEC_NAME(&m->lock);
     DeleteCriticalSection(&m->lock);
 
     HeapFree(GetProcessHeap(), 0, m);
@@ -210,7 +223,7 @@ ULONG stub_manager_int_addref(struct stub_manager *This)
     refs = ++This->refs;
     LeaveCriticalSection(&This->apt->cs);
 
-    TRACE("before %ld\n", refs - 1);
+    TRACE("before %d\n", refs - 1);
 
     return refs;
 }
@@ -224,35 +237,44 @@ ULONG stub_manager_int_release(struct stub_manager *This)
     EnterCriticalSection(&apt->cs);
     refs = --This->refs;
 
-    TRACE("after %ld\n", refs);
+    TRACE("after %d\n", refs);
 
+    /* remove from apartment so no other thread can access it... */
     if (!refs)
-        stub_manager_delete(This);
+        list_remove(&This->entry);
+
     LeaveCriticalSection(&apt->cs);
 
+    /* ... so now we can delete it without being inside the apartment critsec */
+    if (!refs)
+        stub_manager_delete(This);
+
     return refs;
 }
 
 /* add some external references (ie from a client that unmarshaled an ifptr) */
-ULONG stub_manager_ext_addref(struct stub_manager *m, ULONG refs)
+ULONG stub_manager_ext_addref(struct stub_manager *m, ULONG refs, BOOL tableweak)
 {
     ULONG rc;
 
     EnterCriticalSection(&m->lock);
-    
+
     /* make sure we don't overflow extrefs */
     refs = min(refs, (ULONG_MAX-1 - m->extrefs));
     rc = (m->extrefs += refs);
 
+    if (tableweak)
+        rc += ++m->weakrefs;
+
     LeaveCriticalSection(&m->lock);
     
-    TRACE("added %lu refs to %p (oid %s), rc is now %lu\n", refs, m, wine_dbgstr_longlong(m->oid), rc);
+    TRACE("added %u refs to %p (oid %s), rc is now %u\n", refs, m, wine_dbgstr_longlong(m->oid), rc);
 
     return rc;
 }
 
 /* remove some external references */
-ULONG stub_manager_ext_release(struct stub_manager *m, ULONG refs)
+ULONG stub_manager_ext_release(struct stub_manager *m, ULONG refs, BOOL tableweak, BOOL last_unlock_releases)
 {
     ULONG rc;
 
@@ -262,11 +284,14 @@ ULONG stub_manager_ext_release(struct stub_manager *m, ULONG refs)
     refs = min(refs, m->extrefs);
     rc = (m->extrefs -= refs);
 
+    if (tableweak)
+        rc += --m->weakrefs;
+
     LeaveCriticalSection(&m->lock);
     
-    TRACE("removed %lu refs from %p (oid %s), rc is now %lu\n", refs, m, wine_dbgstr_longlong(m->oid), rc);
+    TRACE("removed %u refs from %p (oid %s), rc is now %u\n", refs, m, wine_dbgstr_longlong(m->oid), rc);
 
-    if (rc == 0)
+    if (rc == 0 && last_unlock_releases)
         stub_manager_int_release(m);
 
     return rc;
@@ -293,6 +318,25 @@ static struct ifstub *stub_manager_ipid_to_ifstub(struct stub_manager *m, const
     return result;
 }
 
+struct ifstub *stub_manager_find_ifstub(struct stub_manager *m, REFIID iid, MSHLFLAGS flags)
+{
+    struct ifstub  *result = NULL;
+    struct ifstub  *ifstub;
+
+    EnterCriticalSection(&m->lock);
+    LIST_FOR_EACH_ENTRY( ifstub, &m->ifstubs, struct ifstub, entry )
+    {
+        if (IsEqualIID(iid, &ifstub->iid) && (ifstub->flags == flags))
+        {
+            result = ifstub;
+            break;
+        }
+    }
+    LeaveCriticalSection(&m->lock);
+
+    return result;
+}
+
 /* gets the stub manager associated with an ipid - caller must have
  * a reference to the apartment while a reference to the stub manager is held.
  * it must also call release on the stub manager when it is no longer needed */
@@ -327,12 +371,12 @@ HRESULT ipid_to_stub_manager(const IPID *ipid, APARTMENT **stub_apt, struct stub
 {
     /* FIXME: hack for IRemUnknown */
     if (ipid->Data2 == 0xffff)
-        *stub_apt = apartment_findfromoxid(*(OXID *)ipid->Data4, TRUE);
+        *stub_apt = apartment_findfromoxid(*(const OXID *)ipid->Data4, TRUE);
     else
         *stub_apt = apartment_findfromtid(ipid->Data2);
     if (!*stub_apt)
     {
-        ERR("Couldn't find apartment corresponding to TID 0x%04x\n", ipid->Data2);
+        TRACE("Couldn't find apartment corresponding to TID 0x%04x\n", ipid->Data2);
         return RPC_E_INVALID_OBJECT;
     }
     *stubmgr_ret = get_stub_manager_from_ipid(*stub_apt, ipid);
@@ -345,29 +389,41 @@ HRESULT ipid_to_stub_manager(const IPID *ipid, APARTMENT **stub_apt, struct stub
     return S_OK;
 }
 
-/* gets the apartment and IRpcStubBuffer from an object. the caller must
- * release the references to both objects */
-IRpcStubBuffer *ipid_to_apt_and_stubbuffer(const IPID *ipid, APARTMENT **stub_apt)
+/* gets the apartment, stub and channel of an object. the caller must
+ * release the references to all objects (except iface) if the function
+ * returned success, otherwise no references are returned. */
+HRESULT ipid_get_dispatch_params(const IPID *ipid, APARTMENT **stub_apt,
+                                 IRpcStubBuffer **stub, IRpcChannelBuffer **chan,
+                                 IID *iid, IUnknown **iface)
 {
-    IRpcStubBuffer *ret = NULL;
     struct stub_manager *stubmgr;
     struct ifstub *ifstub;
+    APARTMENT *apt;
     HRESULT hr;
 
-    *stub_apt = NULL;
-
-    hr = ipid_to_stub_manager(ipid, stub_apt, &stubmgr);
-    if (hr != S_OK) return NULL;
+    hr = ipid_to_stub_manager(ipid, &apt, &stubmgr);
+    if (hr != S_OK) return RPC_E_DISCONNECTED;
 
     ifstub = stub_manager_ipid_to_ifstub(stubmgr, ipid);
     if (ifstub)
-        ret = ifstub->stubbuffer;
-
-    if (ret) IRpcStubBuffer_AddRef(ret);
-
-    stub_manager_int_release(stubmgr);
+    {
+        *stub = ifstub->stubbuffer;
+        IRpcStubBuffer_AddRef(*stub);
+        *chan = ifstub->chan;
+        IRpcChannelBuffer_AddRef(*chan);
+        *stub_apt = apt;
+        *iid = ifstub->iid;
+        *iface = ifstub->iface;
 
-    return ret;
+        stub_manager_int_release(stubmgr);
+        return S_OK;
+    }
+    else
+    {
+        stub_manager_int_release(stubmgr);
+        apartment_release(apt);
+        return RPC_E_DISCONNECTED;
+    }
 }
 
 /* generates an ipid in the following format (similar to native version):
@@ -394,9 +450,10 @@ static inline HRESULT generate_ipid(struct stub_manager *m, IPID *ipid)
 }
 
 /* registers a new interface stub COM object with the stub manager and returns registration record */
-struct ifstub *stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *sb, IUnknown *iptr, REFIID iid)
+struct ifstub *stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *sb, IUnknown *iptr, REFIID iid, MSHLFLAGS flags)
 {
     struct ifstub *stub;
+    HRESULT hr;
 
     TRACE("oid=%s, stubbuffer=%p, iptr=%p, iid=%s\n",
           wine_dbgstr_longlong(m->oid), sb, iptr, debugstr_guid(iid));
@@ -404,29 +461,32 @@ struct ifstub *stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *s
     stub = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct ifstub));
     if (!stub) return NULL;
 
+    hr = RPC_CreateServerChannel(&stub->chan);
+    if (hr != S_OK)
+    {
+        HeapFree(GetProcessHeap(), 0, stub);
+        return NULL;
+    }
+
     stub->stubbuffer = sb;
-    IUnknown_AddRef(sb);
+    if (sb) IRpcStubBuffer_AddRef(sb);
 
-    /* no need to ref this, same object as sb */
+    IUnknown_AddRef(iptr);
     stub->iface = iptr;
-
+    stub->flags = flags;
     stub->iid = *iid;
 
-    /* FIXME: hack for IRemUnknown because we don't notify SCM of our IPID
-     * yet, so we need to use a well-known one */
-    if (IsEqualIID(iid, &IID_IRemUnknown))
-    {
-        stub->ipid.Data1 = 0xffffffff;
-        stub->ipid.Data2 = 0xffff;
-        stub->ipid.Data3 = 0xffff;
-        assert(sizeof(stub->ipid.Data4) == sizeof(m->apt->oxid));
-        memcpy(&stub->ipid.Data4, &m->apt->oxid, sizeof(OXID));
-    }
+    /* FIXME: find a cleaner way of identifying that we are creating an ifstub
+     * for the remunknown interface */
+    if (flags & MSHLFLAGSP_REMUNKNOWN)
+        stub->ipid = m->oxid_info.ipidRemUnknown;
     else
         generate_ipid(m, &stub->ipid);
 
     EnterCriticalSection(&m->lock);
     list_add_head(&m->ifstubs, &stub->entry);
+    /* every normal marshal is counted so we don't allow more than we should */
+    if (flags & MSHLFLAGS_NORMAL) m->norm_refs++;
     LeaveCriticalSection(&m->lock);
 
     TRACE("ifstub %p created with ipid %s\n", stub, debugstr_guid(&stub->ipid));
@@ -441,40 +501,38 @@ static void stub_manager_delete_ifstub(struct stub_manager *m, struct ifstub *if
     list_remove(&ifstub->entry);
 
     RPC_UnregisterInterface(&ifstub->iid);
-        
-    IUnknown_Release(ifstub->stubbuffer);
+
+    if (ifstub->stubbuffer) IUnknown_Release(ifstub->stubbuffer);
     IUnknown_Release(ifstub->iface);
+    IRpcChannelBuffer_Release(ifstub->chan);
 
     HeapFree(GetProcessHeap(), 0, ifstub);
 }
 
 /* returns TRUE if it is possible to unmarshal, FALSE otherwise. */
-BOOL stub_manager_notify_unmarshal(struct stub_manager *m)
+BOOL stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ipid)
 {
-    BOOL ret;
+    BOOL ret = TRUE;
+    struct ifstub *ifstub;
+
+    if (!(ifstub = stub_manager_ipid_to_ifstub(m, ipid)))
+    {
+        ERR("attempted unmarshal of unknown IPID %s\n", debugstr_guid(ipid));
+        return FALSE;
+    }
 
     EnterCriticalSection(&m->lock);
 
-    switch (m->state)
+    /* track normal marshals so we can enforce rules whilst in-process */
+    if (ifstub->flags & MSHLFLAGS_NORMAL)
     {
-    case STUBSTATE_TABLE_STRONG:
-    case STUBSTATE_TABLE_WEAK_MARSHALED:
-        /* no transition */
-        ret = TRUE;
-        break;
-    case STUBSTATE_TABLE_WEAK_UNMARSHALED:
-        m->state = STUBSTATE_TABLE_WEAK_MARSHALED;
-        ret = TRUE;
-        break;
-    case STUBSTATE_NORMAL_MARSHALED:
-        m->state = STUBSTATE_NORMAL_UNMARSHALED;
-        ret = TRUE;
-        break;
-    default:
-        WARN("object OID %s already unmarshaled\n",
-            wine_dbgstr_longlong(m->oid));
-        ret = FALSE;
-        break;
+        if (m->norm_refs)
+            m->norm_refs--;
+        else
+        {
+            ERR("attempted invalid normal unmarshal, norm_refs is zero\n");
+            ret = FALSE;
+        }
     }
 
     LeaveCriticalSection(&m->lock);
@@ -482,42 +540,30 @@ BOOL stub_manager_notify_unmarshal(struct stub_manager *m)
     return ret;
 }
 
-void stub_manager_release_marshal_data(struct stub_manager *m, ULONG refs)
+/* handles refcounting for CoReleaseMarshalData */
+void stub_manager_release_marshal_data(struct stub_manager *m, ULONG refs, const IPID *ipid, BOOL tableweak)
 {
-    EnterCriticalSection(&m->lock);
-
-    switch (m->state)
-    {
-    case STUBSTATE_NORMAL_MARSHALED:
-    case STUBSTATE_NORMAL_UNMARSHALED: /* FIXME: check this */
-        /* nothing to change */
-        break;
-    case STUBSTATE_TABLE_WEAK_UNMARSHALED:
-    case STUBSTATE_TABLE_STRONG:
+    struct ifstub *ifstub;
+    if (!(ifstub = stub_manager_ipid_to_ifstub(m, ipid)))
+        return;
+    if (ifstub->flags & MSHLFLAGS_TABLEWEAK)
+        refs = 0;
+    else if (ifstub->flags & MSHLFLAGS_TABLESTRONG)
         refs = 1;
-        break;
-    case STUBSTATE_TABLE_WEAK_MARSHALED:
-        refs = 0; /* like native */
-        break;
-    }
-
-    stub_manager_ext_release(m, refs);
 
-    LeaveCriticalSection(&m->lock);
+    stub_manager_ext_release(m, refs, tableweak, TRUE);
 }
 
 /* is an ifstub table marshaled? */
-BOOL stub_manager_is_table_marshaled(struct stub_manager *m)
+BOOL stub_manager_is_table_marshaled(struct stub_manager *m, const IPID *ipid)
 {
-    BOOL ret;
-
-    EnterCriticalSection(&m->lock);
-    ret = ((m->state == STUBSTATE_TABLE_STRONG) ||
-           (m->state == STUBSTATE_TABLE_WEAK_MARSHALED) ||
-           (m->state == STUBSTATE_TABLE_WEAK_UNMARSHALED));
-    LeaveCriticalSection(&m->lock);
-
-    return ret;
+    struct ifstub *ifstub = stub_manager_ipid_to_ifstub(m, ipid);
+    assert( ifstub );
+    
+    return ifstub->flags & (MSHLFLAGS_TABLESTRONG | MSHLFLAGS_TABLEWEAK);
 }
 
 
@@ -530,12 +576,10 @@ BOOL stub_manager_is_table_marshaled(struct stub_manager *m)
  * interacts with stub managers.
  */
 
-const IID IID_IRemUnknown = { 0x00000131, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46} };
-
 typedef struct rem_unknown
 {
     const IRemUnknownVtbl *lpVtbl;
-    ULONG refs;
+    LONG refs;
 } RemUnknown;
 
 static const IRemUnknownVtbl RemUnknown_Vtbl;
@@ -580,7 +624,7 @@ static ULONG WINAPI RemUnknown_AddRef(IRemUnknown *iface)
 
     refs = InterlockedIncrement(&This->refs);
 
-    TRACE("%p before: %ld\n", iface, refs-1);
+    TRACE("%p before: %d\n", iface, refs-1);
     return refs;
 }
 
@@ -593,7 +637,7 @@ static ULONG WINAPI RemUnknown_Release(IRemUnknown *iface)
     if (!refs)
         HeapFree(GetProcessHeap(), 0, This);
 
-    TRACE("%p after: %ld\n", iface, refs);
+    TRACE("%p after: %d\n", iface, refs);
     return refs;
 }
 
@@ -607,7 +651,7 @@ static HRESULT WINAPI RemUnknown_RemQueryInterface(IRemUnknown *iface,
     APARTMENT *apt;
     struct stub_manager *stubmgr;
 
-    TRACE("(%p)->(%s, %ld, %d, %p, %p)\n", iface, debugstr_guid(ripid), cRefs, cIids, iids, ppQIResults);
+    TRACE("(%p)->(%s, %d, %d, %p, %p)\n", iface, debugstr_guid(ripid), cRefs, cIids, iids, ppQIResults);
 
     hr = ipid_to_stub_manager(ripid, &apt, &stubmgr);
     if (hr != S_OK) return hr;
@@ -656,7 +700,7 @@ static HRESULT WINAPI RemUnknown_RemAddRef(IRemUnknown *iface,
             continue;
         }
 
-        stub_manager_ext_addref(stubmgr, InterfaceRefs[i].cPublicRefs);
+        stub_manager_ext_addref(stubmgr, InterfaceRefs[i].cPublicRefs, FALSE);
         if (InterfaceRefs[i].cPrivateRefs)
             FIXME("Adding %ld refs securely not implemented\n", InterfaceRefs[i].cPrivateRefs);
 
@@ -689,7 +733,7 @@ static HRESULT WINAPI RemUnknown_RemRelease(IRemUnknown *iface,
             break;
         }
 
-        stub_manager_ext_release(stubmgr, InterfaceRefs[i].cPublicRefs);
+        stub_manager_ext_release(stubmgr, InterfaceRefs[i].cPublicRefs, FALSE, TRUE);
         if (InterfaceRefs[i].cPrivateRefs)
             FIXME("Releasing %ld refs securely not implemented\n", InterfaceRefs[i].cPrivateRefs);
 
@@ -711,7 +755,7 @@ static const IRemUnknownVtbl RemUnknown_Vtbl =
 };
 
 /* starts the IRemUnknown listener for the current apartment */
-HRESULT start_apartment_remote_unknown()
+HRESULT start_apartment_remote_unknown(void)
 {
     IRemUnknown *pRemUnknown;
     HRESULT hr = S_OK;
@@ -726,7 +770,7 @@ HRESULT start_apartment_remote_unknown()
         {
             STDOBJREF stdobjref; /* dummy - not used */
             /* register it with the stub manager */
-            hr = marshal_object(apt, &stdobjref, &IID_IRemUnknown, (IUnknown *)pRemUnknown, MSHLFLAGS_NORMAL);
+            hr = marshal_object(apt, &stdobjref, &IID_IRemUnknown, (IUnknown *)pRemUnknown, MSHLFLAGS_NORMAL|MSHLFLAGSP_REMUNKNOWN);
             /* release our reference to the object as the stub manager will manage the life cycle for us */
             IRemUnknown_Release(pRemUnknown);
             if (hr == S_OK)