- Add critsec debugging info.
[wine/hacks.git] / dlls / ole32 / stubmanager.c
blob998d1ed47efb61655427d4f1fd5c13ed51cbde76
1 /*
2 * A stub manager is an object that controls interface stubs. It is
3 * identified by an OID (object identifier) and acts as the network
4 * identity of the object. There can be many stub managers in a
5 * process or apartment.
7 * Copyright 2002 Marcus Meissner
8 * Copyright 2004 Mike Hearn for CodeWeavers
9 * Copyright 2004 Robert Shearman (for CodeWeavers)
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #define COBJMACROS
27 #define NONAMELESSUNION
28 #define NONAMELESSSTRUCT
30 #include <assert.h>
31 #include <stdarg.h>
32 #include <limits.h>
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winuser.h"
37 #include "objbase.h"
38 #include "ole2.h"
39 #include "ole2ver.h"
40 #include "rpc.h"
41 #include "wine/debug.h"
42 #include "compobj_private.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(ole);
46 static void stub_manager_delete_ifstub(struct stub_manager *m, struct ifstub *ifstub);
47 static struct ifstub *stub_manager_ipid_to_ifstub(struct stub_manager *m, const IPID *ipid);
49 /* creates a new stub manager and adds it into the apartment. caller must
50 * release stub manager when it is no longer required. the apartment and
51 * external refs together take one implicit ref */
52 struct stub_manager *new_stub_manager(APARTMENT *apt, IUnknown *object, MSHLFLAGS mshlflags)
54 struct stub_manager *sm;
56 assert( apt );
58 sm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct stub_manager));
59 if (!sm) return NULL;
61 list_init(&sm->ifstubs);
63 InitializeCriticalSection(&sm->lock);
64 DEBUG_SET_CRITSEC_NAME(&sm->lock, "stub_manager");
66 IUnknown_AddRef(object);
67 sm->object = object;
68 sm->apt = apt;
70 /* start off with 2 references because the stub is in the apartment
71 * and the caller will also hold a reference */
72 sm->refs = 2;
74 /* yes, that's right, this starts at zero. that's zero EXTERNAL
75 * refs, ie nobody has unmarshalled anything yet. we can't have
76 * negative refs because the stub manager cannot be explicitly
77 * killed, it has to die by somebody unmarshalling then releasing
78 * the marshalled ifptr.
80 sm->extrefs = 0;
82 if (mshlflags & MSHLFLAGS_TABLESTRONG)
83 sm->state = STUBSTATE_TABLE_STRONG;
84 else if (mshlflags & MSHLFLAGS_TABLEWEAK)
85 sm->state = STUBSTATE_TABLE_WEAK_UNMARSHALED;
86 else
87 sm->state = STUBSTATE_NORMAL_MARSHALED;
89 EnterCriticalSection(&apt->cs);
90 sm->oid = apt->oidc++;
91 list_add_head(&apt->stubmgrs, &sm->entry);
92 LeaveCriticalSection(&apt->cs);
94 TRACE("Created new stub manager (oid=%s) at %p for object with IUnknown %p\n", wine_dbgstr_longlong(sm->oid), sm, object);
96 return sm;
99 /* m->apt->cs must be held on entry to this function */
100 static void stub_manager_delete(struct stub_manager *m)
102 struct list *cursor;
104 TRACE("destroying %p (oid=%s)\n", m, wine_dbgstr_longlong(m->oid));
106 list_remove(&m->entry);
108 /* release every ifstub */
109 while ((cursor = list_head(&m->ifstubs)))
111 struct ifstub *ifstub = LIST_ENTRY(cursor, struct ifstub, entry);
112 stub_manager_delete_ifstub(m, ifstub);
115 IUnknown_Release(m->object);
117 DEBUG_CLEAR_CRITSEC_NAME(&m->lock);
118 DeleteCriticalSection(&m->lock);
120 HeapFree(GetProcessHeap(), 0, m);
123 /* gets the stub manager associated with an object - caller must have
124 * a reference to the apartment while a reference to the stub manager is held.
125 * it must also call release on the stub manager when it is no longer needed */
126 struct stub_manager *get_stub_manager_from_object(APARTMENT *apt, void *object)
128 struct stub_manager *result = NULL;
129 struct list *cursor;
131 EnterCriticalSection(&apt->cs);
132 LIST_FOR_EACH( cursor, &apt->stubmgrs )
134 struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry );
136 if (m->object == object)
138 result = m;
139 stub_manager_int_addref(result);
140 break;
143 LeaveCriticalSection(&apt->cs);
145 if (result)
146 TRACE("found %p for object %p\n", result, object);
147 else
148 TRACE("not found for object %p\n", object);
150 return result;
153 /* removes the apartment reference to an object, destroying it when no other
154 * threads have a reference to it */
155 void apartment_disconnectobject(struct apartment *apt, void *object)
157 int found = FALSE;
158 struct stub_manager *stubmgr;
160 EnterCriticalSection(&apt->cs);
161 LIST_FOR_EACH_ENTRY( stubmgr, &apt->stubmgrs, struct stub_manager, entry )
163 if (stubmgr->object == object)
165 found = TRUE;
166 stub_manager_int_release(stubmgr);
167 break;
170 LeaveCriticalSection(&apt->cs);
172 if (found)
173 TRACE("disconnect object %p\n", object);
174 else
175 WARN("couldn't find object %p\n", object);
178 /* gets the stub manager associated with an object id - caller must have
179 * a reference to the apartment while a reference to the stub manager is held.
180 * it must also call release on the stub manager when it is no longer needed */
181 struct stub_manager *get_stub_manager(APARTMENT *apt, OID oid)
183 struct stub_manager *result = NULL;
184 struct list *cursor;
186 EnterCriticalSection(&apt->cs);
187 LIST_FOR_EACH( cursor, &apt->stubmgrs )
189 struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry );
191 if (m->oid == oid)
193 result = m;
194 stub_manager_int_addref(result);
195 break;
198 LeaveCriticalSection(&apt->cs);
200 if (result)
201 TRACE("found %p for oid %s\n", result, wine_dbgstr_longlong(oid));
202 else
203 TRACE("not found for oid %s\n", wine_dbgstr_longlong(oid));
205 return result;
208 /* increments the internal refcount */
209 ULONG stub_manager_int_addref(struct stub_manager *This)
211 ULONG refs;
213 EnterCriticalSection(&This->apt->cs);
214 refs = ++This->refs;
215 LeaveCriticalSection(&This->apt->cs);
217 TRACE("before %ld\n", refs - 1);
219 return refs;
222 /* decrements the internal refcount */
223 ULONG stub_manager_int_release(struct stub_manager *This)
225 ULONG refs;
226 APARTMENT *apt = This->apt;
228 EnterCriticalSection(&apt->cs);
229 refs = --This->refs;
231 TRACE("after %ld\n", refs);
233 if (!refs)
234 stub_manager_delete(This);
235 LeaveCriticalSection(&apt->cs);
237 return refs;
240 /* add some external references (ie from a client that unmarshaled an ifptr) */
241 ULONG stub_manager_ext_addref(struct stub_manager *m, ULONG refs)
243 ULONG rc;
245 EnterCriticalSection(&m->lock);
247 /* make sure we don't overflow extrefs */
248 refs = min(refs, (ULONG_MAX-1 - m->extrefs));
249 rc = (m->extrefs += refs);
251 LeaveCriticalSection(&m->lock);
253 TRACE("added %lu refs to %p (oid %s), rc is now %lu\n", refs, m, wine_dbgstr_longlong(m->oid), rc);
255 return rc;
258 /* remove some external references */
259 ULONG stub_manager_ext_release(struct stub_manager *m, ULONG refs)
261 ULONG rc;
263 EnterCriticalSection(&m->lock);
265 /* make sure we don't underflow extrefs */
266 refs = min(refs, m->extrefs);
267 rc = (m->extrefs -= refs);
269 LeaveCriticalSection(&m->lock);
271 TRACE("removed %lu refs from %p (oid %s), rc is now %lu\n", refs, m, wine_dbgstr_longlong(m->oid), rc);
273 if (rc == 0)
274 stub_manager_int_release(m);
276 return rc;
279 static struct ifstub *stub_manager_ipid_to_ifstub(struct stub_manager *m, const IPID *ipid)
281 struct list *cursor;
282 struct ifstub *result = NULL;
284 EnterCriticalSection(&m->lock);
285 LIST_FOR_EACH( cursor, &m->ifstubs )
287 struct ifstub *ifstub = LIST_ENTRY( cursor, struct ifstub, entry );
289 if (IsEqualGUID(ipid, &ifstub->ipid))
291 result = ifstub;
292 break;
295 LeaveCriticalSection(&m->lock);
297 return result;
300 /* gets the stub manager associated with an ipid - caller must have
301 * a reference to the apartment while a reference to the stub manager is held.
302 * it must also call release on the stub manager when it is no longer needed */
303 static struct stub_manager *get_stub_manager_from_ipid(APARTMENT *apt, const IPID *ipid)
305 struct stub_manager *result = NULL;
306 struct list *cursor;
308 EnterCriticalSection(&apt->cs);
309 LIST_FOR_EACH( cursor, &apt->stubmgrs )
311 struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry );
313 if (stub_manager_ipid_to_ifstub(m, ipid))
315 result = m;
316 stub_manager_int_addref(result);
317 break;
320 LeaveCriticalSection(&apt->cs);
322 if (result)
323 TRACE("found %p for ipid %s\n", result, debugstr_guid(ipid));
324 else
325 ERR("not found for ipid %s\n", debugstr_guid(ipid));
327 return result;
330 HRESULT ipid_to_stub_manager(const IPID *ipid, APARTMENT **stub_apt, struct stub_manager **stubmgr_ret)
332 /* FIXME: hack for IRemUnknown */
333 if (ipid->Data2 == 0xffff)
334 *stub_apt = apartment_findfromoxid(*(OXID *)ipid->Data4, TRUE);
335 else
336 *stub_apt = apartment_findfromtid(ipid->Data2);
337 if (!*stub_apt)
339 ERR("Couldn't find apartment corresponding to TID 0x%04x\n", ipid->Data2);
340 return RPC_E_INVALID_OBJECT;
342 *stubmgr_ret = get_stub_manager_from_ipid(*stub_apt, ipid);
343 if (!*stubmgr_ret)
345 apartment_release(*stub_apt);
346 *stub_apt = NULL;
347 return RPC_E_INVALID_OBJECT;
349 return S_OK;
352 /* gets the apartment and IRpcStubBuffer from an object. the caller must
353 * release the references to both objects */
354 IRpcStubBuffer *ipid_to_apt_and_stubbuffer(const IPID *ipid, APARTMENT **stub_apt)
356 IRpcStubBuffer *ret = NULL;
357 struct stub_manager *stubmgr;
358 struct ifstub *ifstub;
359 HRESULT hr;
361 *stub_apt = NULL;
363 hr = ipid_to_stub_manager(ipid, stub_apt, &stubmgr);
364 if (hr != S_OK) return NULL;
366 ifstub = stub_manager_ipid_to_ifstub(stubmgr, ipid);
367 if (ifstub)
368 ret = ifstub->stubbuffer;
370 if (ret) IRpcStubBuffer_AddRef(ret);
372 stub_manager_int_release(stubmgr);
374 return ret;
377 /* generates an ipid in the following format (similar to native version):
378 * Data1 = apartment-local ipid counter
379 * Data2 = apartment creator thread ID
380 * Data3 = process ID
381 * Data4 = random value
383 static inline HRESULT generate_ipid(struct stub_manager *m, IPID *ipid)
385 HRESULT hr;
386 hr = UuidCreate(ipid);
387 if (FAILED(hr))
389 ERR("couldn't create IPID for stub manager %p\n", m);
390 UuidCreateNil(ipid);
391 return hr;
394 ipid->Data1 = InterlockedIncrement(&m->apt->ipidc);
395 ipid->Data2 = (USHORT)m->apt->tid;
396 ipid->Data3 = (USHORT)GetCurrentProcessId();
397 return S_OK;
400 /* registers a new interface stub COM object with the stub manager and returns registration record */
401 struct ifstub *stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *sb, IUnknown *iptr, REFIID iid)
403 struct ifstub *stub;
405 TRACE("oid=%s, stubbuffer=%p, iptr=%p, iid=%s\n",
406 wine_dbgstr_longlong(m->oid), sb, iptr, debugstr_guid(iid));
408 stub = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct ifstub));
409 if (!stub) return NULL;
411 stub->stubbuffer = sb;
412 IUnknown_AddRef(sb);
414 /* no need to ref this, same object as sb */
415 stub->iface = iptr;
417 stub->iid = *iid;
419 /* FIXME: hack for IRemUnknown because we don't notify SCM of our IPID
420 * yet, so we need to use a well-known one */
421 if (IsEqualIID(iid, &IID_IRemUnknown))
423 stub->ipid.Data1 = 0xffffffff;
424 stub->ipid.Data2 = 0xffff;
425 stub->ipid.Data3 = 0xffff;
426 assert(sizeof(stub->ipid.Data4) == sizeof(m->apt->oxid));
427 memcpy(&stub->ipid.Data4, &m->apt->oxid, sizeof(OXID));
429 else
430 generate_ipid(m, &stub->ipid);
432 EnterCriticalSection(&m->lock);
433 list_add_head(&m->ifstubs, &stub->entry);
434 LeaveCriticalSection(&m->lock);
436 TRACE("ifstub %p created with ipid %s\n", stub, debugstr_guid(&stub->ipid));
438 return stub;
441 static void stub_manager_delete_ifstub(struct stub_manager *m, struct ifstub *ifstub)
443 TRACE("m=%p, m->oid=%s, ipid=%s\n", m, wine_dbgstr_longlong(m->oid), debugstr_guid(&ifstub->ipid));
445 list_remove(&ifstub->entry);
447 RPC_UnregisterInterface(&ifstub->iid);
449 IUnknown_Release(ifstub->stubbuffer);
450 IUnknown_Release(ifstub->iface);
452 HeapFree(GetProcessHeap(), 0, ifstub);
455 /* returns TRUE if it is possible to unmarshal, FALSE otherwise. */
456 BOOL stub_manager_notify_unmarshal(struct stub_manager *m)
458 BOOL ret;
460 EnterCriticalSection(&m->lock);
462 switch (m->state)
464 case STUBSTATE_TABLE_STRONG:
465 case STUBSTATE_TABLE_WEAK_MARSHALED:
466 /* no transition */
467 ret = TRUE;
468 break;
469 case STUBSTATE_TABLE_WEAK_UNMARSHALED:
470 m->state = STUBSTATE_TABLE_WEAK_MARSHALED;
471 ret = TRUE;
472 break;
473 case STUBSTATE_NORMAL_MARSHALED:
474 m->state = STUBSTATE_NORMAL_UNMARSHALED;
475 ret = TRUE;
476 break;
477 default:
478 WARN("object OID %s already unmarshaled\n",
479 wine_dbgstr_longlong(m->oid));
480 ret = FALSE;
481 break;
484 LeaveCriticalSection(&m->lock);
486 return ret;
489 void stub_manager_release_marshal_data(struct stub_manager *m, ULONG refs)
491 EnterCriticalSection(&m->lock);
493 switch (m->state)
495 case STUBSTATE_NORMAL_MARSHALED:
496 case STUBSTATE_NORMAL_UNMARSHALED: /* FIXME: check this */
497 /* nothing to change */
498 break;
499 case STUBSTATE_TABLE_WEAK_UNMARSHALED:
500 case STUBSTATE_TABLE_STRONG:
501 refs = 1;
502 break;
503 case STUBSTATE_TABLE_WEAK_MARSHALED:
504 refs = 0; /* like native */
505 break;
508 stub_manager_ext_release(m, refs);
510 LeaveCriticalSection(&m->lock);
513 /* is an ifstub table marshaled? */
514 BOOL stub_manager_is_table_marshaled(struct stub_manager *m)
516 BOOL ret;
518 EnterCriticalSection(&m->lock);
519 ret = ((m->state == STUBSTATE_TABLE_STRONG) ||
520 (m->state == STUBSTATE_TABLE_WEAK_MARSHALED) ||
521 (m->state == STUBSTATE_TABLE_WEAK_UNMARSHALED));
522 LeaveCriticalSection(&m->lock);
524 return ret;
528 /*****************************************************************************
530 * IRemUnknown implementation
533 * Note: this object is not related to the lifetime of a stub_manager, but it
534 * interacts with stub managers.
537 const IID IID_IRemUnknown = { 0x00000131, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46} };
539 typedef struct rem_unknown
541 const IRemUnknownVtbl *lpVtbl;
542 ULONG refs;
543 } RemUnknown;
545 static const IRemUnknownVtbl RemUnknown_Vtbl;
548 /* construct an IRemUnknown object with one outstanding reference */
549 static HRESULT RemUnknown_Construct(IRemUnknown **ppRemUnknown)
551 RemUnknown *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
553 if (!This) return E_OUTOFMEMORY;
555 This->lpVtbl = &RemUnknown_Vtbl;
556 This->refs = 1;
558 *ppRemUnknown = (IRemUnknown *)This;
559 return S_OK;
562 static HRESULT WINAPI RemUnknown_QueryInterface(IRemUnknown *iface, REFIID riid, void **ppv)
564 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
566 if (IsEqualIID(riid, &IID_IUnknown) ||
567 IsEqualIID(riid, &IID_IRemUnknown))
569 *ppv = (LPVOID)iface;
570 IRemUnknown_AddRef(iface);
571 return S_OK;
574 FIXME("No interface for iid %s\n", debugstr_guid(riid));
576 *ppv = NULL;
577 return E_NOINTERFACE;
580 static ULONG WINAPI RemUnknown_AddRef(IRemUnknown *iface)
582 ULONG refs;
583 RemUnknown *This = (RemUnknown *)iface;
585 refs = InterlockedIncrement(&This->refs);
587 TRACE("%p before: %ld\n", iface, refs-1);
588 return refs;
591 static ULONG WINAPI RemUnknown_Release(IRemUnknown *iface)
593 ULONG refs;
594 RemUnknown *This = (RemUnknown *)iface;
596 refs = InterlockedDecrement(&This->refs);
597 if (!refs)
598 HeapFree(GetProcessHeap(), 0, This);
600 TRACE("%p after: %ld\n", iface, refs);
601 return refs;
604 static HRESULT WINAPI RemUnknown_RemQueryInterface(IRemUnknown *iface,
605 REFIPID ripid, ULONG cRefs, USHORT cIids, IID *iids /* [size_is(cIids)] */,
606 REMQIRESULT **ppQIResults /* [size_is(,cIids)] */)
608 HRESULT hr;
609 USHORT i;
610 USHORT successful_qis = 0;
611 APARTMENT *apt;
612 struct stub_manager *stubmgr;
614 TRACE("(%p)->(%s, %ld, %d, %p, %p)\n", iface, debugstr_guid(ripid), cRefs, cIids, iids, ppQIResults);
616 hr = ipid_to_stub_manager(ripid, &apt, &stubmgr);
617 if (hr != S_OK) return hr;
619 *ppQIResults = CoTaskMemAlloc(sizeof(REMQIRESULT) * cIids);
621 for (i = 0; i < cIids; i++)
623 HRESULT hrobj = marshal_object(apt, &(*ppQIResults)[i].std, &iids[i],
624 stubmgr->object, MSHLFLAGS_NORMAL);
625 if (hrobj == S_OK)
626 successful_qis++;
627 (*ppQIResults)[i].hResult = hrobj;
630 stub_manager_int_release(stubmgr);
631 apartment_release(apt);
633 if (successful_qis == cIids)
634 return S_OK; /* we got all requested interfaces */
635 else if (successful_qis == 0)
636 return E_NOINTERFACE; /* we didn't get any interfaces */
637 else
638 return S_FALSE; /* we got some interfaces */
641 static HRESULT WINAPI RemUnknown_RemAddRef(IRemUnknown *iface,
642 USHORT cInterfaceRefs,
643 REMINTERFACEREF* InterfaceRefs /* [size_is(cInterfaceRefs)] */,
644 HRESULT *pResults /* [size_is(cInterfaceRefs)] */)
646 HRESULT hr = S_OK;
647 USHORT i;
649 TRACE("(%p)->(%d, %p, %p)\n", iface, cInterfaceRefs, InterfaceRefs, pResults);
651 for (i = 0; i < cInterfaceRefs; i++)
653 APARTMENT *apt;
654 struct stub_manager *stubmgr;
656 pResults[i] = ipid_to_stub_manager(&InterfaceRefs[i].ipid, &apt, &stubmgr);
657 if (pResults[i] != S_OK)
659 hr = S_FALSE;
660 continue;
663 stub_manager_ext_addref(stubmgr, InterfaceRefs[i].cPublicRefs);
664 if (InterfaceRefs[i].cPrivateRefs)
665 FIXME("Adding %ld refs securely not implemented\n", InterfaceRefs[i].cPrivateRefs);
667 stub_manager_int_release(stubmgr);
668 apartment_release(apt);
671 return hr;
674 static HRESULT WINAPI RemUnknown_RemRelease(IRemUnknown *iface,
675 USHORT cInterfaceRefs,
676 REMINTERFACEREF* InterfaceRefs /* [size_is(cInterfaceRefs)] */)
678 HRESULT hr = S_OK;
679 USHORT i;
681 TRACE("(%p)->(%d, %p)\n", iface, cInterfaceRefs, InterfaceRefs);
683 for (i = 0; i < cInterfaceRefs; i++)
685 APARTMENT *apt;
686 struct stub_manager *stubmgr;
688 hr = ipid_to_stub_manager(&InterfaceRefs[i].ipid, &apt, &stubmgr);
689 if (hr != S_OK)
691 hr = E_INVALIDARG;
692 /* FIXME: we should undo any changes already made in this function */
693 break;
696 stub_manager_ext_release(stubmgr, InterfaceRefs[i].cPublicRefs);
697 if (InterfaceRefs[i].cPrivateRefs)
698 FIXME("Releasing %ld refs securely not implemented\n", InterfaceRefs[i].cPrivateRefs);
700 stub_manager_int_release(stubmgr);
701 apartment_release(apt);
704 return hr;
707 static const IRemUnknownVtbl RemUnknown_Vtbl =
709 RemUnknown_QueryInterface,
710 RemUnknown_AddRef,
711 RemUnknown_Release,
712 RemUnknown_RemQueryInterface,
713 RemUnknown_RemAddRef,
714 RemUnknown_RemRelease
717 /* starts the IRemUnknown listener for the current apartment */
718 HRESULT start_apartment_remote_unknown()
720 IRemUnknown *pRemUnknown;
721 HRESULT hr = S_OK;
722 APARTMENT *apt = COM_CurrentApt();
724 EnterCriticalSection(&apt->cs);
725 if (!apt->remunk_exported)
727 /* create the IRemUnknown object */
728 hr = RemUnknown_Construct(&pRemUnknown);
729 if (hr == S_OK)
731 STDOBJREF stdobjref; /* dummy - not used */
732 /* register it with the stub manager */
733 hr = marshal_object(apt, &stdobjref, &IID_IRemUnknown, (IUnknown *)pRemUnknown, MSHLFLAGS_NORMAL);
734 /* release our reference to the object as the stub manager will manage the life cycle for us */
735 IRemUnknown_Release(pRemUnknown);
736 if (hr == S_OK)
737 apt->remunk_exported = TRUE;
740 LeaveCriticalSection(&apt->cs);
741 return hr;