- Generate machine-local IPIDs.
- Make pipes be uniquely identified only by their IPID.
This commit is contained in:
parent
c1db191d9b
commit
ad34f3dc5e
@ -246,8 +246,10 @@ APARTMENT* COM_CreateApartment(DWORD model)
|
|||||||
|
|
||||||
list_init(&apt->proxies);
|
list_init(&apt->proxies);
|
||||||
list_init(&apt->stubmgrs);
|
list_init(&apt->stubmgrs);
|
||||||
apt->oidc = 1;
|
apt->ipidc = 1;
|
||||||
apt->refs = 1;
|
apt->refs = 1;
|
||||||
|
apt->remunk_exported = FALSE;
|
||||||
|
apt->oidc = 1;
|
||||||
InitializeCriticalSection(&apt->cs);
|
InitializeCriticalSection(&apt->cs);
|
||||||
|
|
||||||
apt->model = model;
|
apt->model = model;
|
||||||
@ -363,6 +365,30 @@ APARTMENT *COM_ApartmentFromOXID(OXID oxid, BOOL ref)
|
|||||||
return result;
|
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 *COM_ApartmentFromTID(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;
|
||||||
|
COM_ApartmentAddRef(result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LeaveCriticalSection(&csApartment);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
HWND COM_GetApartmentWin(OXID oxid, BOOL ref)
|
HWND COM_GetApartmentWin(OXID oxid, BOOL ref)
|
||||||
{
|
{
|
||||||
APARTMENT *apt;
|
APARTMENT *apt;
|
||||||
|
@ -110,6 +110,7 @@ struct proxy_manager
|
|||||||
DWORD refs; /* proxy reference count (LOCK) */
|
DWORD refs; /* proxy reference count (LOCK) */
|
||||||
CRITICAL_SECTION cs; /* thread safety for this object and children */
|
CRITICAL_SECTION cs; /* thread safety for this object and children */
|
||||||
ULONG sorflags; /* STDOBJREF flags (RO) */
|
ULONG sorflags; /* STDOBJREF flags (RO) */
|
||||||
|
IRemUnknown *remunk; /* proxy to IRemUnknown used for lifecycle management (CS cs) */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* this needs to become a COM object that implements IRemUnknown */
|
/* this needs to become a COM object that implements IRemUnknown */
|
||||||
@ -122,13 +123,15 @@ struct apartment
|
|||||||
DWORD tid; /* thread id (RO) */
|
DWORD tid; /* thread id (RO) */
|
||||||
HANDLE thread; /* thread handle (RO) */
|
HANDLE thread; /* thread handle (RO) */
|
||||||
OXID oxid; /* object exporter ID (RO) */
|
OXID oxid; /* object exporter ID (RO) */
|
||||||
OID oidc; /* object ID counter, starts at 1, zero is invalid OID (CS cs) */
|
DWORD ipidc; /* interface pointer ID counter, starts at 1 (CS cs) */
|
||||||
HWND win; /* message window (RO) */
|
HWND win; /* message window (RO) */
|
||||||
CRITICAL_SECTION cs; /* thread safety */
|
CRITICAL_SECTION cs; /* thread safety */
|
||||||
LPMESSAGEFILTER filter; /* message filter (CS cs) */
|
LPMESSAGEFILTER filter; /* message filter (CS cs) */
|
||||||
struct list proxies; /* imported objects (CS cs) */
|
struct list proxies; /* imported objects (CS cs) */
|
||||||
struct list stubmgrs; /* stub managers for exported objects (CS cs) */
|
struct list stubmgrs; /* stub managers for exported objects (CS cs) */
|
||||||
|
BOOL remunk_exported; /* has the IRemUnknown interface for this apartment been created yet? (CS cs) */
|
||||||
|
|
||||||
|
OID oidc; /* object ID counter, starts at 1, zero is invalid OID (CS cs). FIXME: remove me */
|
||||||
DWORD listenertid; /* id of apartment_listener_thread. FIXME: remove me */
|
DWORD listenertid; /* id of apartment_listener_thread. FIXME: remove me */
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -177,12 +180,15 @@ ULONG stub_manager_int_release(struct stub_manager *This);
|
|||||||
struct stub_manager *new_stub_manager(APARTMENT *apt, IUnknown *object);
|
struct stub_manager *new_stub_manager(APARTMENT *apt, IUnknown *object);
|
||||||
ULONG stub_manager_ext_addref(struct stub_manager *m, ULONG refs);
|
ULONG stub_manager_ext_addref(struct stub_manager *m, ULONG refs);
|
||||||
ULONG stub_manager_ext_release(struct stub_manager *m, ULONG refs);
|
ULONG stub_manager_ext_release(struct stub_manager *m, ULONG refs);
|
||||||
IRpcStubBuffer *stub_manager_ipid_to_stubbuffer(struct stub_manager *m, const IPID *ipid);
|
|
||||||
struct ifstub *stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *sb, IUnknown *iptr, REFIID iid, BOOL tablemarshal);
|
struct ifstub *stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *sb, IUnknown *iptr, REFIID iid, BOOL tablemarshal);
|
||||||
struct stub_manager *get_stub_manager(APARTMENT *apt, OID oid);
|
struct stub_manager *get_stub_manager(APARTMENT *apt, OID oid);
|
||||||
struct stub_manager *get_stub_manager_from_object(APARTMENT *apt, void *object);
|
struct stub_manager *get_stub_manager_from_object(APARTMENT *apt, void *object);
|
||||||
BOOL stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ipid);
|
BOOL stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ipid);
|
||||||
BOOL stub_manager_is_table_marshaled(struct stub_manager *m, const IPID *ipid);
|
BOOL stub_manager_is_table_marshaled(struct stub_manager *m, const IPID *ipid);
|
||||||
|
HRESULT register_ifstub(APARTMENT *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *obj, MSHLFLAGS mshlflags);
|
||||||
|
HRESULT ipid_to_stub_manager(const IPID *ipid, APARTMENT **stub_apt, struct stub_manager **stubmgr_ret);
|
||||||
|
IRpcStubBuffer *ipid_to_stubbuffer(const IPID *ipid);
|
||||||
|
HRESULT start_apartment_remote_unknown(void);
|
||||||
|
|
||||||
IRpcStubBuffer *mid_to_stubbuffer(wine_marshal_id *mid);
|
IRpcStubBuffer *mid_to_stubbuffer(wine_marshal_id *mid);
|
||||||
|
|
||||||
@ -203,6 +209,7 @@ int WINAPI FileMonikerImpl_DecomposePath(LPCOLESTR str, LPOLESTR** stringTable);
|
|||||||
/* compobj.c */
|
/* compobj.c */
|
||||||
APARTMENT *COM_CreateApartment(DWORD model);
|
APARTMENT *COM_CreateApartment(DWORD model);
|
||||||
APARTMENT *COM_ApartmentFromOXID(OXID oxid, BOOL ref);
|
APARTMENT *COM_ApartmentFromOXID(OXID oxid, BOOL ref);
|
||||||
|
APARTMENT *COM_ApartmentFromTID(DWORD tid);
|
||||||
DWORD COM_ApartmentAddRef(struct apartment *apt);
|
DWORD COM_ApartmentAddRef(struct apartment *apt);
|
||||||
DWORD COM_ApartmentRelease(struct apartment *apt);
|
DWORD COM_ApartmentRelease(struct apartment *apt);
|
||||||
|
|
||||||
|
@ -56,6 +56,8 @@ extern const CLSID CLSID_DfMarshal;
|
|||||||
* when the proxy disconnects or is destroyed */
|
* when the proxy disconnects or is destroyed */
|
||||||
#define SORFP_NOLIFETIMEMGMT SORF_OXRES1
|
#define SORFP_NOLIFETIMEMGMT SORF_OXRES1
|
||||||
|
|
||||||
|
static HRESULT unmarshal_object(const STDOBJREF *stdobjref, APARTMENT *apt, REFIID riid, void **object);
|
||||||
|
|
||||||
/* Marshalling just passes a unique identifier to the remote client,
|
/* Marshalling just passes a unique identifier to the remote client,
|
||||||
* that makes it possible to find the passed interface again.
|
* that makes it possible to find the passed interface again.
|
||||||
*
|
*
|
||||||
@ -83,35 +85,8 @@ get_facbuf_for_iid(REFIID riid,IPSFactoryBuffer **facbuf) {
|
|||||||
return CoGetClassObject(&pxclsid,CLSCTX_INPROC_SERVER,NULL,&IID_IPSFactoryBuffer,(LPVOID*)facbuf);
|
return CoGetClassObject(&pxclsid,CLSCTX_INPROC_SERVER,NULL,&IID_IPSFactoryBuffer,(LPVOID*)facbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
IRpcStubBuffer *mid_to_stubbuffer(wine_marshal_id *mid)
|
|
||||||
{
|
|
||||||
IRpcStubBuffer *ret;
|
|
||||||
APARTMENT *apt;
|
|
||||||
struct stub_manager *m;
|
|
||||||
|
|
||||||
if (!(apt = COM_ApartmentFromOXID(mid->oxid, TRUE)))
|
|
||||||
{
|
|
||||||
WARN("Could not map OXID %s to apartment object\n", wine_dbgstr_longlong(mid->oxid));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(m = get_stub_manager(apt, mid->oid)))
|
|
||||||
{
|
|
||||||
WARN("unknown OID %s\n", wine_dbgstr_longlong(mid->oid));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = stub_manager_ipid_to_stubbuffer(m, &mid->ipid);
|
|
||||||
|
|
||||||
stub_manager_int_release(m);
|
|
||||||
|
|
||||||
COM_ApartmentRelease(apt);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* creates a new stub manager and sets stdobjref->oid when stdobjref->oid == 0 */
|
/* creates a new stub manager and sets stdobjref->oid when stdobjref->oid == 0 */
|
||||||
static HRESULT register_ifstub(APARTMENT *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *obj, MSHLFLAGS mshlflags)
|
HRESULT register_ifstub(APARTMENT *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *obj, MSHLFLAGS mshlflags)
|
||||||
{
|
{
|
||||||
struct stub_manager *manager = NULL;
|
struct stub_manager *manager = NULL;
|
||||||
struct ifstub *ifstub;
|
struct ifstub *ifstub;
|
||||||
|
@ -59,7 +59,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(ole);
|
|||||||
struct request_header
|
struct request_header
|
||||||
{
|
{
|
||||||
DWORD reqid;
|
DWORD reqid;
|
||||||
wine_marshal_id mid;
|
IPID ipid;
|
||||||
DWORD iMethod;
|
DWORD iMethod;
|
||||||
DWORD cbBuffer;
|
DWORD cbBuffer;
|
||||||
};
|
};
|
||||||
@ -162,7 +162,6 @@ static DWORD WINAPI stub_dispatch_thread(LPVOID);
|
|||||||
static HRESULT
|
static HRESULT
|
||||||
PIPE_RegisterPipe(wine_marshal_id *mid, HANDLE hPipe, BOOL startreader)
|
PIPE_RegisterPipe(wine_marshal_id *mid, HANDLE hPipe, BOOL startreader)
|
||||||
{
|
{
|
||||||
|
|
||||||
/* FIXME: this pipe caching code is commented out because it is breaks the
|
/* FIXME: this pipe caching code is commented out because it is breaks the
|
||||||
* tests, causing them hang due to writing to or reading from the wrong pipe.
|
* tests, causing them hang due to writing to or reading from the wrong pipe.
|
||||||
*/
|
*/
|
||||||
@ -205,15 +204,21 @@ PIPE_FindByMID(wine_marshal_id *mid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct pipe*
|
static struct pipe*
|
||||||
PIPE_GetFromMID(wine_marshal_id *mid) {
|
PIPE_GetFromIPID(const IPID *ipid) {
|
||||||
int i;
|
int i;
|
||||||
for (i=0;i<nrofpipes;i++) {
|
for (i=0; i<nrofpipes; i++)
|
||||||
if ((pipes[i].mid.oxid==mid->oxid) &&
|
{
|
||||||
(GetCurrentThreadId()==pipes[i].tid)
|
/* compare TID and PID fields */
|
||||||
)
|
if ((pipes[i].mid.ipid.Data2 == ipid->Data2) &&
|
||||||
return pipes+i;
|
(pipes[i].mid.ipid.Data3 == ipid->Data3) &&
|
||||||
}
|
(GetCurrentThreadId() == pipes[i].tid))
|
||||||
return NULL;
|
return pipes+i;
|
||||||
|
/* special case for IRemUnknown IPID */
|
||||||
|
else if ((pipes[i].mid.oxid == *(OXID *)ipid->Data4) &&
|
||||||
|
(GetCurrentThreadId() == pipes[i].tid))
|
||||||
|
return pipes+i;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HRESULT
|
static HRESULT
|
||||||
@ -331,7 +336,7 @@ COM_InvokeAndRpcSend(struct rpc *req) {
|
|||||||
HRESULT hres;
|
HRESULT hres;
|
||||||
DWORD reqtype;
|
DWORD reqtype;
|
||||||
|
|
||||||
if (!(stub = mid_to_stubbuffer(&(req->reqh.mid))))
|
if (!(stub = ipid_to_stubbuffer(&(req->reqh.ipid))))
|
||||||
{
|
{
|
||||||
ERR("Stub not found?\n");
|
ERR("Stub not found?\n");
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
@ -366,7 +371,7 @@ RPC_QueueRequestAndWait(struct rpc *req) {
|
|||||||
struct rpc *xreq;
|
struct rpc *xreq;
|
||||||
HRESULT hres;
|
HRESULT hres;
|
||||||
DWORD reqtype;
|
DWORD reqtype;
|
||||||
struct pipe *xpipe = PIPE_GetFromMID(&(req->reqh.mid));
|
struct pipe *xpipe = PIPE_GetFromIPID(&(req->reqh.ipid));
|
||||||
|
|
||||||
if (!xpipe) {
|
if (!xpipe) {
|
||||||
FIXME("no pipe found.\n");
|
FIXME("no pipe found.\n");
|
||||||
@ -421,7 +426,7 @@ PipeBuf_SendReceive(LPRPCCHANNELBUFFER iface,RPCOLEMESSAGE* msg,ULONG *status)
|
|||||||
if (hres) return hres;
|
if (hres) return hres;
|
||||||
req->reqh.iMethod = msg->iMethod;
|
req->reqh.iMethod = msg->iMethod;
|
||||||
req->reqh.cbBuffer = msg->cbBuffer;
|
req->reqh.cbBuffer = msg->cbBuffer;
|
||||||
memcpy(&(req->reqh.mid),&(This->mid),sizeof(This->mid));
|
req->reqh.ipid = This->mid.ipid;
|
||||||
req->Buffer = msg->Buffer;
|
req->Buffer = msg->Buffer;
|
||||||
hres = RPC_QueueRequestAndWait(req);
|
hres = RPC_QueueRequestAndWait(req);
|
||||||
if (hres) {
|
if (hres) {
|
||||||
|
@ -243,11 +243,103 @@ static struct ifstub *stub_manager_ipid_to_ifstub(struct stub_manager *m, const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
IRpcStubBuffer *stub_manager_ipid_to_stubbuffer(struct stub_manager *m, const IPID *ipid)
|
/* 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 */
|
||||||
|
static struct stub_manager *get_stub_manager_from_ipid(APARTMENT *apt, const IPID *ipid)
|
||||||
{
|
{
|
||||||
struct ifstub *ifstub = stub_manager_ipid_to_ifstub(m, ipid);
|
struct stub_manager *result = NULL;
|
||||||
|
struct list *cursor;
|
||||||
return ifstub ? ifstub->stubbuffer : NULL;
|
|
||||||
|
EnterCriticalSection(&apt->cs);
|
||||||
|
LIST_FOR_EACH( cursor, &apt->stubmgrs )
|
||||||
|
{
|
||||||
|
struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry );
|
||||||
|
|
||||||
|
if (stub_manager_ipid_to_ifstub(m, ipid))
|
||||||
|
{
|
||||||
|
result = m;
|
||||||
|
stub_manager_int_addref(result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LeaveCriticalSection(&apt->cs);
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
TRACE("found %p for ipid %s\n", result, debugstr_guid(ipid));
|
||||||
|
else
|
||||||
|
ERR("not found for ipid %s\n", debugstr_guid(ipid));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT ipid_to_stub_manager(const IPID *ipid, APARTMENT **stub_apt, struct stub_manager **stubmgr_ret)
|
||||||
|
{
|
||||||
|
/* FIXME: hack for IRemUnknown */
|
||||||
|
if (ipid->Data2 == 0xffff)
|
||||||
|
*stub_apt = COM_ApartmentFromOXID(*(OXID *)ipid->Data4, TRUE);
|
||||||
|
else
|
||||||
|
*stub_apt = COM_ApartmentFromTID(ipid->Data2);
|
||||||
|
if (!*stub_apt)
|
||||||
|
{
|
||||||
|
ERR("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);
|
||||||
|
if (!*stubmgr_ret)
|
||||||
|
{
|
||||||
|
COM_ApartmentRelease(*stub_apt);
|
||||||
|
*stub_apt = NULL;
|
||||||
|
return RPC_E_INVALID_OBJECT;
|
||||||
|
}
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
IRpcStubBuffer *ipid_to_stubbuffer(const IPID *ipid)
|
||||||
|
{
|
||||||
|
IRpcStubBuffer *ret = NULL;
|
||||||
|
APARTMENT *apt;
|
||||||
|
struct stub_manager *stubmgr;
|
||||||
|
struct ifstub *ifstub;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
hr = ipid_to_stub_manager(ipid, &apt, &stubmgr);
|
||||||
|
if (hr != S_OK) return NULL;
|
||||||
|
|
||||||
|
ifstub = stub_manager_ipid_to_ifstub(stubmgr, ipid);
|
||||||
|
if (ifstub)
|
||||||
|
ret = ifstub->stubbuffer;
|
||||||
|
|
||||||
|
stub_manager_int_release(stubmgr);
|
||||||
|
|
||||||
|
COM_ApartmentRelease(apt);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* generates an ipid in the following format (similar to native version):
|
||||||
|
* Data1 = apartment-local ipid counter
|
||||||
|
* Data2 = apartment creator thread ID
|
||||||
|
* Data3 = process ID
|
||||||
|
* Data4 = random value
|
||||||
|
*/
|
||||||
|
static inline HRESULT generate_ipid(struct stub_manager *m, IPID *ipid)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
hr = UuidCreate(ipid);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
ERR("couldn't create IPID for stub manager %p\n", m);
|
||||||
|
UuidCreateNil(ipid);
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnterCriticalSection(&m->apt->cs);
|
||||||
|
ipid->Data1 = m->apt->ipidc++;
|
||||||
|
LeaveCriticalSection(&m->apt->cs);
|
||||||
|
ipid->Data2 = (USHORT)m->apt->tid;
|
||||||
|
ipid->Data3 = (USHORT)GetCurrentProcessId();
|
||||||
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* registers a new interface stub COM object with the stub manager and returns registration record */
|
/* registers a new interface stub COM object with the stub manager and returns registration record */
|
||||||
@ -273,12 +365,26 @@ struct ifstub *stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *s
|
|||||||
stub->state = IFSTUB_STATE_NORMAL_MARSHALED;
|
stub->state = IFSTUB_STATE_NORMAL_MARSHALED;
|
||||||
|
|
||||||
stub->iid = *iid;
|
stub->iid = *iid;
|
||||||
stub->ipid = *iid; /* FIXME: should be globally unique */
|
|
||||||
|
/* 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));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
generate_ipid(m, &stub->ipid);
|
||||||
|
|
||||||
EnterCriticalSection(&m->lock);
|
EnterCriticalSection(&m->lock);
|
||||||
list_add_head(&m->ifstubs, &stub->entry);
|
list_add_head(&m->ifstubs, &stub->entry);
|
||||||
LeaveCriticalSection(&m->lock);
|
LeaveCriticalSection(&m->lock);
|
||||||
|
|
||||||
|
TRACE("ifstub %p created with ipid %s\n", stub, debugstr_guid(&stub->ipid));
|
||||||
|
|
||||||
return stub;
|
return stub;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,3 +457,5 @@ BOOL stub_manager_is_table_marshaled(struct stub_manager *m, const IPID *ipid)
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const IID IID_IRemUnknown = { 0x00000131, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46} };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user