Preliminary support for COM apartments.
This commit is contained in:
parent
350aa2dcc4
commit
ef57c4d905
|
@ -31,7 +31,8 @@
|
||||||
|
|
||||||
#define NONAMELESSUNION
|
#define NONAMELESSUNION
|
||||||
#define NONAMELESSSTRUCT
|
#define NONAMELESSSTRUCT
|
||||||
#include "windef.h"
|
#include "winbase.h"
|
||||||
|
#include "winuser.h"
|
||||||
#include "objbase.h"
|
#include "objbase.h"
|
||||||
#include "ole2.h"
|
#include "ole2.h"
|
||||||
#include "ole2ver.h"
|
#include "ole2ver.h"
|
||||||
|
@ -41,6 +42,7 @@
|
||||||
#include "wownt32.h"
|
#include "wownt32.h"
|
||||||
#include "wine/unicode.h"
|
#include "wine/unicode.h"
|
||||||
#include "objbase.h"
|
#include "objbase.h"
|
||||||
|
#include "ole32_main.h"
|
||||||
#include "compobj_private.h"
|
#include "compobj_private.h"
|
||||||
|
|
||||||
#include "wine/debug.h"
|
#include "wine/debug.h"
|
||||||
|
@ -62,45 +64,8 @@ const CLSID CLSID_StdGlobalInterfaceTable = { 0x00000323, 0, 0, {0xc0, 0, 0, 0,
|
||||||
|
|
||||||
static void* StdGlobalInterfaceTableInstance;
|
static void* StdGlobalInterfaceTableInstance;
|
||||||
|
|
||||||
/*****************************************************************************
|
APARTMENT MTA, *apts;
|
||||||
* Apartment management stuff
|
static CRITICAL_SECTION csApartment = CRITICAL_SECTION_INIT("csApartment");
|
||||||
*
|
|
||||||
* 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 ApartmentLockCount;
|
|
||||||
} OleApartmentData;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
OleApartmentData *ApartmentData;
|
|
||||||
} OleThreadData;
|
|
||||||
|
|
||||||
/* not jet used
|
|
||||||
static CRITICAL_SECTION csApartmentData = CRITICAL_SECTION_INIT("csApartmentData");
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* the first STA created in a process is the main STA
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* not jet used
|
|
||||||
static OleApartmentData * mainSTA;
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* a Process can only have one MTA
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* not jet used
|
|
||||||
static OleApartmentData * processMTA;
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This lock count counts the number of times CoInitialize is called. It is
|
* This lock count counts the number of times CoInitialize is called. It is
|
||||||
|
@ -150,19 +115,112 @@ typedef struct tagOpenDll {
|
||||||
static CRITICAL_SECTION csOpenDllList = CRITICAL_SECTION_INIT("csOpenDllList");
|
static CRITICAL_SECTION csOpenDllList = CRITICAL_SECTION_INIT("csOpenDllList");
|
||||||
static OpenDll *openDllList = NULL; /* linked list of open dlls */
|
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_Add(HANDLE hLibrary);
|
||||||
static void COMPOBJ_DllList_FreeUnused(int Timeout);
|
static void COMPOBJ_DllList_FreeUnused(int Timeout);
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* Initialize/Uninitialize critical sections.
|
* Initialize/Unitialize threading stuff.
|
||||||
*/
|
*/
|
||||||
void COMPOBJ_InitProcess( void )
|
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 )
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
|
@ -282,12 +340,6 @@ HRESULT WINAPI CoInitialize(
|
||||||
* S_FALSE if this function was called already.
|
* S_FALSE if this function was called already.
|
||||||
* RPC_E_CHANGED_MODE if a previous call to CoInitialize specified another
|
* RPC_E_CHANGED_MODE if a previous call to CoInitialize specified another
|
||||||
* threading model.
|
* 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(
|
HRESULT WINAPI CoInitializeEx(
|
||||||
LPVOID lpReserved, /* [in] pointer to win32 malloc interface
|
LPVOID lpReserved, /* [in] pointer to win32 malloc interface
|
||||||
|
@ -295,7 +347,8 @@ HRESULT WINAPI CoInitializeEx(
|
||||||
DWORD dwCoInit /* [in] A value from COINIT specifies the threading model */
|
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);
|
TRACE("(%p, %x)\n", lpReserved, (int)dwCoInit);
|
||||||
|
|
||||||
|
@ -304,14 +357,9 @@ HRESULT WINAPI CoInitializeEx(
|
||||||
ERR("(%p, %x) - Bad parameter passed-in %p, must be an old Windows Application\n", lpReserved, (int)dwCoInit, lpReserved);
|
ERR("(%p, %x) - Bad parameter passed-in %p, must be an old Windows Application\n", lpReserved, (int)dwCoInit, lpReserved);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
apt = NtCurrentTeb()->ErrorInfo;
|
||||||
* Check for unsupported features.
|
if (apt && dwCoInit != apt->model) return RPC_E_CHANGED_MODE;
|
||||||
*/
|
hr = apt ? S_FALSE : S_OK;
|
||||||
if (dwCoInit!=COINIT_APARTMENTTHREADED)
|
|
||||||
{
|
|
||||||
FIXME(":(%p,%x): unsupported flag %x\n", lpReserved, (int)dwCoInit, (int)dwCoInit);
|
|
||||||
/* Hope for the best and continue anyway */
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check the lock count. If this is the first time going through the initialize
|
* Check the lock count. If this is the first time going through the initialize
|
||||||
|
@ -326,13 +374,15 @@ HRESULT WINAPI CoInitializeEx(
|
||||||
*/
|
*/
|
||||||
TRACE("() - Initializing the COM libraries\n");
|
TRACE("() - Initializing the COM libraries\n");
|
||||||
|
|
||||||
|
COM_InitMTA();
|
||||||
|
|
||||||
RunningObjectTableImpl_Initialize();
|
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;
|
return hr;
|
||||||
}
|
}
|
||||||
|
@ -347,8 +397,18 @@ HRESULT WINAPI CoInitializeEx(
|
||||||
void WINAPI CoUninitialize(void)
|
void WINAPI CoUninitialize(void)
|
||||||
{
|
{
|
||||||
LONG lCOMRefCnt;
|
LONG lCOMRefCnt;
|
||||||
|
APARTMENT *apt;
|
||||||
|
|
||||||
TRACE("()\n");
|
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.
|
* Decrease the reference count.
|
||||||
* If we are back to 0 locks on the COM library, make sure we free
|
* If we are back to 0 locks on the COM library, make sure we free
|
||||||
|
@ -378,6 +438,7 @@ void WINAPI CoUninitialize(void)
|
||||||
*/
|
*/
|
||||||
COM_ExternalLockFreeList();
|
COM_ExternalLockFreeList();
|
||||||
|
|
||||||
|
COM_UninitMTA();
|
||||||
}
|
}
|
||||||
else if (lCOMRefCnt<1) {
|
else if (lCOMRefCnt<1) {
|
||||||
ERR( "CoUninitialize() - not CoInitialized.\n" );
|
ERR( "CoUninitialize() - not CoInitialized.\n" );
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* Copyright 1999 Francis Beaudet
|
* Copyright 1999 Francis Beaudet
|
||||||
* Copyright 1999 Sylvain St-Germain
|
* Copyright 1999 Sylvain St-Germain
|
||||||
* Copyright 2002 Marcus Meissner
|
* Copyright 2002 Marcus Meissner
|
||||||
|
* Copyright 2003 Ove Kåven, TransGaming Technologies
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* 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 */
|
/* All private prototype functions used by OLE will be added to this header file */
|
||||||
|
|
||||||
#include "wtypes.h"
|
#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_Construct();
|
||||||
extern void StdGlobalInterfaceTable_Destroy(void* self);
|
extern void StdGlobalInterfaceTable_Destroy(void* self);
|
||||||
|
@ -121,4 +190,25 @@ int WINAPI FileMonikerImpl_DecomposePath(LPCOLESTR str, LPOLESTR** stringTable);
|
||||||
|
|
||||||
HRESULT WINAPI __CLSIDFromStringA(LPCSTR idstr, CLSID *id);
|
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 */
|
#endif /* __WINE_OLE_COMPOBJ_H */
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
#include "objbase.h"
|
#include "objbase.h"
|
||||||
#include "wine/unicode.h"
|
#include "wine/unicode.h"
|
||||||
#include "thread.h"
|
#include "compobj_private.h"
|
||||||
|
|
||||||
#include "wine/debug.h"
|
#include "wine/debug.h"
|
||||||
|
|
||||||
|
@ -483,13 +483,13 @@ HRESULT WINAPI CreateErrorInfo(ICreateErrorInfo **pperrinfo)
|
||||||
*/
|
*/
|
||||||
HRESULT WINAPI GetErrorInfo(ULONG dwReserved, IErrorInfo **pperrinfo)
|
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 ) return E_INVALIDARG;
|
||||||
if(!(*pperrinfo = (IErrorInfo*)(NtCurrentTeb()->ErrorInfo))) return S_FALSE;
|
if(!(*pperrinfo = (IErrorInfo*)(COM_CurrentInfo()->ErrorInfo))) return S_FALSE;
|
||||||
|
|
||||||
/* clear thread error state */
|
/* clear thread error state */
|
||||||
NtCurrentTeb()->ErrorInfo = NULL;
|
COM_CurrentInfo()->ErrorInfo = NULL;
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,11 +502,11 @@ HRESULT WINAPI SetErrorInfo(ULONG dwReserved, IErrorInfo *perrinfo)
|
||||||
TRACE("(%ld, %p): stub:\n", dwReserved, perrinfo);
|
TRACE("(%ld, %p): stub:\n", dwReserved, perrinfo);
|
||||||
|
|
||||||
/* release old errorinfo */
|
/* release old errorinfo */
|
||||||
pei = (IErrorInfo*)NtCurrentTeb()->ErrorInfo;
|
pei = (IErrorInfo*)COM_CurrentInfo()->ErrorInfo;
|
||||||
if(pei) IErrorInfo_Release(pei);
|
if(pei) IErrorInfo_Release(pei);
|
||||||
|
|
||||||
/* set to new value */
|
/* set to new value */
|
||||||
NtCurrentTeb()->ErrorInfo = perrinfo;
|
COM_CurrentInfo()->ErrorInfo = perrinfo;
|
||||||
if(perrinfo) IErrorInfo_AddRef(perrinfo);
|
if(perrinfo) IErrorInfo_AddRef(perrinfo);
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue