Copying a virtual file to the clipboard - c++

By slightly modifying the code from the Raymond Chen's article, I got a class for copying a vector to the clipboard as a file:
typedef std::wstring String;
class CFileDataObject : public IDataObject
{
public:
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
IUnknown *punk = NULL;
if (riid == IID_IUnknown)
punk = static_cast<IUnknown*>(this);
else if (riid == IID_IDataObject)
{
punk = static_cast<IDataObject*>(this);
}
*ppv = punk;
if (punk)
{
punk->AddRef();
return S_OK;
}
else return E_NOINTERFACE;
};
STDMETHODIMP_(ULONG) AddRef()
{
return ++m_cRef;
};
STDMETHODIMP_(ULONG) Release()
{
ULONG cRef = --m_cRef;
if (cRef == 0)
{
delete this;
}
return cRef;
}
// IDataObject
STDMETHODIMP GetData(FORMATETC *pfe, STGMEDIUM *pmed)
{
ZeroMemory(pmed, sizeof(*pmed));
switch (GetDataIndex(pfe))
{
case DATA_FILEGROUPDESCRIPTOR:
{
FILEGROUPDESCRIPTOR fgd;
ZeroMemory(&fgd, sizeof(fgd));
fgd.cItems = 1;
fgd.fgd[0].dwFlags = FD_FILESIZE | FD_WRITESTIME;
fgd.fgd[0].nFileSizeLow = DataSize & 0xFFFFFFFFULL;
fgd.fgd[0].nFileSizeHigh = (DataSize & 0xFFFFFFFF00000000ULL) >> 32;
fgd.fgd[0].ftLastWriteTime.dwLowDateTime = 0x256d4000;
fgd.fgd[0].ftLastWriteTime.dwHighDateTime = 0x01bf53eb;
StringCchCopy(fgd.fgd[0].cFileName,ARRAYSIZE(fgd.fgd[0].cFileName),
m_FileName.c_str());
pmed->tymed = TYMED_HGLOBAL;
return CreateHGlobalFromBlob(&fgd, sizeof(fgd),GMEM_MOVEABLE, &pmed->hGlobal);
}
case DATA_FILECONTENTS:
pmed->tymed = TYMED_HGLOBAL;
pmed->hGlobal = hData;
return S_OK;
}
return DV_E_FORMATETC;
};
STDMETHODIMP GetDataHere(FORMATETC *pfe, STGMEDIUM *pmed)
{
return E_NOTIMPL;
};
STDMETHODIMP QueryGetData(FORMATETC *pfe)
{
return GetDataIndex(pfe) == DATA_INVALID ? S_FALSE : S_OK;
};
STDMETHODIMP GetCanonicalFormatEtc(FORMATETC *pfeIn,FORMATETC *pfeOut)
{
*pfeOut = *pfeIn;
pfeOut->ptd = NULL;
return DATA_S_SAMEFORMATETC;
};
STDMETHODIMP SetData(FORMATETC *pfe, STGMEDIUM *pmed,BOOL fRelease)
{
return E_NOTIMPL;
};
STDMETHODIMP EnumFormatEtc(DWORD dwDirection,LPENUMFORMATETC *ppefe)
{
if (dwDirection == DATADIR_GET)
return SHCreateStdEnumFmtEtc(ARRAYSIZE(m_rgfe), m_rgfe, ppefe);
*ppefe = NULL;
return E_NOTIMPL;
}
STDMETHODIMP DAdvise(FORMATETC *pfe, DWORD grfAdv,IAdviseSink *pAdvSink, DWORD *pdwConnection)
{
return OLE_E_ADVISENOTSUPPORTED;
};
STDMETHODIMP DUnadvise(DWORD dwConnection)
{
return OLE_E_ADVISENOTSUPPORTED;
};
STDMETHODIMP EnumDAdvise(LPENUMSTATDATA *ppefe)
{
return OLE_E_ADVISENOTSUPPORTED;
};
CFileDataObject(const String& FileName, const std::vector<uint8_t>& Data)
: m_cRef(1),m_FileName(FileName)
{
SetFORMATETC(&m_rgfe[DATA_FILEGROUPDESCRIPTOR],
RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR));
SetFORMATETC(&m_rgfe[DATA_FILECONTENTS],
RegisterClipboardFormat(CFSTR_FILECONTENTS),TYMED_HGLOBAL, 0);
HRESULT hres = CreateHGlobalFromBlob(Data.data(), Data.size(), GMEM_MOVEABLE, &hData);
if (!SUCCEEDED(hres)) { /* Some error handling goes here */ };
DataSize = Data.size();
}
private:
enum {
DATA_FILEGROUPDESCRIPTOR,
DATA_FILECONTENTS,
DATA_NUM,
DATA_INVALID = -1,
};
int GetDataIndex(const FORMATETC *pfe)
{
for (int i = 0; i < ARRAYSIZE(m_rgfe); i++)
{
if (pfe->cfFormat == m_rgfe[i].cfFormat && (pfe->tymed & m_rgfe[i].tymed) &&
pfe->dwAspect == m_rgfe[i].dwAspect && pfe->lindex == m_rgfe[i].lindex)
return i;
}
return DATA_INVALID;
}
private:
ULONG m_cRef;
FORMATETC m_rgfe[DATA_NUM];
String m_FileName;
HGLOBAL hData = 0;
size_t DataSize = 0;
};
Usage:
std::vector<uint8_t> FileData;
// Filling the vector with some data
IDataObject* fdo = new CFileDataObject(FileName, FileData);
if (fdo)
{
HRESULT hres = OleSetClipboard(fdo);
fdo->Release();
}
The class serves its purpose, but there is one detail: the file can only be pasted only once. All further attempts to paste the file fail. Is this normal or is there something wrong with the class?

Your GetData() is returning the original hData object for DATA_FILECONTENTS instead of a copy. Since the pmed->pUnkForRelease field is being returned as NULL, the caller of GetData() will free the returned HGLOBAL when done using it.
https://learn.microsoft.com/en-us/windows/win32/api/objidl/ns-objidl-ustgmedium-r1
pUnkForRelease
Pointer to an interface instance that allows the sending process to control the way the storage is released when the receiving process calls the ReleaseStgMedium function. If pUnkForRelease is NULL, ReleaseStgMedium uses default procedures to release the storage; otherwise, ReleaseStgMedium uses the specified IUnknown interface.
https://learn.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-releasestgmedium
The provider indicates that the receiver of the medium is responsible for freeing the medium by specifying NULL for the punkForRelease structure member. Then the receiver calls ReleaseStgMedium, which makes a call as described in the following table depending on the type of storage medium being freed.
Medium
ReleaseStgMedium Action
TYMED_HGLOBAL
Calls the GlobalFree function on the handle.
So, to keep that from happening, set pmed->pUnkForRelease to an AddRef'ed IUnknown that is responsible for freeing hData when noone is using it anymore. In your case, you can use the this pointer for your CFileDataObject object for that IUnknown interface.
When the original provider of the medium is responsible for freeing the medium, the provider calls ReleaseStgMedium, specifying the medium and the appropriate IUnknown pointer as the punkForRelease structure member. Depending on the type of storage medium being freed, one of the following actions is taken, followed by a call to the IUnknown::Release method on the specified IUnknown  pointer.
Medium
ReleaseStgMedium Action
TYMED_HGLOBAL
None.
Otherwise, another option is to implement DATA_FILECONTENTS as an IStream instead of an HGLOBAL, where the IStream accesses the original vector data. This option is even described in the Shell Clipboard Formats documentation:
CFSTR_FILECONTENTS
This format identifier is used with the CFSTR_FILEDESCRIPTOR format to transfer data as if it were a file, regardless of how it is actually stored. The data consists of an STGMEDIUM structure that represents the contents of one file. The file is normally represented as a stream object, which avoids having to place the contents of the file in memory. In that case, the tymed member of the STGMEDIUM structure is set to TYMED_ISTREAM, and the file is represented by an IStream interface. The file can also be a storage or global memory object (TYMED_ISTORAGE or TYMED_HGLOBAL). The associated CFSTR_FILEDESCRIPTOR format contains a FILEDESCRIPTOR structure for each file that specifies the file's name and attributes.

Related

C++ - DirectWrite: Load font from file at runtime

I'm trying to load a font from a file at runtime and display it with DirectWrite. The following code should initialize a IDWriteTextFormat object with that font:
hr = pDWriteFactory->CreateTextFormat(
L"Font Family", // Font family name
NULL, // Font collection (NULL sets it to use the system font collection)
// Somehow, a custom font collection should be used here, but I don't know how to do that
DWRITE_FONT_WEIGHT_REGULAR,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
16.0f,
L"en-us",
&pTextFormat // IDWriteTextFormat object
);
It works perfectly with system fonts but I don't know how to load a custom font file. I would appreciate any examples on how to achieve that.
What I tried so far:
• I read this article and this question on Stackoverflow but I don't see where one should pass the file path of the font file (therefore, I completely failed to implement any of the code provided on those two pages)
• I also read this article but if I'm right, it has nothing to do with DirectWrite (?) (I tried to implement the AddFontResourceEx-method, but to no avail)
• Finally, the answer to this question suggested using ResourceFontContext::CreateFontCollection could solve the problem but after reading this page (it's in German but the screenshot is in English) I believe that it can only be used with fonts embedded as resources (which is not an option in my case)
Sure, it's possible to do that. You'll need to:
implement IDWriteFontCollectionLoader interface in your code;
You should also implement IDWriteFontFileEnumerator obviously, but this should be trivial.
register loader with the factory using RegisterFontCollectionLoader
create a collection using CreateCustomFontCollection and same factory;
pass it to CreateTextFormat called on same factory.
When you call CreateCustomFontCollection you have to provide a collection key and its size, it's a blob only meaningful to you, could be anything. Later on your loader will be called with this key, so you can identify it. For example you can use a string 'myspecialkey' as a key, and in CreateEnumeratorFromKey check if it's called with that key, rejecting any other key.
If you only wanted to create a fontface object from a path to a file, you don't need any of above, just use CreateFontFileReference followed by CreateFontFace.
If anyone is interested in the code which finally worked:
Add Common.h, FontLoader.h and FontLoader.cpp to your project (code is given below) and add the following lines to your application:
#include "FontLoader.h"
// ...
IDWriteFactory* pDWriteFactory;
IDWriteFontCollection *fCollection;
IDWriteTextFormat* pTextFormat;
// ...
MFFontContext fContext(pDWriteFactory);
std::vector<std::wstring> filePaths; // vector containing ABSOLUTE file paths of the font files which are to be added to the collection
std::wstring fontFileFilePath = L"C:\\xyz\\abc.ttf";
filePaths.push_back(fontFileFilePath);
HRESULT hr = fContext.CreateFontCollection(filePaths, &fCollection); // create custom font collection
hr = pDWriteFactory->CreateTextFormat(
L"Font Family", // Font family name
fCollection,
DWRITE_FONT_WEIGHT_REGULAR,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
16.0f,
L"en-us",
&pTextFormat // IDWriteTextFormat object
);
FontLoader.h
#pragma once
#include <string>
#include "Common.h"
typedef std::vector<std::wstring> MFCollection;
class MFFontCollectionLoader : public IDWriteFontCollectionLoader
{
public:
MFFontCollectionLoader() : refCount_(0)
{
}
// IUnknown methods
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject);
virtual ULONG STDMETHODCALLTYPE AddRef();
virtual ULONG STDMETHODCALLTYPE Release();
// IDWriteFontCollectionLoader methods
virtual HRESULT STDMETHODCALLTYPE CreateEnumeratorFromKey(
IDWriteFactory* factory,
void const* collectionKey, // [collectionKeySize] in bytes
UINT32 collectionKeySize,
OUT IDWriteFontFileEnumerator** fontFileEnumerator
);
// Gets the singleton loader instance.
static IDWriteFontCollectionLoader* GetLoader()
{
return instance_;
}
static bool IsLoaderInitialized()
{
return instance_ != NULL;
}
private:
ULONG refCount_;
static IDWriteFontCollectionLoader* instance_;
};
class MFFontFileEnumerator : public IDWriteFontFileEnumerator
{
public:
MFFontFileEnumerator(
IDWriteFactory* factory
);
HRESULT Initialize(
UINT const* collectionKey, // [resourceCount]
UINT32 keySize
);
~MFFontFileEnumerator()
{
SafeRelease(&currentFile_);
SafeRelease(&factory_);
}
// IUnknown methods
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject);
virtual ULONG STDMETHODCALLTYPE AddRef();
virtual ULONG STDMETHODCALLTYPE Release();
// IDWriteFontFileEnumerator methods
virtual HRESULT STDMETHODCALLTYPE MoveNext(OUT BOOL* hasCurrentFile);
virtual HRESULT STDMETHODCALLTYPE GetCurrentFontFile(OUT IDWriteFontFile** fontFile);
private:
ULONG refCount_;
IDWriteFactory* factory_;
IDWriteFontFile* currentFile_;
std::vector<std::wstring> filePaths_;
size_t nextIndex_;
};
class MFFontContext
{
public:
MFFontContext(IDWriteFactory *pFactory);
~MFFontContext();
HRESULT Initialize();
HRESULT CreateFontCollection(
MFCollection &newCollection,
OUT IDWriteFontCollection** result
);
private:
// Not copyable or assignable.
MFFontContext(MFFontContext const&);
void operator=(MFFontContext const&);
HRESULT InitializeInternal();
IDWriteFactory *g_dwriteFactory;
static std::vector<unsigned int> cKeys;
// Error code from Initialize().
HRESULT hr_;
};
class MFFontGlobals
{
public:
MFFontGlobals() {}
static unsigned int push(MFCollection &addCollection)
{
unsigned int ret = fontCollections.size();
fontCollections.push_back(addCollection);
return ret;
}
static std::vector<MFCollection>& collections()
{
return fontCollections;
}
private:
static std::vector<MFCollection> fontCollections;
};
FontLoader.cpp
#include "FontLoader.h"
IDWriteFontCollectionLoader* MFFontCollectionLoader::instance_(
new(std::nothrow) MFFontCollectionLoader()
);
HRESULT STDMETHODCALLTYPE MFFontCollectionLoader::QueryInterface(REFIID iid, void** ppvObject)
{
if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontCollectionLoader))
{
*ppvObject = this;
AddRef();
return S_OK;
}
else
{
*ppvObject = NULL;
return E_NOINTERFACE;
}
}
ULONG STDMETHODCALLTYPE MFFontCollectionLoader::AddRef()
{
return InterlockedIncrement(&refCount_);
}
ULONG STDMETHODCALLTYPE MFFontCollectionLoader::Release()
{
ULONG newCount = InterlockedDecrement(&refCount_);
if (newCount == 0)
delete this;
return newCount;
}
HRESULT STDMETHODCALLTYPE MFFontCollectionLoader::CreateEnumeratorFromKey(
IDWriteFactory* factory,
void const* collectionKey, // [collectionKeySize] in bytes
UINT32 collectionKeySize,
OUT IDWriteFontFileEnumerator** fontFileEnumerator
)
{
*fontFileEnumerator = NULL;
HRESULT hr = S_OK;
if (collectionKeySize % sizeof(UINT) != 0)
return E_INVALIDARG;
MFFontFileEnumerator* enumerator = new(std::nothrow) MFFontFileEnumerator(
factory
);
if (enumerator == NULL)
return E_OUTOFMEMORY;
UINT const* mfCollectionKey = static_cast<UINT const*>(collectionKey);
UINT32 const mfKeySize = collectionKeySize;
hr = enumerator->Initialize(
mfCollectionKey,
mfKeySize
);
if (FAILED(hr))
{
delete enumerator;
return hr;
}
*fontFileEnumerator = SafeAcquire(enumerator);
return hr;
}
// ------------------------------ MFFontFileEnumerator ----------------------------------------------------------
MFFontFileEnumerator::MFFontFileEnumerator(
IDWriteFactory* factory
) :
refCount_(0),
factory_(SafeAcquire(factory)),
currentFile_(),
nextIndex_(0)
{
}
HRESULT MFFontFileEnumerator::Initialize(
UINT const* collectionKey, // [resourceCount]
UINT32 keySize
)
{
try
{
// dereference collectionKey in order to get index of collection in MFFontGlobals::fontCollections vector
UINT cPos = *collectionKey;
for (MFCollection::iterator it = MFFontGlobals::collections()[cPos].begin(); it != MFFontGlobals::collections()[cPos].end(); ++it)
{
filePaths_.push_back(it->c_str());
}
}
catch (...)
{
return ExceptionToHResult();
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE MFFontFileEnumerator::QueryInterface(REFIID iid, void** ppvObject)
{
if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileEnumerator))
{
*ppvObject = this;
AddRef();
return S_OK;
}
else
{
*ppvObject = NULL;
return E_NOINTERFACE;
}
}
ULONG STDMETHODCALLTYPE MFFontFileEnumerator::AddRef()
{
return InterlockedIncrement(&refCount_);
}
ULONG STDMETHODCALLTYPE MFFontFileEnumerator::Release()
{
ULONG newCount = InterlockedDecrement(&refCount_);
if (newCount == 0)
delete this;
return newCount;
}
HRESULT STDMETHODCALLTYPE MFFontFileEnumerator::MoveNext(OUT BOOL* hasCurrentFile)
{
HRESULT hr = S_OK;
*hasCurrentFile = FALSE;
SafeRelease(&currentFile_);
if (nextIndex_ < filePaths_.size())
{
hr = factory_->CreateFontFileReference(
filePaths_[nextIndex_].c_str(),
NULL,
&currentFile_
);
if (SUCCEEDED(hr))
{
*hasCurrentFile = TRUE;
++nextIndex_;
}
}
return hr;
}
HRESULT STDMETHODCALLTYPE MFFontFileEnumerator::GetCurrentFontFile(OUT IDWriteFontFile** fontFile)
{
*fontFile = SafeAcquire(currentFile_);
return (currentFile_ != NULL) ? S_OK : E_FAIL;
}
// ---------------------------------------- MFFontContext ---------------------------------------------------------
MFFontContext::MFFontContext(IDWriteFactory *pFactory) : hr_(S_FALSE), g_dwriteFactory(pFactory)
{
}
MFFontContext::~MFFontContext()
{
g_dwriteFactory->UnregisterFontCollectionLoader(MFFontCollectionLoader::GetLoader());
}
HRESULT MFFontContext::Initialize()
{
if (hr_ == S_FALSE)
{
hr_ = InitializeInternal();
}
return hr_;
}
HRESULT MFFontContext::InitializeInternal()
{
HRESULT hr = S_OK;
if (!MFFontCollectionLoader::IsLoaderInitialized())
{
return E_FAIL;
}
// Register our custom loader with the factory object.
hr = g_dwriteFactory->RegisterFontCollectionLoader(MFFontCollectionLoader::GetLoader());
return hr;
}
HRESULT MFFontContext::CreateFontCollection(
MFCollection &newCollection,
OUT IDWriteFontCollection** result
)
{
*result = NULL;
HRESULT hr = S_OK;
// save new collection in MFFontGlobals::fontCollections vector
UINT collectionKey = MFFontGlobals::push(newCollection);
cKeys.push_back(collectionKey);
const void *fontCollectionKey = &cKeys.back();
UINT32 keySize = sizeof(collectionKey);
hr = Initialize();
if (FAILED(hr))
return hr;
hr = g_dwriteFactory->CreateCustomFontCollection(
MFFontCollectionLoader::GetLoader(),
fontCollectionKey,
keySize,
result
);
return hr;
}
std::vector<unsigned int> MFFontContext::cKeys = std::vector<unsigned int>(0);
// ----------------------------------- MFFontGlobals ---------------------------------------------------------
std::vector<MFCollection> MFFontGlobals::fontCollections = std::vector<MFCollection>(0);
Common.h
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
//
//----------------------------------------------------------------------------
#pragma once
// The following macros define the minimum required platform. The minimum required platform
// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run
// your application. The macros work by enabling all features available on platform versions up to and
// including the version specified.
// Modify the following defines if you have to target a platform prior to the ones specified below.
// Refer to MSDN for the latest info on corresponding values for different platforms.
#ifndef WINVER // Minimum platform is Windows 7
#define WINVER 0x0601
#endif
#ifndef _WIN32_WINNT // Minimum platform is Windows 7
#define _WIN32_WINNT 0x0601
#endif
#ifndef _WIN32_WINDOWS // Minimum platform is Windows 7
#define _WIN32_WINDOWS 0x0601
#endif
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#ifndef UNICODE
#define UNICODE
#endif
// Windows header files
#include <windows.h>
#include <dwrite.h>
#include <d2d1.h>
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <memory>
#include <vector>
// Ignore unreferenced parameters, since they are very common
// when implementing callbacks.
#pragma warning(disable : 4100)
////////////////////////////////////////
// COM inheritance helpers.
// Releases a COM object and nullifies pointer.
template <typename InterfaceType>
inline void SafeRelease(InterfaceType** currentObject)
{
if (*currentObject != NULL)
{
(*currentObject)->Release();
*currentObject = NULL;
}
}
// Acquires an additional reference, if non-null.
template <typename InterfaceType>
inline InterfaceType* SafeAcquire(InterfaceType* newObject)
{
if (newObject != NULL)
newObject->AddRef();
return newObject;
}
// Sets a new COM object, releasing the old one.
template <typename InterfaceType>
inline void SafeSet(InterfaceType** currentObject, InterfaceType* newObject)
{
SafeAcquire(newObject);
SafeRelease(&currentObject);
currentObject = newObject;
}
// Maps exceptions to equivalent HRESULTs,
inline HRESULT ExceptionToHResult() throw()
{
try
{
throw; // Rethrow previous exception.
}
catch (std::bad_alloc&)
{
return E_OUTOFMEMORY;
}
catch (...)
{
return E_FAIL;
}
}
The code is certainly not perfect but it worked for me.
If you have any questions concerning this code just leave a comment (even though I probably won't be able to give a satisfying answer anymore). Note that I copied large parts of the code from here.

Advise of Connection Point calls QueryInterface with IID_IMarshal

I am trying to build Connection Points in existing COM component. I derive the class from CCmdTarget and call MFC macros in class declaration and implementation as described in MSDN. Component is correctly build, linked, and run. However, when I debug my client application I see the following issue:
Client locates the correct component, finds the connection point container and appropriate connection point; however Advise method fails to establish connection.
It is assumed that this Advise calls the QueryInterface with IID__IAdHocPresenceEvents.
Do not understand why this calls the QueryInterface with IID_IMarshal.
Later this becomes reason for not getting event at ClientSink::OnAdHocPresenceQuery
So, please help me to resolve this issue or give a hint where else to look for the issue.
Thanks in advance,
Hovo
class ClientSink : public _IAdHocPresenceEvents
{
private:
DWORD m_dwRefCount;
public:
ClientSink();
virtual ~CClientSink();
STDMETHODIMP OnAdHocPresenceQuery(int Result)
{
CString strTemp ("OnAdHocPresenceQuery");
AfxMessageBox(strTemp);
return S_OK;
};
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject)
{
HRESULT hr = S_OK;
if (iid == IID_IUnknown)
{
m_dwRefCount++;
*ppvObject = (IUnknown *)this;
}
else if (iid == IID__IAdHocPresenceEvents)
{
m_dwRefCount++;
*ppvObject = (_IAdHocPresenceEvents *)this;
}
else
{
*ppvObject = NULL;
hr = E_NOINTERFACE;
}
return hr;
}
ULONG STDMETHODCALLTYPE AddRef()
{
m_dwRefCount++;
return m_dwRefCount;
}
ULONG STDMETHODCALLTYPE Release()
{
ULONG l;
l = m_dwRefCount--;
if ( 0 == m_dwRefCount)
{
delete this;
}
return l;
}
};
int SendRequest()
{
CLSID clientCLSID;
if (FAILED(CLSIDFromProgID(A2BSTR("SMC.SMCLink"), &clientCLSID)))
{
clientCLSID = SMC::CLSID_SMCLink;
}
LPUNKNOWN lpUnk;
SMC::ISMCLink* m_pSMCLink = NULL;
if (m_pSMCLink == NULL)
{
if (GetActiveObject(clientCLSID, NULL, &lpUnk) == NOERROR)
{
hr = lpUnk->QueryInterface(SMC::IID_ISMCLink, (LPVOID*)&m_pSMCLink);
lpUnk->Release();
}
}
if(hr != S_OK)
{
return -1;
}
DWORD dwAdvise = 0; //variable,shown here for completeness
IConnectionPoint *pCntPoint = NULL;
IConnectionPointContainer *pConnPtContainer = NULL;
//check if this interface supports connectible objects
hr = m_pSMCLink->QueryInterface(IID_IConnectionPointContainer,(void **)&pConnPtContainer);
if ( !SUCCEEDED(hr) )
{
return hr;
}
// find the specific connection point
hr = pConnPtContainer->FindConnectionPoint(IID__IAdHocPresenceEvents, &pCntPoint);
if ( !SUCCEEDED(hr) )
{
return hr;
}
//we are done with the connection point container interface
pConnPtContainer->Release();
IUnknown *pSinkUnk = NULL;
CSink *pSink = NULL;
pSink = new CSink;
if ( NULL == pSink )
{
return E_FAIL;
}
//Get the pointer to CSink's IUnknown pointer
hr = pSink->QueryInterface (IID_IUnknown,(void **)&pSinkUnk);
if ( !SUCCEEDED(hr) )
{
return hr;
}
// It is assumed that this should call the QueryInterface with IID__IAdHocPresenceEvents
// Do not understand why this calls the QueryInterface with IID_IMarshal
// Later this becomes reason for not getting event at ClientSink::OnAdHocPresenceQuery
hr = pCntPoint->Advise(pSinkUnk, &dwAdvise);
if ( !SUCCEEDED(hr) )
{
return hr;
}
hr = m_pSMCLink->RequestService(BSTR("hov#moco"));
pCntPoint->Unadvise(dwAdvise); //disconnect from server
pCntPoint->Release();
return hr;
}
[
helpstring("Interface to control My Product"),
uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx),
dual,
oleautomation
]
interface ISMCLink : IDispatch
{
// ...
HRESULT RequestService([in] BSTR user);
// ...
};
// Primary dispatch interface for My Component
[ uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) ]
dispinterface IDispSMCLink
{
interface ISMCLink;
};
[
helpstring("ISMCLink2, Interface to access My Product"),
uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx),
dual,
oleautomation
]
interface ISMCLink2 : ISMCLink
{
//..
};
[ uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx), version(1.0) ]
library SMC
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
[
uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx),
helpstring("_IAdHocPresenceEvents Interface")
]
interface _IAdHocPresenceEvents : IUnknown
{
[id(1), helpstring("method OnAdHocPresenceQuery")] HRESULT OnAdHocPresenceQuery(int Result);
};
#include "ISMCLink.idl"
[ uuid(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) ]
coclass SMCLink
{
dispinterface IDispSMCLink;
[default] interface ISMCLink;
interface ISMCLink2;
[default, source] interface _IAdHocPresenceEvents;
};
};
class SMCLink : public CCmdTarget
{
protected:
DECLARE_MESSAGE_MAP()
DECLARE_DISPATCH_MAP()
DECLARE_INTERFACE_MAP()
// Connection point for ISMCLink interface
BEGIN_CONNECTION_PART(SMCLink, AdHocPresenceEvents)
CONNECTION_IID(IID__IAdHocPresenceEvents)
END_CONNECTION_PART(AdHocPresenceEvents)
DECLARE_CONNECTION_MAP()
public:
SMCLink(void);
virtual ~SMCLink(void);
// to be OLE creatable, it must be DYNCREATE and OLECREATE
DECLARE_DYNCREATE(SMCLink)
DECLARE_OLECREATE(SMCLink)
// Generated OLE dispatch map functions
//{{AFX_DISPATCH(SMCLink)
afx_msg HRESULT RequestAdHocPresence(BSTR sipAddr);
//}}AFX_DISPATCH
BEGIN_DUAL_INTERFACE_PART(DualSMCLink, ISMCLink)
//..
STDMETHOD(RequestService)(THIS_ BSTR user);
END_DUAL_INTERFACE_PART(DualSMCLink)
};
//..
BEGIN_MESSAGE_MAP(SMCLink, CCmdTarget)
//{{AFX_MSG_MAP(CAutoProxy)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
IMPLEMENT_DYNCREATE(SMCLink, CCmdTarget)
BEGIN_DISPATCH_MAP(SMCLink, CCmdTarget)
//{{AFX_DISPATCH_MAP(SMCLink)
//..
DISP_FUNCTION(SMCLink, "RequestService", RequestService, VT_ERROR, VTS_BSTR)
//}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()
BEGIN_INTERFACE_MAP(SMCLink, CCmdTarget)
INTERFACE_PART(SMCLink, IID_ISMCLink, DualSMCLink)
INTERFACE_PART(SMCLink, IID_ISMCLink2, DualSMCLink)
INTERFACE_PART(SMCLink, IID_IConnectionPointContainer, ConnPtContainer)
END_INTERFACE_MAP()
BEGIN_CONNECTION_MAP(SMCLink, CCmdTarget)
CONNECTION_PART(SMCLink, IID__IAdHocPresenceEvents, AdHocPresenceEvents)
END_CONNECTION_MAP()
SMCLink::SMCLink(void)
{
// enable this object for OLE automation
EnableAutomation();
// enable this object for connection points
EnableConnections();
}
//..
afx_msg HRESULT SMCLink::RequestService(BSTR sipAddr)
{
HRESULT hr = E_FAIL;
int status = 1;
const CPtrArray* pConnections = m_xAdHocPresenceEvents.GetConnections ();
ASSERT (pConnections != NULL);
int nConnections = pConnections->GetSize ();
if (nConnections) {
for (int i=0; i<nConnections; i++)
{
_IAdHocPresenceEvents* pInterface = (_IAdHocPresenceEvents*) (pConnections->GetAt (i));
ASSERT (pInterface != NULL);
// Outgoing!
hr = pInterface->OnAdHocPresenceQuery (status);
}
}
return hr;
}
// delegate standard IDispatch methods to MFC IDispatch implementation
DELEGATE_DUAL_INTERFACE(SMCLink, DualSMCLink)
STDMETHODIMP SMCLink::XDualSMCLink::RequestService(BSTR user)
{
METHOD_PROLOGUE(SMCLink, DualSMCLink)
return pThis->RequestService(user);
}
//..
IMarshal is queried because COM needs to marhsal the calls between processes. While you can obviously implement the interface yourself, there is no point in bothering with this. Instead, make sure your interface is OLE Automation compatible, and is on a type library and the type library is registered (you can check this with COM/OLE Viewer tool. COM will supply automatic proxy/stub pair for you and won't require that IMarshal is implemented.

Explorer doesn't release IDataObject when doing a drag/drop

I'm implementing drag-and-drop in my application. I'm having a problem with Windows Explorer not releasing my IDataObject after a drag-and-drop operation. To isolate the problem, I've implemented a very simple drag-and-drop source that should compile in most any Win32 compiler. The data object contains no data; as you can see everything is very simple. The data object contains tracing that can be viewed with DebugView to indicate when it is created and when it is destroyed.
To reproduce:
Start the drag by holding down the mouse button.
Drag-and-drop the object into an open Windows Explorer window.
Observe the output in DebugView; sample output:
[4964] gdo ctor
[4964] gds ctor
[4964] gds dtor
This output indicates that the data source was destructed, but somebody is still holding a reference to my IDataObject!
Start dragging a file in the same Explorer window. Even though I'm not at all interacting with my project at this time, it causes gdo dtor to be printed - indicating that the final reference to the IDataObject was released.
I'm running Windows 7 64-bit. It's interesting to note that some Explorer windows do release the data object right away after the drop; others don't seem to do that until you start dragging a different object into the Explorer window as indicated in step #4. It also seems to depend on where in the window I drop the object - some places cause the object to be immediately released and others don't. It's very strange!
My questions are these:
Is this normal for Explorer to do this? Why is this? Or do I have a bug in my code? It's very disconcerting to see COM objects still referenced when my application terminates! Also it means that the resources held by IDataObject are tied up until Explorer decides to release the object.
If this is indeed normal behavior (and even if it isn't, I guess I should cope with ill-behaved drop targets), then what is the best practice for cleaning up this unreleased COM object when the application terminates? I'm writing in C++ Builder and using ATL, and when the user tries to close the application, they get a very unfriendly "There are still active COM objects in this application, blah blah blah. Are you sure you want to close this application?" - presumably generated by ATL which is noticing there are unreleased COM objects - generally a bad thing on application shutdown.
Here's some sample code. It implements an IDataObject that provides no data, and a very basic IDropSource. Of course, the real application provides data via IDataObject but I found this basic implementation is enough to reproduce the issue. I wrote it in C++ Builder but 90% of it is portable Win32 code. Just add a label or other object to the GUI toolkit of choice (MFC, WinForms with C++/CLI, Qt, wxWidgets, straight Win32, whatever) and tie the appropriate code to the MouseDown event.
I can't think of any bugs in this code that would cause this behavior, but that doesn't mean I didn't miss any!
class GenericDataObject : public IDataObject
{
public:
// basic IUnknown implementation
ULONG __stdcall AddRef() { return InterlockedIncrement(&refcount); }
ULONG __stdcall Release() {
ULONG nRefCount = InterlockedDecrement(&refcount);
if (nRefCount == 0) delete this;
return nRefCount;
}
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) {
if (!ppvObject) return E_POINTER;
if (riid == IID_IUnknown) {
*ppvObject = static_cast<IUnknown*>(this);
AddRef();
return S_OK;
} else if (riid == IID_IDataObject) {
*ppvObject = static_cast<IDataObject*>(this);
AddRef();
return S_OK;
} else {
*ppvObject = NULL;
return E_NOINTERFACE;
}
}
// IDataObject members
STDMETHODIMP GetData (FORMATETC *pformatetcIn, STGMEDIUM *pmedium) { return DV_E_FORMATETC; }
STDMETHODIMP GetDataHere (FORMATETC *pformatetc, STGMEDIUM *pmedium) { return E_NOTIMPL; }
STDMETHODIMP QueryGetData (FORMATETC *pformatetc) { return DV_E_FORMATETC; }
STDMETHODIMP GetCanonicalFormatEtc (FORMATETC *pformatectIn, FORMATETC *pformatetcOut) { return DV_E_FORMATETC; }
STDMETHODIMP SetData (FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease) { return E_NOTIMPL; }
STDMETHODIMP EnumFormatEtc (DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc) { return E_NOTIMPL; }
STDMETHODIMP DAdvise (FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection) { return OLE_E_ADVISENOTSUPPORTED; }
STDMETHODIMP DUnadvise (DWORD dwConnection) { return OLE_E_ADVISENOTSUPPORTED; }
STDMETHODIMP EnumDAdvise (IEnumSTATDATA **ppenumAdvise) { return OLE_E_ADVISENOTSUPPORTED; }
public:
GenericDataObject() : refcount(1) {OutputDebugString("gdo ctor");}
~GenericDataObject() {OutputDebugString("gdo dtor");}
private:
LONG refcount;
};
class GenericDropSource : public IDropSource
{
public:
// basic IUnknown implementation
ULONG __stdcall AddRef() { return InterlockedIncrement(&refcount); }
ULONG __stdcall Release() {
ULONG nRefCount = InterlockedDecrement(&refcount);
if (nRefCount == 0) delete this;
return nRefCount;
}
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) {
if (!ppvObject) return E_POINTER;
if (riid == IID_IUnknown) {
*ppvObject = static_cast<IUnknown*>(this);
AddRef();
return S_OK;
} else if (riid == IID_IDropSource) {
*ppvObject = static_cast<IDropSource*>(this);
AddRef();
return S_OK;
} else {
*ppvObject = NULL;
return E_NOINTERFACE;
}
}
// IDropSource members
STDMETHODIMP QueryContinueDrag (BOOL fEscapePressed, DWORD grfKeyState) {
if (fEscapePressed) {
return DRAGDROP_S_CANCEL;
}
if (!(grfKeyState & (MK_LBUTTON | MK_RBUTTON))) {
return DRAGDROP_S_DROP;
}
return S_OK;
}
STDMETHODIMP GiveFeedback (DWORD dwEffect) { return DRAGDROP_S_USEDEFAULTCURSORS; }
public:
GenericDropSource() : refcount(1) {OutputDebugString("gds ctor");}
~GenericDropSource() {OutputDebugString("gds dtor");}
private:
LONG refcount;
};
// This is the C++ Builder-specific part; all I did was add a label to the default form
// and tie this event to it.
void __fastcall TForm1::Label1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
{
OleInitialize(NULL);
GenericDataObject *o = new GenericDataObject;
GenericDropSource *s = new GenericDropSource;
DWORD effect = 0;
DoDragDrop(o, s, DROPEFFECT_COPY, &effect);
o->Release();
s->Release();
}

IHTMLDocument2->documentElement->outerHTML is too slow recreating HTML from DOM, is there a faster way?

I've got an IE BHO plugin that sends out via a COM call the HTML of a page that was loaded in the window.
// Note all error handling removed for readability :)
STDMETHODIMP CPlugin::get_HTML(long lMaxSize, BSTR *pbstrHTML)
{
CComPtr<IDispatch> pDispatch;
MSHTML::IHTMLDocument2Ptr pDocument2 = NULL;
MSHTML::IHTMLDocument3Ptr pDocument3 = NULL;
hr = m_spWebBrowser->get_Document(&pDispatch);
hr = pDispatch->QueryInterface(IID_IHTMLDocument3, (void**)&pDocument3);
MSHTML::IHTMLElementPtr pRoot = pDocument3->documentElement;
wstring strHTML = pRoot->outerHTML;
CComBSTR bstrHTML = strOutput.c_str();
bstrHTML.CopyTo(pbstrHTML);
}
However when it encounters a very large page (e.g. "http://sitemap.zillow.com/uncompressed/ForSale_Hood_MedPri_1.xml"), it takes 3 minutes to create the HTML from the DOM.
Is there a way to access the raw HTML/XML?
When you do a 'view page source' in IE, it pops up almost immediately, so internally IE must be using some API that can do what I want.
Thanks,
Shane.
It seems that in old versions of MSHTML, outerHTML had a O(n^2) performance. However, in newer versions (IE8) this problem is gone. If you have a choice, use IE8 or later.
Otherwise, using IPersistStream::Save is an option. But CreateStreamOnHGlobal won't help you since its implementation is also O(n^2). You'll have to use a custom IStream for that.
Included is an IStream implementation which was made for this purpose and supports quick writes:
#include <atlbase.h>
#include <atlcom.h>
#include <vector>
// an implementation of a write-only IStream.
// needed because the CreateStreamOnHGlobal implementation doesn't handle
// resizes well (N writes seem to take O(N^2) time)
class MyStream :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<MyStream>,
public IStreamImpl
{
public:
std::vector<char> buf;
BEGIN_COM_MAP(MyStream)
COM_INTERFACE_ENTRY(IStream)
END_COM_MAP()
STDMETHOD(Write) (const void * pv, ULONG cb, ULONG *pcbWritten);
};
/*
Usage:
CComPtr<IStream> stream;
hr = MyStream::CreateInstance(&stream);
// streamObj will be valid as long as IStream smart pointer lives
MyStream *streamObj = (MyStream*)stream.p;
*/
STDMETHODIMP MyStream::Write(const void * pv, ULONG cb, ULONG *pcbWritten)
{
buf.insert(buf.end(), (char*)pv, (char*)pv+cb);
return S_OK;
}
Yes, you can QI for IPersistStream and save to a memory stream created by CreateStreamOnHGlobal
Note the document must finished downloading (ready state needs to be complete).
Thanks Amnon, the following code is mostly working for me.
// an implementation of a write-only IStream.
// needed because the CreateStreamOnHGlobal implementation doesn't handle
// resizes well (N writes seem to take O(N^2) time)
class MyStream :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<MyStream>,
public IStream
{
public:
std::vector<char> buf;
BEGIN_COM_MAP(MyStream)
COM_INTERFACE_ENTRY(IStream)
END_COM_MAP()
STDMETHOD(Write) (const void * pv, ULONG cb, ULONG *pcbWritten);
// Implement IStream abstract functions
STDMETHOD(Read) (void *pv, ULONG cb, ULONG *pcbRead) { return S_OK; };
STDMETHOD(Seek) (LARGE_INTEGER dlibMove,DWORD dwOrigin,ULARGE_INTEGER *plibNewPosition) { return S_OK; };
STDMETHOD(SetSize) (ULARGE_INTEGER libNewSize) { return S_OK; };
STDMETHOD(CopyTo) (IStream *pstm,ULARGE_INTEGER cb,ULARGE_INTEGER *pcbRead,ULARGE_INTEGER *pcbWritten) { return S_OK; };
STDMETHOD(Commit) (DWORD grfCommitFlags) { return S_OK; };
STDMETHOD(Revert) () { return S_OK; };
STDMETHOD(LockRegion) (ULARGE_INTEGER libOffset,ULARGE_INTEGER cb,DWORD dwLockType) { return S_OK; };
STDMETHOD(UnlockRegion) (ULARGE_INTEGER libOffset,ULARGE_INTEGER cb,DWORD dwLockType) { return S_OK; };
STDMETHOD(Stat) (__RPC__out STATSTG *pstatstg,DWORD grfStatFlag) { return S_OK; };
STDMETHOD(Clone) (__RPC__deref_out_opt IStream **ppstm) { return S_OK; };
};
STDMETHODIMP MyStream::Write(const void * pv, ULONG cb, ULONG *pcbWritten)
{
buf.insert(buf.end(), (char*)pv, (char*)pv+cb);
return S_OK;
}
// Retrieves the HTML of the current page
STDMETHODIMP CPlugin::get_HTML(long lMaxSize, BSTR *pbstrHTML)
{
HRESULT hr = S_OK;
try
{
CComPtr<IDispatch> pDispatch;
MSHTML::IHTMLDocumentPtr pDocument = NULL;
CComPtr<IStream> mystream;
hr = MyStream::CreateInstance(&mystream);
// streamObj will be valid as long as IStream smart pointer lives
MyStream *streamObj = (MyStream*)mystream.p;
hr = m_spWebBrowser->get_Document(&pDispatch);
hr = pDispatch->QueryInterface(IID_IHTMLDocument, (void**)&pDocument);
IPersistStreamInitPtr persistStream = pDocument;
hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
hr = persistStream->Save(mystream, FALSE);
}
catch(...)
{
TRACE_FN("Got exception somewhere");
}
return hr;
}
Now the only problem left is how to figure why some it returns me single-byte chars most times, and double-byte chars at other times. Any ideas?
Thanks for the help.

Flash ActiveX: How to Load Movie from memory or resource or stream?

I'm embedding a Flash ActiveX control in my C++ app (Flash.ocx, Flash10a.ocx, etc depending on your Flash version).
I can load an SWF file by calling LoadMovie(0, filename), but the file needs to physically reside in the disk. How to load the SWF from memory (or resource, or stream)? I'm sure there must be a way, because commercial solutions like f-in-box's feature Load flash movies from memory directly also uses Flash ActiveX control.
Appearantly I a going to need to supply details for a vote 'up'.. OK.
The internal flash buffer when first initiailized indicates if a movie is loaded or if the buffer hold properties in the buffer fisrt four bytes.
gUfU -- no movie loaded. properties to follow ....
fUfU -- .. [4bytes] size as integer.
then the UNCOMPRESSED movie or SWF as it were.
Write a IStream class.
fill with above.
save as szFile
TFlashStream *fStream = new TFlashStream(szFile);
// QI flash player
IPersistStreamInit * psStreamInit = 0;
shock->QueryInterface(::IID_IPersistStreamInit,
(LPVOID*)&psStreamInit);
if(psStreamInit)
{
psStreamInit->InitNew();
psStreamInit->Load(fStream);
psStreamInit->Release();
}
delete fStream;
Things to note :
When psStreamInit->Load(fStream);
will call IStream::Read looking for the header 'fUfU'.
if the return is correct psStreamInit then calls IStream::Read for the buffer size.
If everthing looks good so far, psStreamInit then reads in 1024 byte chunks until the read is exhausted.
However. for the header and file size.
STDMETHOD(Read)(void *pv, ULONG cb, ULONG *pcbRead)
pcbRead is invalid. you may want to use something like
IsBadReadPtr
--
Michael
To spare you some typing. It works for me in this way (just works not extensively tested):
void flash_load_memory(FlashWidget* w, void* data, ULONG size) {
FlashMemoryStream fStream = FlashMemoryStream(data, size);
IPersistStreamInit* psStreamInit = NULL;
w->mFlashInterface->QueryInterface(IID_IPersistStreamInit,(LPVOID*) &psStreamInit);
if(psStreamInit) {
psStreamInit->InitNew();
psStreamInit->Load((LPSTREAM)&fStream);
psStreamInit->Release();
}
}
class FlashMemoryStream : IStream {
public:
FlashMemoryStream(void* data,ULONG size) {
this->data = data;
this->size = size;
this->pos = 0;
}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID* ppv) {
return E_NOTIMPL;
}
ULONG STDMETHODCALLTYPE AddRef() {
return E_NOTIMPL;
}
ULONG STDMETHODCALLTYPE Release() {
return E_NOTIMPL;
}
// IStream methods
STDMETHOD(Read) (void *pv,ULONG cb,ULONG *pcbRead) {
if(pos == 0 && cb == 4) {
memcpy(pv,"fUfU",4);
pos += 4;
return S_OK;
}
else if(pos == 4 && cb == 4) {
memcpy(pv,&size,4);
size += 8;
pos += 4;
return S_OK;
}
else {
if(pos + cb > size) cb = size - pos;
if(cb == 0) return S_FALSE;
memcpy(pv,(char*)data + pos - 8,cb);
if(pcbRead) (*pcbRead) = cb;
pos += cb;
return S_OK;
}
}
STDMETHOD(Write) (void const *pv,ULONG cb,ULONG *pcbWritten) { return E_NOTIMPL; }
STDMETHOD(Seek) (LARGE_INTEGER dlibMove,DWORD dwOrigin,ULARGE_INTEGER *plibNewPosition) { return E_NOTIMPL; }
STDMETHOD(SetSize) (ULARGE_INTEGER libNewSize) { return E_NOTIMPL; }
STDMETHOD(CopyTo) (IStream *pstm,ULARGE_INTEGER cb,ULARGE_INTEGER *pcbRead,ULARGE_INTEGER *pcbWritten) { return E_NOTIMPL; }
STDMETHOD(Commit) (DWORD grfCommitFlags) { return E_NOTIMPL; }
STDMETHOD(Revert) (void) { return E_NOTIMPL; }
STDMETHOD(LockRegion) (ULARGE_INTEGER libOffset,ULARGE_INTEGER cb,DWORD dwLockType) { return E_NOTIMPL; }
STDMETHOD(UnlockRegion) (ULARGE_INTEGER libOffset,ULARGE_INTEGER cb,DWORD dwLockType) { return E_NOTIMPL; }
STDMETHOD(Stat) (STATSTG *pstatstg,DWORD grfStatFlag) { return E_NOTIMPL; }
STDMETHOD(Clone) (IStream **ppstm) { return E_NOTIMPL; }
void* data;
ULONG size;
ULONG pos;
};
Being a flash guy I don't know any details on the C++ side, but if you made a request on the Flash side to a fake protocol, on the C side can you intercept that request and answer it with a data stream? I mean something like:
var mc:MovieClip = createEmptyMovieClip( "mc", 0 );
mc.loadMovie( "fakeprotocol://"+filename )
As long as the response looks (to Flash) like an HTTP stream, that ought to work. (Apologies in advance if the "intercept the request and return a data stream" is the part you're asking for help with.)
in addition....
Flash player promotes IPersistStorage.
flash.QI IPersistStorage
pStorage.load (mystorage_as_stream)
.. in theory.
Sorry for the above.. I intended to post
Flash player promotes IPersistStreamInit.
flash.QI IPersistStreamInit
pStream.load (my_stream)
Michael
This method don't work when you try to load a movie via the MovieclipLoader or LoadMovie from another movie!!!
The result is to replace the calling SWF file!! ...so this method work only for loading the base file.
Someone know a better method that work also with MovieClipLoader and LoadMovie?
Thanks.
MS VC ATL sample (have built with VS 2010 SP1 + Windows SDK 7.1 and tested on Windows 7 SP1 64-bit with Flash64_11_3_300_257.ocx / Flash32_11_3_300_257.ocx and on Windows XP SP3 32-bit with Flash32_11_3_300_257.ocx):
#pragma pack(push, 1)
typedef struct _FLASH_STREAM_HEADER
{
DWORD m_dwSignature;
DWORD m_dwDataSize;
} FLASH_STREAM_HEADER, *PFLASH_STREAM_HEADER;
#pragma pack(pop)
static HRESULT LoadFlashMovieFromResource(ATL::CComPtr<IShockwaveFlash>& spShockwaveFlash,
UINT nResourceID, LPCTSTR pszResourceType = RT_RCDATA)
{
HMODULE hModule = ATL::_AtlBaseModule.GetModuleInstance();
ATLASSUME(hModule != NULL);
//HINSTANCE hResourceInstance = ATL::AtlFindResourceInstance(nResourceID, pszResourceType);
//HRSRC hResource = ::FindResource(hResourceInstance, MAKEINTRESOURCE(nResourceID),
// pszResourceType);
HRSRC hResource = ::FindResource(hModule, MAKEINTRESOURCE(nResourceID), pszResourceType);
if (hResource == NULL)
return HRESULT_FROM_WIN32(::GetLastError());
DWORD dwResourceDataSize = ::SizeofResource(hModule, hResource);
if (dwResourceDataSize == 0)
return HRESULT_FROM_WIN32(::GetLastError());
HGLOBAL hResourceLoaded = ::LoadResource(hModule, hResource);
if (hResourceLoaded == NULL)
return HRESULT_FROM_WIN32(::GetLastError());
ATL::CComPtr<IStream> spStream;
HRESULT hResult = ::CreateStreamOnHGlobal(NULL, TRUE, &spStream);
if (FAILED(hResult))
return hResult;
FLASH_STREAM_HEADER fsh = {0x55665566, dwResourceDataSize};
ULARGE_INTEGER uli = {sizeof (fsh) + dwResourceDataSize};
hResult = spStream->SetSize(uli);
if (FAILED(hResult))
return hResult;
hResult = spStream->Write(&fsh, sizeof (fsh), NULL);
if (FAILED(hResult))
return hResult;
hResult = spStream->Write(reinterpret_cast<void*>(hResourceLoaded), dwResourceDataSize, NULL);
if (FAILED(hResult))
return hResult;
uli.QuadPart = 0;
hResult = spStream->Seek(*reinterpret_cast<PLARGE_INTEGER>(&uli), STREAM_SEEK_SET, NULL);
if (FAILED(hResult))
return hResult;
ATL::CComPtr<IPersistStreamInit> spPersistStreamInit;
hResult = spShockwaveFlash.QueryInterface(&spPersistStreamInit);
if (SUCCEEDED(hResult))
hResult = spPersistStreamInit->Load(spStream);
return hResult;
}