mirror of
https://github.com/php/php-src.git
synced 2026-03-24 08:12:21 +01:00
* Fix GH-17658: COMPersistHelper::LoadFromStream() can segfault The actual fix is trivial, but to be able to test the behavior we have to introduce an own COM object, since existing persistable objects likely implement `IPersistInit`, not only `IPersist`. We also want to avoid further test dependencies on possibly unavailable objects, such as `Word.Application`. To this purposes, we add a small COM in-process server, which may be extended for other testing purposes. We keep it simple by implementing it in C++, but without using any more sophisticated frameworks like ATL. This component needs to be built explicitly (`nmake comtest.dll`), and also needs to be explicitly registered (`nmake register_comtest`). When no longer needed, it is possible to unregister the component (`nmake unregister_comtest`).
318 lines
7.6 KiB
C++
318 lines
7.6 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| https://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
| Author: Christoph M. Becker <cmb@php.net> |
|
|
| Based on: <https://thrysoee.dk/InsideCOM+/> |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include "comtest.h" // Generated by MIDL
|
|
|
|
long g_cComponents = 0;
|
|
long g_cLocks = 0;
|
|
|
|
class CDocument : public IDocument, IPersistStream
|
|
{
|
|
public:
|
|
// IUnknown
|
|
STDMETHODIMP_(ULONG) AddRef();
|
|
STDMETHODIMP_(ULONG) Release();
|
|
STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
|
|
|
|
// IDispatch
|
|
STDMETHODIMP GetTypeInfoCount(UINT* pCountTypeInfo);
|
|
STDMETHODIMP GetTypeInfo(UINT iTypeInfo, LCID lcid, ITypeInfo** ppITypeInfo);
|
|
STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId);
|
|
STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams,
|
|
VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr);
|
|
|
|
// IDocument
|
|
STDMETHODIMP get_Content(BSTR* retvalue);
|
|
STDMETHODIMP put_Content(BSTR newvalue);
|
|
|
|
// IPersist
|
|
STDMETHODIMP GetClassID(CLSID *pClassID);
|
|
|
|
// IPersistStream
|
|
STDMETHODIMP IsDirty( void);
|
|
STDMETHODIMP Load(IStream *pStm);
|
|
STDMETHODIMP Save(IStream *pStm, BOOL fClearDirty);
|
|
STDMETHODIMP GetSizeMax(ULARGE_INTEGER *pcbSize);
|
|
|
|
CDocument() : m_content(SysAllocString(L"")), m_cRef(1) { g_cComponents++; }
|
|
~CDocument() { SysFreeString(m_content); g_cComponents--; }
|
|
HRESULT Init(void);
|
|
|
|
private:
|
|
BSTR m_content;
|
|
ULONG m_cRef;
|
|
ITypeInfo* m_pTypeInfo;
|
|
BOOL m_dirty;
|
|
};
|
|
|
|
ULONG CDocument::AddRef()
|
|
{
|
|
return ++m_cRef;
|
|
}
|
|
|
|
ULONG CDocument::Release()
|
|
{
|
|
if (--m_cRef != 0) {
|
|
return m_cRef;
|
|
}
|
|
m_pTypeInfo->Release();
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
HRESULT CDocument::QueryInterface(REFIID riid, void** ppv)
|
|
{
|
|
if (riid == IID_IUnknown) {
|
|
*ppv = static_cast<IDocument*>(this);
|
|
} else if (riid == IID_IDocument) {
|
|
*ppv = static_cast<IDocument*>(this);
|
|
} else if (riid == IID_IDispatch) {
|
|
*ppv = static_cast<IDispatch*>(this);
|
|
} else if (riid == IID_IPersistStream) {
|
|
*ppv = static_cast<IPersistStream*>(this);
|
|
} else {
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDocument::GetTypeInfoCount(UINT* pCountTypeInfo)
|
|
{
|
|
*pCountTypeInfo = 1;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDocument::GetTypeInfo(UINT iTypeInfo, LCID lcid, ITypeInfo** ppITypeInfo)
|
|
{
|
|
(void) lcid;
|
|
*ppITypeInfo = NULL;
|
|
if (iTypeInfo != 0) {
|
|
return DISP_E_BADINDEX;
|
|
}
|
|
m_pTypeInfo->AddRef();
|
|
*ppITypeInfo = m_pTypeInfo;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDocument::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId)
|
|
{
|
|
(void) lcid;
|
|
if (riid != IID_NULL) {
|
|
return DISP_E_UNKNOWNINTERFACE;
|
|
}
|
|
return DispGetIDsOfNames(m_pTypeInfo, rgszNames, cNames, rgDispId);
|
|
}
|
|
|
|
HRESULT CDocument::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams,
|
|
VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr)
|
|
{
|
|
(void) lcid;
|
|
if (riid != IID_NULL) {
|
|
return DISP_E_UNKNOWNINTERFACE;
|
|
}
|
|
return DispInvoke(this, m_pTypeInfo, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
|
|
}
|
|
|
|
HRESULT CDocument::get_Content(BSTR* retvalue)
|
|
{
|
|
*retvalue = SysAllocString(m_content);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDocument::put_Content(BSTR newvalue)
|
|
{
|
|
SysFreeString(m_content);
|
|
m_content = SysAllocString(newvalue);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDocument::Init(void)
|
|
{
|
|
ITypeLib* pTypeLib;
|
|
if (FAILED(LoadRegTypeLib(LIBID_ComTest, 1, 0, LANG_NEUTRAL, &pTypeLib))) {
|
|
return E_FAIL;
|
|
}
|
|
HRESULT hr = pTypeLib->GetTypeInfoOfGuid(IID_IDocument, &m_pTypeInfo);
|
|
if (FAILED(hr)) {
|
|
pTypeLib->Release();
|
|
return hr;
|
|
}
|
|
|
|
pTypeLib->Release();
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDocument::GetClassID(CLSID *pClassID)
|
|
{
|
|
*pClassID = CLSID_Document;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDocument::IsDirty(void)
|
|
{
|
|
return m_dirty ? S_OK : S_FALSE;
|
|
}
|
|
|
|
HRESULT CDocument::Load(IStream *pStm)
|
|
{
|
|
ULONG read = 0;
|
|
ULONG cbSize;
|
|
|
|
if (FAILED(pStm->Read(&cbSize, sizeof cbSize, &read))) {
|
|
return S_FALSE;
|
|
}
|
|
if (!SysReAllocStringLen(&m_content, NULL, cbSize)) {
|
|
return S_FALSE;
|
|
}
|
|
if (FAILED(pStm->Read(m_content, cbSize, &read))) {
|
|
// we may have garbage in m_content, but don't mind
|
|
return S_FALSE;
|
|
}
|
|
m_dirty = FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDocument::Save(IStream *pStm, BOOL fClearDirty)
|
|
{
|
|
ULONG written = 0;
|
|
ULONG cbSize = SysStringByteLen(m_content);
|
|
HRESULT hr;
|
|
if (FAILED(hr = pStm->Write(&cbSize, sizeof cbSize, &written))) {
|
|
return S_FALSE;
|
|
}
|
|
if (FAILED(hr = pStm->Write(m_content, cbSize, &written))) {
|
|
return S_FALSE;
|
|
}
|
|
|
|
if (fClearDirty) {
|
|
m_dirty = FALSE;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDocument::GetSizeMax(ULARGE_INTEGER *pcbSize)
|
|
{
|
|
(*pcbSize).QuadPart = sizeof(ULONG) + SysStringByteLen(m_content);
|
|
return S_OK;
|
|
}
|
|
|
|
class CDocumentFactory : public IClassFactory
|
|
{
|
|
public:
|
|
// IUnknown
|
|
STDMETHODIMP_(ULONG) AddRef();
|
|
STDMETHODIMP_(ULONG) Release();
|
|
STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
|
|
|
|
// IClassFactory
|
|
STDMETHODIMP CreateInstance(IUnknown* pUnknownOuter, REFIID riid, void** ppv);
|
|
STDMETHODIMP LockServer(BOOL bLock);
|
|
|
|
CDocumentFactory() : m_cRef(1) { g_cLocks++; }
|
|
~CDocumentFactory() { g_cLocks--; }
|
|
|
|
private:
|
|
ULONG m_cRef;
|
|
};
|
|
|
|
ULONG CDocumentFactory::AddRef()
|
|
{
|
|
return ++m_cRef;
|
|
}
|
|
|
|
ULONG CDocumentFactory::Release()
|
|
{
|
|
if (--m_cRef != 0) {
|
|
return m_cRef;
|
|
}
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
HRESULT CDocumentFactory::QueryInterface(REFIID riid, void** ppv)
|
|
{
|
|
if (riid == IID_IUnknown) {
|
|
*ppv = (IUnknown*)this;
|
|
} else if (riid == IID_IClassFactory) {
|
|
*ppv = (IClassFactory*)this;
|
|
} else {
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDocumentFactory::CreateInstance(IUnknown *pUnknownOuter, REFIID riid, void** ppv)
|
|
{
|
|
if (pUnknownOuter != NULL) {
|
|
return CLASS_E_NOAGGREGATION;
|
|
}
|
|
|
|
CDocument *pDocument = new CDocument;
|
|
if (pDocument == NULL) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
HRESULT hr;
|
|
if (FAILED(hr = pDocument->Init())) {
|
|
return hr;
|
|
}
|
|
hr = pDocument->QueryInterface(riid, ppv);
|
|
pDocument->Release();
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CDocumentFactory::LockServer(BOOL bLock)
|
|
{
|
|
if (bLock) {
|
|
g_cLocks++;
|
|
} else {
|
|
g_cLocks --;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT __stdcall DllCanUnloadNow()
|
|
{
|
|
if (g_cLocks == 0) {
|
|
return S_OK;
|
|
} else {
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
|
|
HRESULT __stdcall DllGetClassObject(REFCLSID clsid, REFIID riid, void** ppv)
|
|
{
|
|
if (clsid != CLSID_Document) {
|
|
return CLASS_E_CLASSNOTAVAILABLE;
|
|
}
|
|
|
|
CDocumentFactory* pFactory = new CDocumentFactory;
|
|
if (pFactory == NULL) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// riid is probably IID_IClassFactory.
|
|
HRESULT hr = pFactory->QueryInterface(riid, ppv);
|
|
pFactory->Release();
|
|
return hr;
|
|
}
|