reactos/dll/win32/ole32/git.c

403 lines
10 KiB
C
Raw Normal View History

/*
* Implementation of the StdGlobalInterfaceTable object
*
* The GlobalInterfaceTable (GIT) object is used to marshal interfaces between
* threading apartments (contexts). When you want to pass an interface but not
* as a parameter, it wouldn't get marshalled automatically, so you can use this
* object to insert the interface into a table, and you get back a cookie.
* Then when it's retrieved, it'll be unmarshalled into the right apartment.
*
* Copyright 2003 Mike Hearn <mike@theoretic.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#define COBJMACROS
#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "objbase.h"
#include "ole2.h"
#include "winerror.h"
#include "compobj_private.h"
#include "wine/list.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(ole);
/****************************************************************************
* StdGlobalInterfaceTable definition
*
* This class implements IGlobalInterfaceTable and is a process-wide singleton
* used for marshalling interfaces between threading apartments using cookies.
*/
/* Each entry in the linked list of GIT entries */
typedef struct StdGITEntry
{
DWORD cookie;
IID iid; /* IID of the interface */
IStream* stream; /* Holds the marshalled interface */
struct list entry;
} StdGITEntry;
/* Class data */
typedef struct StdGlobalInterfaceTableImpl
{
IGlobalInterfaceTable IGlobalInterfaceTable_iface;
struct list list;
ULONG nextCookie;
} StdGlobalInterfaceTableImpl;
static IGlobalInterfaceTable *std_git;
static CRITICAL_SECTION git_section;
static CRITICAL_SECTION_DEBUG critsect_debug =
{
0, 0, &git_section,
{ &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
Sync from Wine-20050830 to Wine-0_9_1: Francois Gouget <fgouget@free.fr> - Assorted spelling fixes. - Fix .spec declarations for functinos with 64bit arguments (reported by winapi_check). Mike Hearn <mh@codeweavers.com> - Robert Shearman <rob@codeweavers.com> Change stub manager to track the number of normal marshals instead of using the state machine so that multiple marshals of the same object and interface work correctly. Robert Shearman <rob@codeweavers.com> - Pass WM_QUIT to outer modal loop. - Fix the return codes during unmarshaling so that it returns failure codes instead of S_FALSE returned from IStream_Read. - Don't fail in CoRegisterClassObject if the class already exists and the REGCLS_MULTIPLEUSE flag is specified. - Fix ref-counting rules to match native DCOM Dlls. - Add exception handling for stubs. - Implement OleRegEnumVerbs. - The [string] attribute is only valid on byte, char and wchar_t types, so replace "unsigned short" by "WCHAR". - Implement OleIsRunning. - Add a stubbed out implementation of IAdviseSink and advise the delegate object to use it. - Initialize out pointer to NULL before access check in IStorage_CreateStorage. - WriteClassStg should return E_INVALIDARG if pstg is NULL instead of asserting. - ReadClassStg should return E_INVALIDARG if pstg is NULL instead of crashing and a NULL pclsid should cause it to return the same value. - Make the interfaces that should be supported by the data cache explicit so their is no confusion in this file as to what it should be implementing and what this object should implement. - Delegate some IOleObject methods to the server IOleObject if it is running. - Implement some IRunningObject functions that actually start the server and initialize it. - Remove redunant braces. - Compact multi-line comments that fit into one line. - Remove comments that state the obvious. - Remove extra brackets that are redundant because the -> operator binds tighter than &. - Change "this" to "This" to make code more like other interface implementations. - Remove redundant comparisons with NULL for pointers. - Re-arrange some functions and vtables so we don't have declarations for all of the functions in the file. - Fix a trace to refer to the object ID rather than the legacy MID. - Fix the error case of CoMarshalInterThreadInterface to release the stream. - Move all 16-bit functions to an appropriate 16-bit file. - Implement OLE object notifications, making sure to cope with the case of the advise holder not being created because no notifications are needed. - Implement a Stop function and use this to properly implement IOleObject_Close, IAdviseSink_OnClose and the destructor. - We shouldn't pass the application name into CreateProcess because the value stored in the registry could include arguments. - Extend COM_OpenKeyForCLSID to open a subkey and return an HRESULT. - Fix up the callers and reorganize CoGetClassObject to split out the inproc code into another function. Alex Villacís Lasso <a_villacis@palosanto.com> - Add NULL check for sinkInterface in DataCache_GetAdvise. - Add missing ! to fix a reversed condition check in OleCreateDefaultHandler, in order to match intent in comment. Vincent Béron <vberon@mecano.gme.usherb.ca> - Use the proper calling convention for 2 16-bit functions. Alexandre Julliard <julliard@winehq.org> - We are no longer generating .spec.c files. - Use a more portable scheme for storing the name of a critical section. - Fixed some traces to use the right printf format and avoid typecasts. - Removed unused debug channels. - We are no longer generating .dbg.c files. Richard Cohen <richard@daijobu.co.uk> - Relax the share mode validation for transacted storage, with a test. Eric Pouech <eric.pouech@wanadoo.fr> - Fixes for function prototypes without arguments. Michael Jung <mjung@iss.tu-darmstadt.de> - Added tracing to CoCreateInstance. Markus Amsler <markus.amsler@oribi.org> - Improve c2man Documented-Total count. svn path=/trunk/; revision=19370
2005-11-20 15:01:10 +00:00
0, 0, { (DWORD_PTR)(__FILE__ ": global interface table") }
};
static CRITICAL_SECTION git_section = { &critsect_debug, -1, 0, 0, 0, 0 };
static inline StdGlobalInterfaceTableImpl *impl_from_IGlobalInterfaceTable(IGlobalInterfaceTable *iface)
{
return CONTAINING_RECORD(iface, StdGlobalInterfaceTableImpl, IGlobalInterfaceTable_iface);
}
/***
* A helper function to traverse the list and find the entry that matches the cookie.
* Returns NULL if not found. Must be called inside git_section critical section.
*/
static StdGITEntry* StdGlobalInterfaceTable_FindEntry(StdGlobalInterfaceTableImpl* This,
DWORD cookie)
{
StdGITEntry* e;
TRACE("This=%p, cookie=0x%x\n", This, cookie);
LIST_FOR_EACH_ENTRY(e, &This->list, StdGITEntry, entry) {
if (e->cookie == cookie)
return e;
}
TRACE("Entry not found\n");
return NULL;
}
/***
* Here's the boring boilerplate stuff for IUnknown
*/
static HRESULT WINAPI
StdGlobalInterfaceTable_QueryInterface(IGlobalInterfaceTable* iface,
REFIID riid, void** ppvObject)
{
/* Make sure silly coders can't crash us */
if (ppvObject == 0) return E_INVALIDARG;
*ppvObject = 0; /* assume we don't have the interface */
/* Do we implement that interface? */
if (IsEqualIID(&IID_IUnknown, riid) ||
IsEqualIID(&IID_IGlobalInterfaceTable, riid))
*ppvObject = iface;
else
return E_NOINTERFACE;
/* Now inc the refcount */
IGlobalInterfaceTable_AddRef(iface);
return S_OK;
}
static ULONG WINAPI
StdGlobalInterfaceTable_AddRef(IGlobalInterfaceTable* iface)
{
return 1;
}
static ULONG WINAPI
StdGlobalInterfaceTable_Release(IGlobalInterfaceTable* iface)
{
return 1;
}
/***
* Now implement the actual IGlobalInterfaceTable interface
*/
static HRESULT WINAPI
StdGlobalInterfaceTable_RegisterInterfaceInGlobal(
IGlobalInterfaceTable* iface, IUnknown* pUnk,
REFIID riid, DWORD* pdwCookie)
{
StdGlobalInterfaceTableImpl* const This = impl_from_IGlobalInterfaceTable(iface);
IStream* stream = NULL;
HRESULT hres;
StdGITEntry* entry;
LARGE_INTEGER zero;
TRACE("iface=%p, pUnk=%p, riid=%s, pdwCookie=0x%p\n", iface, pUnk, debugstr_guid(riid), pdwCookie);
if (pUnk == NULL) return E_INVALIDARG;
/* marshal the interface */
TRACE("About to marshal the interface\n");
hres = CreateStreamOnHGlobal(0, TRUE, &stream);
if (hres != S_OK) return hres;
hres = CoMarshalInterface(stream, riid, pUnk, MSHCTX_INPROC, NULL, MSHLFLAGS_TABLESTRONG);
if (hres != S_OK)
{
IStream_Release(stream);
return hres;
}
zero.QuadPart = 0;
IStream_Seek(stream, zero, STREAM_SEEK_SET, NULL);
entry = HeapAlloc(GetProcessHeap(), 0, sizeof(StdGITEntry));
if (!entry)
{
CoReleaseMarshalData(stream);
IStream_Release(stream);
return E_OUTOFMEMORY;
}
EnterCriticalSection(&git_section);
entry->iid = *riid;
entry->stream = stream;
entry->cookie = This->nextCookie;
This->nextCookie++; /* inc the cookie count */
/* insert the new entry at the end of the list */
list_add_tail(&This->list, &entry->entry);
/* and return the cookie */
*pdwCookie = entry->cookie;
LeaveCriticalSection(&git_section);
TRACE("Cookie is 0x%x\n", entry->cookie);
return S_OK;
}
static HRESULT WINAPI
StdGlobalInterfaceTable_RevokeInterfaceFromGlobal(
IGlobalInterfaceTable* iface, DWORD dwCookie)
{
StdGlobalInterfaceTableImpl* This = impl_from_IGlobalInterfaceTable(iface);
StdGITEntry* entry;
HRESULT hr;
TRACE("iface=%p, dwCookie=0x%x\n", iface, dwCookie);
EnterCriticalSection(&git_section);
entry = StdGlobalInterfaceTable_FindEntry(This, dwCookie);
if (entry == NULL) {
TRACE("Entry not found\n");
LeaveCriticalSection(&git_section);
return E_INVALIDARG; /* not found */
}
list_remove(&entry->entry);
LeaveCriticalSection(&git_section);
/* Free the stream */
hr = CoReleaseMarshalData(entry->stream);
if (hr != S_OK)
{
WARN("Failed to release marshal data, hr = 0x%08x\n", hr);
return hr;
}
IStream_Release(entry->stream);
HeapFree(GetProcessHeap(), 0, entry);
return S_OK;
}
static HRESULT WINAPI
StdGlobalInterfaceTable_GetInterfaceFromGlobal(
IGlobalInterfaceTable* iface, DWORD dwCookie,
REFIID riid, void **ppv)
{
StdGlobalInterfaceTableImpl* This = impl_from_IGlobalInterfaceTable(iface);
StdGITEntry* entry;
HRESULT hres;
IStream *stream;
TRACE("dwCookie=0x%x, riid=%s, ppv=%p\n", dwCookie, debugstr_guid(riid), ppv);
EnterCriticalSection(&git_section);
entry = StdGlobalInterfaceTable_FindEntry(This, dwCookie);
if (entry == NULL) {
WARN("Entry for cookie 0x%x not found\n", dwCookie);
LeaveCriticalSection(&git_section);
return E_INVALIDARG;
}
TRACE("entry=%p\n", entry);
hres = IStream_Clone(entry->stream, &stream);
LeaveCriticalSection(&git_section);
if (hres != S_OK) {
WARN("Failed to clone stream with error 0x%08x\n", hres);
return hres;
}
/* unmarshal the interface */
hres = CoUnmarshalInterface(stream, riid, ppv);
IStream_Release(stream);
if (hres != S_OK) {
WARN("Failed to unmarshal stream\n");
return hres;
}
TRACE("ppv=%p\n", *ppv);
return S_OK;
}
/* Classfactory definition - despite what MSDN says, some programs need this */
static HRESULT WINAPI
GITCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid, LPVOID *ppv)
{
*ppv = NULL;
if (IsEqualIID(riid, &IID_IUnknown) ||
IsEqualIID(riid, &IID_IClassFactory))
{
*ppv = iface;
return S_OK;
}
return E_NOINTERFACE;
}
static ULONG WINAPI GITCF_AddRef(LPCLASSFACTORY iface)
{
return 2;
}
static ULONG WINAPI GITCF_Release(LPCLASSFACTORY iface)
{
return 1;
}
static HRESULT WINAPI
GITCF_CreateInstance(LPCLASSFACTORY iface, LPUNKNOWN pUnk,
REFIID riid, LPVOID *ppv)
{
if (IsEqualIID(riid,&IID_IGlobalInterfaceTable)) {
IGlobalInterfaceTable *git = get_std_git();
return IGlobalInterfaceTable_QueryInterface(git, riid, ppv);
}
FIXME("(%s), not supported.\n",debugstr_guid(riid));
return E_NOINTERFACE;
}
static HRESULT WINAPI GITCF_LockServer(LPCLASSFACTORY iface, BOOL fLock)
{
FIXME("(%d), stub!\n",fLock);
return S_OK;
}
Sync to Wine-20050628: Robert Shearman <rob@codeweavers.com> - Convert some registry helper functions to use unicode versions of CLSID & registry functions (untested). - Add registry entries for local-only OLE interfaces. - Change IUnknown to local interface. - IUnknown isn't a remotable interface so the stub manager shouldn't need a marshaller for it. - Change the RPC code to use the unicode versions of the CLSID & registry functions. - Don't disconnect proxies flagged with SORFP_NOLIFETIMEMGMT. It makes no sense and only causes trouble for proxies that depend on these proxies being available. - Change some of the registry helper functions to use the unicode versions of the CLSID & registry functions. - Reindent CoGetClassObject and output an error message if the class isn't registered. - Add tests for the touched functions. Dmitry Timoshkov <dmitry@codeweavers.com> - Make remaining OLE interface vtables const. Richard Cohen <richard@daijobu.co.uk> - Base FileMonikerImpl_Save() on XP. - Correct handling of Unicode strings & multibyte locales. - More error checking. - Change ERR to WARN. - Match Windows quick & dirty estimate for GetSizeMax(). Stefan Huehner <stefan@huehner.org> - Fix some more -Wstrict-prototypes warnings. Mike Hearn <mike@navi.cx> - Add some tracing to the IRemUnknown RpcProxyBuffer implementation. Eric Pouech <pouech-eric@wanadoo.fr> - Const correctness fixes. Richard Cohen <richard@daijobu.co.uk> - IEnum::Clone shouldn't do a Reset. Marcus Meissner <marcus@jet.franken.de> - Removed CLSID_CompositeMoniker (conflicting with static definition). Alexandre Julliard <julliard@winehq.org> - Sort entry points alphabetically. svn path=/trunk/; revision=17036
2005-08-03 22:31:39 +00:00
static const IClassFactoryVtbl GITClassFactoryVtbl = {
GITCF_QueryInterface,
GITCF_AddRef,
GITCF_Release,
GITCF_CreateInstance,
GITCF_LockServer
};
static IClassFactory git_classfactory = { &GITClassFactoryVtbl };
HRESULT StdGlobalInterfaceTable_GetFactory(LPVOID *ppv)
{
*ppv = &git_classfactory;
TRACE("Returning GIT classfactory\n");
return S_OK;
}
/* Virtual function table */
Sync to Wine-20050628: Robert Shearman <rob@codeweavers.com> - Convert some registry helper functions to use unicode versions of CLSID & registry functions (untested). - Add registry entries for local-only OLE interfaces. - Change IUnknown to local interface. - IUnknown isn't a remotable interface so the stub manager shouldn't need a marshaller for it. - Change the RPC code to use the unicode versions of the CLSID & registry functions. - Don't disconnect proxies flagged with SORFP_NOLIFETIMEMGMT. It makes no sense and only causes trouble for proxies that depend on these proxies being available. - Change some of the registry helper functions to use the unicode versions of the CLSID & registry functions. - Reindent CoGetClassObject and output an error message if the class isn't registered. - Add tests for the touched functions. Dmitry Timoshkov <dmitry@codeweavers.com> - Make remaining OLE interface vtables const. Richard Cohen <richard@daijobu.co.uk> - Base FileMonikerImpl_Save() on XP. - Correct handling of Unicode strings & multibyte locales. - More error checking. - Change ERR to WARN. - Match Windows quick & dirty estimate for GetSizeMax(). Stefan Huehner <stefan@huehner.org> - Fix some more -Wstrict-prototypes warnings. Mike Hearn <mike@navi.cx> - Add some tracing to the IRemUnknown RpcProxyBuffer implementation. Eric Pouech <pouech-eric@wanadoo.fr> - Const correctness fixes. Richard Cohen <richard@daijobu.co.uk> - IEnum::Clone shouldn't do a Reset. Marcus Meissner <marcus@jet.franken.de> - Removed CLSID_CompositeMoniker (conflicting with static definition). Alexandre Julliard <julliard@winehq.org> - Sort entry points alphabetically. svn path=/trunk/; revision=17036
2005-08-03 22:31:39 +00:00
static const IGlobalInterfaceTableVtbl StdGlobalInterfaceTableImpl_Vtbl =
{
StdGlobalInterfaceTable_QueryInterface,
StdGlobalInterfaceTable_AddRef,
StdGlobalInterfaceTable_Release,
StdGlobalInterfaceTable_RegisterInterfaceInGlobal,
StdGlobalInterfaceTable_RevokeInterfaceFromGlobal,
StdGlobalInterfaceTable_GetInterfaceFromGlobal
};
IGlobalInterfaceTable* get_std_git(void)
{
if (!std_git)
{
StdGlobalInterfaceTableImpl* newGIT;
newGIT = HeapAlloc(GetProcessHeap(), 0, sizeof(StdGlobalInterfaceTableImpl));
if (!newGIT) return NULL;
newGIT->IGlobalInterfaceTable_iface.lpVtbl = &StdGlobalInterfaceTableImpl_Vtbl;
list_init(&newGIT->list);
newGIT->nextCookie = 0xf100; /* that's where windows starts, so that's where we start */
if (InterlockedCompareExchangePointer((void**)&std_git, &newGIT->IGlobalInterfaceTable_iface, NULL))
{
HeapFree(GetProcessHeap(), 0, newGIT);
}
else
TRACE("Created the GIT at %p\n", newGIT);
}
return std_git;
}
void release_std_git(void)
{
StdGlobalInterfaceTableImpl *git;
StdGITEntry *entry, *entry2;
if (!std_git) return;
git = impl_from_IGlobalInterfaceTable(std_git);
LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &git->list, StdGITEntry, entry)
{
list_remove(&entry->entry);
CoReleaseMarshalData(entry->stream);
IStream_Release(entry->stream);
HeapFree(GetProcessHeap(), 0, entry);
}
HeapFree(GetProcessHeap(), 0, git);
}