3253 lines
104 KiB
C++
3253 lines
104 KiB
C++
/***************************************************************************
|
|
* DSOFDOCOBJ.CPP
|
|
*
|
|
* CDsoDocObject: ActiveX Document Single Instance Frame/Site Object
|
|
*
|
|
* Copyright ©1999-2004; Microsoft Corporation. All rights reserved.
|
|
* Written by Microsoft Developer Support Office Integration (PSS DSOI)
|
|
*
|
|
* This code is provided via KB 311765 as a sample. It is not a formal
|
|
* product and has not been tested with all containers or servers. Use it
|
|
* for educational purposes only. See the EULA.TXT file included in the
|
|
* KB download for full terms of use and restrictions.
|
|
*
|
|
* THIS CODE 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.
|
|
*
|
|
***************************************************************************/
|
|
#include "dsoframer.h"
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject - The DocObject Site Class
|
|
//
|
|
// This class wraps the functionality for DocObject hosting. Right now
|
|
// we are setup for one active site at a time, but this could be changed
|
|
// to allow multiple sites (although only one could be UI active at any
|
|
// given time).
|
|
//
|
|
CDsoDocObject::CDsoDocObject()
|
|
{
|
|
ODS("CDsoDocObject::CDsoDocObject\n");
|
|
m_cRef = 1;
|
|
m_fDisplayTools = TRUE;
|
|
}
|
|
|
|
CDsoDocObject::~CDsoDocObject(void)
|
|
{
|
|
ODS("CDsoDocObject::~CDsoDocObject\n");
|
|
if (m_pole) Close();
|
|
if (m_hwnd) DestroyWindow(m_hwnd);
|
|
|
|
SAFE_FREESTRING(m_pwszUsername);
|
|
SAFE_FREESTRING(m_pwszPassword);
|
|
SAFE_FREESTRING(m_pwszHostName);
|
|
|
|
SAFE_RELEASE_INTERFACE(m_pstgroot);
|
|
SAFE_RELEASE_INTERFACE(m_punkIPPResource);
|
|
SAFE_RELEASE_INTERFACE(m_pcmdCtl);
|
|
SAFE_RELEASE_INTERFACE(m_psiteCtl);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::CreateNewDocObject
|
|
//
|
|
// Static Creation Function.
|
|
//
|
|
STDMETHODIMP_(CDsoDocObject*) CDsoDocObject::CreateInstance(IDsoDocObjectSite* phost)
|
|
{
|
|
ODS("CDsoDocObject::CreateInstance()\n");
|
|
CDsoDocObject* pnew = new CDsoDocObject();
|
|
if ((pnew) && FAILED(pnew->InitializeNewInstance(phost)))
|
|
{
|
|
pnew->Release();
|
|
pnew = NULL;
|
|
}
|
|
return pnew;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::InitializeNewInstance
|
|
//
|
|
// Sets up new docobject class. We must a control site to attach this
|
|
// window to. It will call back to host for menu and IOleCommandTarget.
|
|
//
|
|
STDMETHODIMP CDsoDocObject::InitializeNewInstance(IDsoDocObjectSite* phost)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
WNDCLASS wndclass;
|
|
HWND hwndCtl;
|
|
|
|
ODS("CDsoDocObject::InitializeNewInstance()\n");
|
|
|
|
// We need valid IDsoDocObjectSite...
|
|
if ((phost == NULL) || FAILED(phost->GetWindow(&hwndCtl)))
|
|
return hr;
|
|
|
|
// As an AxDoc site, we need a valid parent window...
|
|
if ((!hwndCtl) || (!IsWindow(hwndCtl)))
|
|
return hr;
|
|
|
|
// Create a temp storage for this docobj site (if one already exists, bomb out)...
|
|
if ((m_pstgroot) || FAILED(hr = StgCreateDocfile(NULL, STGM_TRANSACTED | STGM_READWRITE |
|
|
STGM_SHARE_EXCLUSIVE | STGM_CREATE | STGM_DELETEONRELEASE, 0, &m_pstgroot)))
|
|
return hr;
|
|
|
|
// If our site window class has not been registered before, we should register it...
|
|
|
|
// This is protected by a critical section just for fun. The fact we had to single
|
|
// instance the OCX because of the host hook makes having multiple instances conflict here
|
|
// very unlikely. However, that could change sometime, so better to be safe than sorry.
|
|
EnterCriticalSection(&v_csecThreadSynch);
|
|
|
|
if (GetClassInfo(v_hModule, "DSOFramerDocWnd", &wndclass) == 0)
|
|
{
|
|
memset(&wndclass, 0, sizeof(WNDCLASS));
|
|
wndclass.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
|
|
wndclass.lpfnWndProc = CDsoDocObject::FrameWindowProc;
|
|
wndclass.hInstance = v_hModule;
|
|
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wndclass.lpszClassName = "DSOFramerDocWnd";
|
|
if (RegisterClass(&wndclass) == 0)
|
|
hr = E_WIN32_LASTERROR;
|
|
}
|
|
|
|
LeaveCriticalSection(&v_csecThreadSynch);
|
|
if (FAILED(hr)) return hr;
|
|
|
|
// Get the position RECT (and validate as needed)...
|
|
hr = phost->GetBorder(&m_rcViewRect);
|
|
if (FAILED(hr)) return hr;
|
|
|
|
if (m_rcViewRect.top > m_rcViewRect.bottom)
|
|
{m_rcViewRect.top = 0; m_rcViewRect.bottom = 0;}
|
|
if (m_rcViewRect.left > m_rcViewRect.right)
|
|
{m_rcViewRect.left = 0; m_rcViewRect.right = 0;}
|
|
|
|
// Create our site window at the give location (we are child of the control window)...
|
|
m_hwnd = CreateWindowEx(0, "DSOFramerDocWnd", NULL, WS_CHILD,
|
|
m_rcViewRect.left, m_rcViewRect.top,
|
|
(m_rcViewRect.right - m_rcViewRect.left),
|
|
(m_rcViewRect.bottom - m_rcViewRect.top),
|
|
hwndCtl, NULL, v_hModule, NULL);
|
|
|
|
if (!m_hwnd) return E_OUTOFMEMORY;
|
|
SetWindowLong(m_hwnd, GWL_USERDATA, (LONG)this);
|
|
|
|
// Save the control info for this object...
|
|
m_hwndCtl = hwndCtl;
|
|
(m_psiteCtl = phost)->AddRef();
|
|
|
|
m_psiteCtl->QueryInterface(IID_IOleCommandTarget, (void**)&m_pcmdCtl);
|
|
m_psiteCtl->GetHostName(&m_pwszHostName);
|
|
if (m_pwszHostName == NULL)
|
|
m_pwszHostName = DsoCopyString(L"DsoFramerControl");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::CreateDocObject (From CLSID)
|
|
//
|
|
// This does embedding of new object, or loads a copy from storage.
|
|
// To activate and show the object, you must call IPActivateView().
|
|
//
|
|
STDMETHODIMP CDsoDocObject::CreateDocObject(REFCLSID rclsid)
|
|
{
|
|
HRESULT hr;
|
|
CLSID clsid;
|
|
DWORD dwMiscStatus = 0;
|
|
IOleObject* pole = NULL;
|
|
IPersistStorage* pipstg = NULL;
|
|
BOOL fInitNew = (m_pstgfile == NULL);
|
|
|
|
ODS("CDsoDocObject::CreateDocObject(CLSID)\n");
|
|
ASSERT(!(m_pole));
|
|
|
|
// Don't load if an object has already been loaded...
|
|
if (m_pole) return E_UNEXPECTED;
|
|
|
|
// It is possible that someone picked an older ProgId/CLSID that
|
|
// will AutoConvert on CoCreate, so fix up the storage with the
|
|
// new CLSID info. We we actually call CoCreate on the new CLSID...
|
|
if (fInitNew && SUCCEEDED(OleGetAutoConvert(rclsid, &clsid)))
|
|
{
|
|
OleDoAutoConvert(m_pstgfile, &clsid);
|
|
}
|
|
else clsid = rclsid;
|
|
|
|
// First, check the server to make sure it is AxDoc server...
|
|
if (FAILED(hr = ValidateDocObjectServer(rclsid)))
|
|
return hr;
|
|
|
|
// If we haven't loaded a storage, create a new one and remember to
|
|
// call InitNew (instead of Load) later on...
|
|
if (fInitNew && FAILED(hr = CreateObjectStorage(rclsid)))
|
|
return hr;
|
|
|
|
// We are ready to create an instance. Call CoCreate to make an
|
|
// inproc handler and ask for IOleObject (all docobjs must support this)...
|
|
if (FAILED(hr = InstantiateDocObjectServer(clsid, &pole)))
|
|
return hr;
|
|
|
|
// Do a quick check to see if server wants us to set client site before the load..
|
|
hr = pole->GetMiscStatus(DVASPECT_CONTENT, &dwMiscStatus);
|
|
if (dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST)
|
|
hr = pole->SetClientSite((IOleClientSite*)&m_xOleClientSite);
|
|
|
|
// Load up the bloody thing...
|
|
if (SUCCEEDED(hr = pole->QueryInterface(IID_IPersistStorage, (void**)&pipstg)))
|
|
{
|
|
// Remember to InitNew if this is a new storage...
|
|
hr = ((fInitNew) ? pipstg->InitNew(m_pstgfile) : pipstg->Load(m_pstgfile));
|
|
pipstg->Release();
|
|
}
|
|
|
|
// Assuming all the above worked we should have an OLE Embeddable
|
|
// object and should finish the initialization (set object running)...
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Save the IOleObject* and do a disconnect on quit...
|
|
SAFE_SET_INTERFACE(m_pole, pole);
|
|
m_fDisconnectOnQuit = TRUE;
|
|
|
|
// Keep server CLSID for this object
|
|
m_clsidObject = clsid;
|
|
|
|
// Ensure server is running and locked...
|
|
EnsureOleServerRunning(TRUE);
|
|
|
|
// If we didn't do so already, set our client site...
|
|
if (!(dwMiscStatus & OLEMISC_SETCLIENTSITEFIRST))
|
|
hr = m_pole->SetClientSite((IOleClientSite*)&m_xOleClientSite);
|
|
|
|
// Set the host names for OLE embedding...
|
|
m_pole->SetHostNames(m_pwszHostName, m_pwszHostName);
|
|
|
|
// Ask object to save (if dirty)...
|
|
if (IsDirty()) SaveObjectStorage();
|
|
}
|
|
else
|
|
{// Be sure they disconnect from our site if we failed load...
|
|
pole->SetClientSite(NULL);
|
|
}
|
|
|
|
// This will free the OLE server if anything above failed...
|
|
SAFE_RELEASE_INTERFACE(pole);
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::CreateDocObject (From IStorage*)
|
|
//
|
|
// This does embedding of new object, or loads a copy from storage.
|
|
// To activate and show the object, you must call IPActivateView().
|
|
//
|
|
STDMETHODIMP CDsoDocObject::CreateDocObject(IStorage *pstg)
|
|
{
|
|
HRESULT hr;
|
|
CLSID clsid;
|
|
|
|
ODS("CDsoDocObject::CreateDocObject(IStorage*)\n");
|
|
CHECK_NULL_RETURN(pstg, E_POINTER);
|
|
|
|
// Read the clsid from the storage..
|
|
if (FAILED(hr = ReadClassStg(pstg, &clsid)))
|
|
return hr;
|
|
|
|
// Validate the server is AxDoc server...
|
|
if (FAILED(hr = ValidateDocObjectServer(clsid)))
|
|
return hr;
|
|
|
|
// Create a new storage for this CLSID...
|
|
if (FAILED(hr = CreateObjectStorage(clsid)))
|
|
return hr;
|
|
|
|
// Copy data into the new storage and commit the change...
|
|
hr = pstg->CopyTo(0, NULL, NULL, m_pstgfile);
|
|
if (SUCCEEDED(hr)) hr = m_pstgfile->Commit(STGC_OVERWRITE);
|
|
|
|
// Then do normal create on existing storage made from copy...
|
|
return CreateDocObject(clsid);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::CreateFromFile
|
|
//
|
|
// Loads document into the framer control. The bind options determine if
|
|
// file should be loaded read-only or read-write. An alternate CLSID can
|
|
// be provided if the document type is not normally associated with a
|
|
// a DocObject server in OLE.
|
|
//
|
|
// To activate and show the object, you must call IPActivateView().
|
|
//
|
|
// The code will attempt to open the file in one of three ways:
|
|
//
|
|
// 1.) Using IMoniker, we will attempt to open and load existing file
|
|
// via a passed moniker, similar to how IE loads files.
|
|
//
|
|
// 2.) Using IPersistFile, we will attempt to open via direct file path.
|
|
//
|
|
// 3.) Finally, if above two options don't work, we will try to open the
|
|
// storage, make a copy and load using IPersistStorage. This last way follows
|
|
// the (old) Binder behavior, and should *by spec* always work, but is a bit
|
|
// clunky since we open a copy, not the real file.
|
|
//
|
|
STDMETHODIMP CDsoDocObject::CreateFromFile(LPWSTR pwszFile, REFCLSID rclsid, LPBIND_OPTS pbndopts)
|
|
{
|
|
HRESULT hr;
|
|
CLSID clsid;
|
|
CLSID clsidConv;
|
|
IOleObject *pole = NULL;
|
|
IBindCtx *pbctx = NULL;
|
|
IMoniker *pmkfile = NULL;
|
|
IStorage *pstg = NULL;
|
|
BOOL fLoadFromAltCLSID = (rclsid != GUID_NULL);
|
|
|
|
// Sanity check of parameters...
|
|
if (!(pwszFile) || ((*pwszFile) == L'\0') || (pbndopts == NULL))
|
|
return E_INVALIDARG;
|
|
|
|
TRACE2("CDsoDocObject::CreateFromFile(%S, %x)\n", pwszFile, pbndopts->grfMode);
|
|
|
|
// First. we'll try to find the associated CLSID for the given file,
|
|
// and then set it to the alternate if not found. If we don't have a
|
|
// CLSID by the end of this, because user didn't specify alternate
|
|
// and GetClassFile failed, then we error out...
|
|
if (FAILED(GetClassFile(pwszFile, &clsid)) && !(fLoadFromAltCLSID))
|
|
return DSO_E_INVALIDSERVER;
|
|
|
|
// We should try to load from alternate CLSID if provided one...
|
|
if (fLoadFromAltCLSID) clsid = rclsid;
|
|
|
|
// We should also handle auto-convert to start "newest" server...
|
|
if (SUCCEEDED(OleGetAutoConvert(clsid, &clsidConv)))
|
|
clsid = clsidConv;
|
|
|
|
// Validate that we have a DocObject server...
|
|
if ((clsid == GUID_NULL) || FAILED(ValidateDocObjectServer(clsid)))
|
|
return DSO_E_INVALIDSERVER;
|
|
|
|
// Check for IE cache items since these are read-only as far as user is concerned...
|
|
if (FIsIECacheFile(pwszFile))
|
|
{
|
|
pbndopts->grfMode &= ~(STGM_READWRITE);
|
|
pbndopts->grfMode &= ~(STGM_WRITE);
|
|
pbndopts->grfMode |= STGM_READ;
|
|
}
|
|
|
|
// First, we try to bind by moniker (same as IE). We'll need a bind context
|
|
// and a file moniker for the orginal source...
|
|
if (SUCCEEDED(hr = CreateBindCtx(0, &pbctx)))
|
|
{
|
|
if (SUCCEEDED(hr = pbctx->SetBindOptions(pbndopts)) &&
|
|
SUCCEEDED(hr = CreateFileMoniker(pwszFile, &pmkfile)))
|
|
{
|
|
|
|
// Bind to the object moniker refers to...
|
|
hr = pmkfile->BindToObject(pbctx, NULL, IID_IOleObject, (void**)&pole);
|
|
|
|
// If that failed, try to bind direct to file in new server...
|
|
if (FAILED(hr))
|
|
{
|
|
IPersistFile *pipfile = NULL;
|
|
|
|
if (SUCCEEDED(hr = InstantiateDocObjectServer(clsid, &pole)) &&
|
|
SUCCEEDED(hr = pole->QueryInterface(IID_IPersistFile, (void**)&pipfile)))
|
|
{
|
|
hr = pipfile->Load(pwszFile, pbndopts->grfMode);
|
|
pipfile->Release();
|
|
}
|
|
}
|
|
|
|
// If either solution worked, setup the rest of the bind info...
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Save the IOleObject* and do a disconnect on quit...
|
|
SAFE_SET_INTERFACE(m_pole, pole);
|
|
m_fDisconnectOnQuit = TRUE;
|
|
|
|
// Keep server CLSID for this object
|
|
m_clsidObject = clsid;
|
|
|
|
// Keep the moniker and bind ctx...
|
|
SAFE_SET_INTERFACE(m_pmkSourceFile, pmkfile);
|
|
SAFE_SET_INTERFACE(m_pbctxSourceFile, pbctx);
|
|
|
|
// Set out client site...
|
|
m_pole->SetClientSite((IOleClientSite*)&m_xOleClientSite);
|
|
|
|
// We don't normally set host name for moniker bind, but MSWORD object
|
|
// requires it to IP activate it instead of link & show external...
|
|
if (IsWordObject())
|
|
m_pole->SetHostNames(m_pwszHostName, m_pwszHostName);
|
|
|
|
}
|
|
else pole->SetClientSite(NULL);
|
|
|
|
// This will release the moniker if above failed...
|
|
pmkfile->Release();
|
|
}
|
|
// This will release the bind ctx if above failed...
|
|
pbctx->Release();
|
|
}
|
|
|
|
// If binding by moniker failed, try the old fashion way of bind to OLE storage...
|
|
if (FAILED(hr))
|
|
{
|
|
// Try to open file as OLE storage (native OLE DocFile)...
|
|
if (SUCCEEDED(hr = StgOpenStorage(pwszFile, NULL, pbndopts->grfMode, NULL, 0, &pstg)))
|
|
{
|
|
// Create our substorage, and copy the data over to it...
|
|
if (SUCCEEDED(hr = CreateObjectStorage(clsid)) &&
|
|
SUCCEEDED(hr = pstg->CopyTo(0, NULL, NULL, m_pstgfile)))
|
|
{
|
|
m_pstgfile->Commit(STGC_OVERWRITE);
|
|
|
|
// Then create the object from the storage copy...
|
|
if (SUCCEEDED(hr = CreateDocObject(clsid)))
|
|
{
|
|
SAFE_SET_INTERFACE(m_pstgSourceFile, pstg);
|
|
}
|
|
}
|
|
// This will release the storage if above failed...
|
|
pstg->Release();
|
|
}
|
|
}
|
|
|
|
// If all went well, we should save file name and whether it opened read-only...
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_pwszSourceFile = DsoCopyString(pwszFile);
|
|
m_idxSourceName = CalcDocNameIndex(m_pwszSourceFile);
|
|
m_fOpenReadOnly = ((pbndopts->grfMode & STGM_WRITE) == 0) &&
|
|
((pbndopts->grfMode & STGM_READWRITE) == 0);
|
|
}
|
|
|
|
// This will free the OLE server if anything above failed...
|
|
SAFE_RELEASE_INTERFACE(pole);
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::CreateFromURL
|
|
//
|
|
// Loads document from a URL resource. The document can be open read-only
|
|
// using URLMON in the same context as the host client (i.e., it can be
|
|
// treated as normal navigation in host if host supports URLMON, like IE).
|
|
// Or you can choose to open read-write, which will attempt to use the
|
|
// Microsoft Internet Publishing Provider (MSDAIPP) to author on the web.
|
|
//
|
|
// The default is to open read-only with URLMON. If client wants read-write,
|
|
// we will try MSDAIPP for DAV first, then MSDAIPP for FPSE/WSS/SPS.
|
|
//
|
|
STDMETHODIMP CDsoDocObject::CreateFromURL(LPWSTR pwszUrlFile, REFCLSID rclsid, LPBIND_OPTS pbndopts, LPWSTR pwszUserName, LPWSTR pwszPassword)
|
|
{
|
|
HRESULT hr;
|
|
BOOL fReadOnly;
|
|
LPWSTR pwszTempFile;
|
|
IStream *pstmWebResource = NULL;
|
|
|
|
// First, a sanity check on the parameters passed...
|
|
if ((pwszUrlFile == NULL) || (pbndopts == NULL))
|
|
return E_INVALIDARG;
|
|
|
|
TRACE2("CDsoDocObject::CreateFromURL(%S, %x)\n", pwszUrlFile, pbndopts->grfMode);
|
|
|
|
// Get a temp path for the download...
|
|
if (!GetTempPathForURLDownload(pwszUrlFile, &pwszTempFile))
|
|
return E_ACCESSDENIED;
|
|
|
|
// Save out the user name and password (if provided) for IAuthenticate...
|
|
if (pwszUserName)
|
|
{
|
|
SAFE_FREESTRING(m_pwszUsername);
|
|
m_pwszUsername = DsoCopyString(pwszUserName);
|
|
|
|
SAFE_FREESTRING(m_pwszPassword);
|
|
m_pwszPassword = DsoCopyString(pwszPassword);
|
|
}
|
|
|
|
// Determine if they want to open read-only using URLMON or read-write using MSDAIPP...
|
|
fReadOnly = ((pbndopts->grfMode & STGM_WRITE) == 0) &&
|
|
((pbndopts->grfMode & STGM_READWRITE) == 0);
|
|
|
|
if (fReadOnly) // If using URLMON...
|
|
{
|
|
hr = URLDownloadFile((IUnknown*)&m_xAuthenticate, pwszUrlFile, pwszTempFile);
|
|
}
|
|
else // Otherwise we need access to MSDAIPP for writable stream...
|
|
{
|
|
hr = IPPDownloadWebResource(pwszUrlFile, pwszTempFile, &pstmWebResource);
|
|
}
|
|
|
|
// If all goes well with the download, we can then open the temp file for write access to
|
|
// use as local file during the editing. Then we can save back on the stream if requested.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
BIND_OPTS bopts = {sizeof(BIND_OPTS), BIND_MAYBOTHERUSER, STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 10000};
|
|
hr = CreateFromFile(pwszTempFile, rclsid, &bopts);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_fOpenReadOnly = fReadOnly;
|
|
if (!m_fOpenReadOnly)
|
|
{
|
|
m_pwszWebResource = DsoCopyString(pwszUrlFile);
|
|
SAFE_SET_INTERFACE(m_pstmWebResource, pstmWebResource);
|
|
}
|
|
}
|
|
}
|
|
|
|
SAFE_RELEASE_INTERFACE(pstmWebResource);
|
|
DsoMemFree(pwszTempFile);
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::CreateFromRunningObject
|
|
//
|
|
// Grabs a pre-existing object (such as a running object) and attempts
|
|
// to embed it. If that fails, it will make a copy of the object and open
|
|
// the copy read-only.
|
|
//
|
|
STDMETHODIMP CDsoDocObject::CreateFromRunningObject(LPUNKNOWN punkObj, LPWSTR pwszObjectName, LPBIND_OPTS pbndopts)
|
|
{
|
|
HRESULT hr;
|
|
IPersistStorage *prststg;
|
|
IOleObject *pole;
|
|
CLSID clsid;
|
|
BOOL fReadOnly;
|
|
|
|
ODS("CDsoDocObject::CreateFromRunningObject()\n");
|
|
CHECK_NULL_RETURN(punkObj, E_POINTER);
|
|
CHECK_NULL_RETURN(pbndopts, E_POINTER);
|
|
|
|
// We only can support read-only since a running object is external, and in order
|
|
// to get a copy inplace active, we end up making a copy of the storage, which means
|
|
// this control will have a copy and not control the orginal source. Since the orginal
|
|
// can be locked and not available, we have to treat this as read-only.
|
|
fReadOnly = ((pbndopts->grfMode & STGM_WRITE) == 0) &&
|
|
((pbndopts->grfMode & STGM_READWRITE) == 0);
|
|
|
|
if (!fReadOnly) return STG_E_LOCKVIOLATION;
|
|
|
|
// First, ensure the object passed supports embedding (i.e., we expect
|
|
// something like a Word.Document or Excel.Sheet object, not a Word.Application
|
|
// object or something else that is not a document type)...
|
|
hr = punkObj->QueryInterface(IID_IOleObject, (void**)&pole);
|
|
if (FAILED(hr)) return hr;
|
|
|
|
// Ask the object to save its persistent state. We also collect its CLSID and
|
|
// validate that the object type supports DocObject embedding...
|
|
hr = pole->QueryInterface(IID_IPersistStorage, (void**)&prststg);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = prststg->GetClassID(&clsid); // Validate the object type...
|
|
if (FAILED(hr) || FAILED(ValidateDocObjectServer(clsid)))
|
|
{
|
|
hr = DSO_E_INVALIDSERVER;
|
|
}
|
|
else
|
|
{
|
|
// Create a matching storage for the object and save the current
|
|
// state of the file in our storage (this is our private copy)...
|
|
if (SUCCEEDED(hr = CreateObjectStorage(clsid)) &&
|
|
SUCCEEDED(hr = prststg->Save(m_pstgfile, FALSE)) &&
|
|
SUCCEEDED(hr = prststg->SaveCompleted(NULL)))
|
|
{
|
|
// At this point we have a read-only copy...
|
|
prststg->HandsOffStorage();
|
|
m_fOpenReadOnly = TRUE;
|
|
|
|
// Create the embedding...
|
|
hr = CreateDocObject(clsid);
|
|
}
|
|
}
|
|
|
|
prststg->Release();
|
|
}
|
|
|
|
pole->Release();
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::IPActivateView
|
|
//
|
|
// Activates the object for inplace viewing.
|
|
//
|
|
STDMETHODIMP CDsoDocObject::IPActivateView()
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
ODS("CDsoDocObject::IPActivateView()\n");
|
|
ASSERT(m_pole);
|
|
|
|
// Make sure the site window is made visible...
|
|
if (!IsWindowVisible(m_hwnd))
|
|
ShowWindow(m_hwnd, SW_SHOW);
|
|
|
|
// If we have an IOleDocument pointer, we can use the Show method for
|
|
// inplace activation...
|
|
if (m_pdocv)
|
|
{
|
|
hr = m_pdocv->Show(TRUE);
|
|
}
|
|
else if (m_pole)
|
|
{
|
|
// Try creating a document view and loading it...
|
|
hr = m_xOleDocumentSite.ActivateMe(NULL);
|
|
if (FAILED(hr)) // If that fails, go the old OLE route...
|
|
{
|
|
RECT rcView; GetClientRect(m_hwnd, &rcView);
|
|
|
|
// First call IOleObject::DoVerb with OLEIVERB_INPLACEACTIVATE
|
|
// (or OLEIVERB_SHOW), and our view rect and IOleClientSite pointer...
|
|
hr = m_pole->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL,
|
|
(IOleClientSite*)&m_xOleClientSite, (UINT)-1, m_hwnd, &rcView);
|
|
|
|
// If the server doesn't recognize IP verb, try OLEIVERB_SHOW instead...
|
|
if (hr == OLEOBJ_E_INVALIDVERB)
|
|
hr = m_pole->DoVerb(OLEIVERB_SHOW, NULL,
|
|
(IOleClientSite*)&m_xOleClientSite, (UINT)-1, m_hwnd, &rcView);
|
|
|
|
// There is an issue with Visio 2002 rejecting DoVerb when it is called while
|
|
// Visio is still loading up the main app window. OLE servers normally return RPC
|
|
// retry later error, which will spin us in the msg-filter, but Visio just rejects
|
|
// the call all together, which pops us out of the filter early. This causes the
|
|
// embed attempt to fail. So to workaround this, we will check for the condition,
|
|
// sleep a bit, and then try our own semi-msg-filter loop...
|
|
if ((hr == RPC_E_CALL_REJECTED) && IsVisioObject())
|
|
{
|
|
DWORD dwLoopCnt = 0;
|
|
do
|
|
{
|
|
Sleep((200 * ++dwLoopCnt));
|
|
hr = m_pole->DoVerb(OLEIVERB_SHOW, NULL,
|
|
(IOleClientSite*)&m_xOleClientSite, (UINT)-1, m_hwnd, &rcView);
|
|
}
|
|
while ((hr == RPC_E_CALL_REJECTED) && (dwLoopCnt < 4));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Go ahead and UI activate now...
|
|
if (SUCCEEDED(hr))
|
|
hr = UIActivateView();
|
|
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::IPDeactivateView
|
|
//
|
|
// Deactivates the object ready for close.
|
|
//
|
|
STDMETHODIMP CDsoDocObject::IPDeactivateView()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ODS("CDsoDocObject::IPDeactivateView()\n");
|
|
|
|
// If we still have a UI active object, tell it to UI deactivate...
|
|
if (m_pipactive)
|
|
UIDeactivateView();
|
|
|
|
// Next hide the active object...
|
|
if (m_pdocv)
|
|
m_pdocv->Show(FALSE);
|
|
|
|
// Notify object our intention to IP deactivate...
|
|
if (m_pipobj)
|
|
m_pipobj->InPlaceDeactivate();
|
|
|
|
// Close the object down and release pointers...
|
|
if (m_pdocv)
|
|
{
|
|
hr = m_pdocv->CloseView(0);
|
|
m_pdocv->SetInPlaceSite(NULL);
|
|
}
|
|
|
|
SAFE_RELEASE_INTERFACE(m_pcmdt);
|
|
SAFE_RELEASE_INTERFACE(m_pdocv);
|
|
SAFE_RELEASE_INTERFACE(m_pipobj);
|
|
|
|
// Hide the site window...
|
|
ShowWindow(m_hwnd, SW_HIDE);
|
|
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::UIActivateView
|
|
//
|
|
// UI Activates the object (which should bring up toolbars and caret).
|
|
//
|
|
STDMETHODIMP CDsoDocObject::UIActivateView()
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
ODS("CDsoDocObject::UIActivateView()\n");
|
|
|
|
if (m_pdocv) // Go UI active...
|
|
{
|
|
hr = m_pdocv->UIActivate(TRUE);
|
|
}
|
|
else if (m_pole) // We should never get here, but just in case pdocv is NULL, signal UI active the old way...
|
|
{
|
|
RECT rcView; GetClientRect(m_hwnd, &rcView);
|
|
m_pole->DoVerb(OLEIVERB_UIACTIVATE, NULL, (IOleClientSite*)&m_xOleClientSite, (UINT)-1, m_hwnd, &rcView);
|
|
}
|
|
|
|
// Forward focus to the IP object...
|
|
if (SUCCEEDED(hr) && (m_hwndIPObject))
|
|
SetFocus(m_hwndIPObject);
|
|
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::UIDeactivateView
|
|
//
|
|
// UI Deactivates the object (which should hide toolbars and caret).
|
|
//
|
|
STDMETHODIMP CDsoDocObject::UIDeactivateView()
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
ODS("CDsoDocObject::UIDeactivateView()\n");
|
|
if (m_pdocv)
|
|
hr = m_pdocv->UIActivate(FALSE);
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::IsDirty
|
|
//
|
|
// Determines if the file is dirty (by asking IPersistXXX::IsDirty).
|
|
//
|
|
STDMETHODIMP_(BOOL) CDsoDocObject::IsDirty()
|
|
{
|
|
BOOL fDirty = TRUE; // Assume we are dirty unless object says we are not
|
|
IPersistStorage *pprststg;
|
|
IPersistFile *pprst;
|
|
|
|
// Can't be dirty without object
|
|
CHECK_NULL_RETURN(m_pole, FALSE);
|
|
|
|
// Ask object its dirty state...
|
|
if ((m_pmkSourceFile) &&
|
|
SUCCEEDED(m_pole->QueryInterface(IID_IPersistFile, (void**)&pprst)))
|
|
{
|
|
fDirty = ((pprst->IsDirty() == S_FALSE) ? FALSE : TRUE);
|
|
pprst->Release();
|
|
}
|
|
else if (SUCCEEDED(m_pole->QueryInterface(IID_IPersistStorage, (void**)&pprststg)))
|
|
{
|
|
fDirty = ((pprststg->IsDirty() == S_FALSE) ? FALSE : TRUE);
|
|
pprststg->Release();
|
|
}
|
|
|
|
return fDirty;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::Save
|
|
//
|
|
// Does default save action for existing document (if it is not read-only).
|
|
//
|
|
// There are three types of loaded doc objects we can save:
|
|
// (1) Files obtained by URL write bind via MSDAIPP;
|
|
// (2) Files opened from local file source; and
|
|
// (3) Objects already running or files linked to via OLE moniker.
|
|
//
|
|
// This function determines which type we should do the save, and call
|
|
// the right code for that type.
|
|
//
|
|
STDMETHODIMP CDsoDocObject::Save()
|
|
{
|
|
HRESULT hr = DSO_E_NOTBEENSAVED;
|
|
|
|
// We can't do save default if file was open read-only,
|
|
// caller must do save with new file name...
|
|
if (!IsReadOnly())
|
|
{
|
|
// If we have a URL (write-access) resource, do MSDAIPP save...
|
|
if (m_pstmWebResource)
|
|
{
|
|
hr = SaveToURL(NULL, TRUE, NULL, NULL);
|
|
}
|
|
// Else if it is local file, do a local save...
|
|
else if (m_pwszSourceFile)
|
|
{
|
|
hr = SaveToFile(NULL, TRUE);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::SaveToFile(LPWSTR pwszFile, BOOL fOverwriteFile)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
IStorage *pstg;
|
|
LPWSTR pwszFullName = NULL;
|
|
LPWSTR pwszRename = NULL;
|
|
BOOL fDoNormalSave = FALSE;
|
|
BOOL fDoOverwriteOps = FALSE;
|
|
BOOL fFileOpSuccess = FALSE;
|
|
|
|
TRACE2("CDsoDocObject::SaveToFile(%S, %d)\n", ((pwszFile) ? pwszFile : L"[Default]"), fOverwriteFile);
|
|
CHECK_NULL_RETURN(m_pole, hr);
|
|
|
|
// If they passed no file, use the default if current file is not read-only...
|
|
if (fDoNormalSave = (pwszFile == NULL))
|
|
{
|
|
// File must be read-write for normal save...
|
|
if (IsReadOnly()) return DSO_E_DOCUMENTREADONLY;
|
|
|
|
// If we are not moniker bound, we'll use the existing file path for ole save...
|
|
if (m_pwszSourceFile == NULL) return DSO_E_NOTBEENSAVED;
|
|
|
|
pwszFile = (pwszFullName = DsoCopyString(m_pwszSourceFile));
|
|
if (pwszFile == NULL) return E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
// Make sure a file extension exists for string passed by caller...
|
|
if (ValidateFileExtension(pwszFile, &pwszFullName))
|
|
pwszFile = pwszFullName;
|
|
}
|
|
|
|
// See if we will be overwriting, and error unless given permission to do so...
|
|
if ((fDoOverwriteOps = FFileExists(pwszFile)) && !(fOverwriteFile))
|
|
return STG_E_FILEALREADYEXISTS;
|
|
|
|
// If we had a previous lock on storage, we have to free it...
|
|
SAFE_RELEASE_INTERFACE(m_pstgSourceFile);
|
|
|
|
// If we are overwriting, we do a little Shell Operation here. This is done
|
|
// for two reasons: (1) it keeps the server from asking us to overwrite the
|
|
// file as it normally would in case of normal save; and (2) it lets us
|
|
// restore the original if the save fails...
|
|
if (fDoOverwriteOps)
|
|
{
|
|
pwszRename = DsoCopyStringCat(pwszFile, L".dstmp");
|
|
fFileOpSuccess = ((pwszRename) && FPerformShellOp(FO_RENAME, pwszFile, pwszRename));
|
|
}
|
|
|
|
// Time to do the save to new file.
|
|
|
|
// First we try to save by moniker using IPersistMoniker/IPersistFile. Check for defaut
|
|
// save to the current document. If to a new doc, create new moniker and save ...
|
|
if ((fDoNormalSave) && (m_pmkSourceFile))
|
|
{
|
|
hr = SaveDocToMoniker(m_pmkSourceFile, m_pbctxSourceFile, TRUE);
|
|
}
|
|
else if (pwszFile)
|
|
{
|
|
// Create a new moniker for the save...
|
|
IMoniker *pnewmk = NULL;
|
|
if (SUCCEEDED(hr = CreateFileMoniker(pwszFile, &pnewmk)))
|
|
{
|
|
|
|
// If current document is read-only, we want new one to be read-write...
|
|
if ((m_fOpenReadOnly) && (m_pbctxSourceFile))
|
|
{
|
|
BIND_OPTS bndopts; bndopts.cbStruct = sizeof(BIND_OPTS);
|
|
if (SUCCEEDED(m_pbctxSourceFile->GetBindOptions(&bndopts)))
|
|
{
|
|
bndopts.grfMode = (STGM_TRANSACTED | STGM_SHARE_DENY_WRITE | STGM_READWRITE);
|
|
hr = m_pbctxSourceFile->SetBindOptions(&bndopts);
|
|
}
|
|
}
|
|
else if (m_pbctxSourceFile == NULL)
|
|
{
|
|
// May need to create a new bind context if we haven't already...
|
|
if (SUCCEEDED(hr = CreateBindCtx(0, &m_pbctxSourceFile)))
|
|
{
|
|
BIND_OPTS bndopts; bndopts.cbStruct = sizeof(BIND_OPTS);
|
|
bndopts.grfFlags = BIND_MAYBOTHERUSER;
|
|
bndopts.grfMode = (STGM_TRANSACTED | STGM_SHARE_DENY_WRITE | STGM_READWRITE);
|
|
bndopts.dwTickCountDeadline = 10000;
|
|
hr = m_pbctxSourceFile->SetBindOptions(&bndopts);
|
|
}
|
|
}
|
|
|
|
// Do the save with moniker bind option and keep lock...
|
|
if (SUCCEEDED(hr = SaveDocToMoniker(pnewmk, m_pbctxSourceFile, TRUE)))
|
|
{
|
|
SAFE_RELEASE_INTERFACE(m_pmkSourceFile);
|
|
SAFE_SET_INTERFACE(m_pmkSourceFile, pnewmk);
|
|
m_fOpenReadOnly = FALSE; // Should be read-write now...
|
|
}
|
|
|
|
pnewmk->Release();
|
|
}
|
|
}
|
|
|
|
|
|
// If we are not using moniker binding, or the save with new moniker failed, try to use
|
|
// a save copy approach and we will overwrite the file...
|
|
if (!(m_pmkSourceFile) || FAILED(hr))
|
|
{
|
|
// Update the internal storage to the current doc state...
|
|
if (((m_pstgfile) || SUCCEEDED(hr = CreateObjectStorage(m_clsidObject))) &&
|
|
SUCCEEDED(hr = SaveObjectStorage()))
|
|
{
|
|
// Ask for server to save to file in "Save Copy As" fashion. This produces a native
|
|
// OLE document file more similar to what happens in normal SaveAs dialog for BIFF file...
|
|
hr = SaveDocToFile(pwszFile, FALSE);
|
|
|
|
// If that doesn't work, save out the storage as OLE storage and ...
|
|
if (FAILED(hr) && SUCCEEDED(hr = StgCreateDocfile(pwszFile, STGM_TRANSACTED |
|
|
STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, &pstg)))
|
|
{
|
|
WriteClassStg(pstg, m_clsidObject);
|
|
|
|
if (SUCCEEDED(hr = m_pstgfile->CopyTo(0, NULL, NULL, pstg)))
|
|
hr = pstg->Commit(STGC_OVERWRITE);
|
|
|
|
pstg->Release();
|
|
}
|
|
}
|
|
|
|
// If that worked, and we once had a moniker, we have to dump it now...
|
|
if (SUCCEEDED(hr) && (m_pmkSourceFile))
|
|
{
|
|
SAFE_RELEASE_INTERFACE(m_pmkSourceFile);
|
|
SAFE_RELEASE_INTERFACE(m_pbctxSourceFile);
|
|
}
|
|
}
|
|
|
|
// If we made a copy to protect on overwrite, either restore or delete it as needed...
|
|
if ((fDoOverwriteOps) && (fFileOpSuccess) && (pwszRename))
|
|
{
|
|
FPerformShellOp((FAILED(hr) ? FO_RENAME : FO_DELETE), pwszRename, pwszFile);
|
|
}
|
|
|
|
// If this is an exisitng file save, or the operation failed, relock the
|
|
// the original file save source...
|
|
if ((m_pstgfile) && !(m_pmkSourceFile) && (m_pwszSourceFile) &&
|
|
((fDoNormalSave) || (FAILED(hr))))
|
|
{
|
|
StgOpenStorage(m_pwszSourceFile, NULL,
|
|
((m_pwszWebResource) ?
|
|
(STGM_READ | STGM_SHARE_DENY_WRITE) :
|
|
(STGM_TRANSACTED | STGM_SHARE_DENY_WRITE | STGM_READWRITE)),
|
|
NULL, 0, &m_pstgSourceFile);
|
|
}
|
|
else if (SUCCEEDED(hr))
|
|
{
|
|
// Otherwise if we succeeded, free any existing file info we have it and
|
|
// save the new file info for later re-saves (and lock)...
|
|
SAFE_FREESTRING(m_pwszSourceFile);
|
|
SAFE_FREESTRING(m_pwszWebResource);
|
|
SAFE_RELEASE_INTERFACE(m_pstmWebResource);
|
|
|
|
// Save the name, and try to lock the file for editing...
|
|
if (m_pwszSourceFile = DsoCopyString(pwszFile))
|
|
{
|
|
m_idxSourceName = CalcDocNameIndex(m_pwszSourceFile);
|
|
|
|
if (!(m_pmkSourceFile) && (m_pstgfile))
|
|
StgOpenStorage(m_pwszSourceFile, NULL,
|
|
(STGM_TRANSACTED | STGM_SHARE_DENY_WRITE | STGM_READWRITE), NULL, 0, &m_pstgSourceFile);
|
|
}
|
|
}
|
|
|
|
if (pwszRename)
|
|
DsoMemFree(pwszRename);
|
|
|
|
if (pwszFullName)
|
|
DsoMemFree(pwszFullName);
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::SaveToURL(LPWSTR pwszURL, BOOL fOverwriteFile, LPWSTR pwszUserName, LPWSTR pwszPassword)
|
|
{
|
|
HRESULT hr = DSO_E_DOCUMENTREADONLY;
|
|
|
|
// If we have no URL to save to and no previously open web stream, fail...
|
|
if ((!pwszURL) && (!m_pstmWebResource))
|
|
return hr;
|
|
|
|
// Save out the user name and password (if provided) for IAuthenticate...
|
|
if (pwszUserName)
|
|
{
|
|
SAFE_FREESTRING(m_pwszUsername);
|
|
m_pwszUsername = DsoCopyString(pwszUserName);
|
|
|
|
SAFE_FREESTRING(m_pwszPassword);
|
|
m_pwszPassword = DsoCopyString(pwszPassword);
|
|
}
|
|
|
|
if ((pwszURL) && (m_pwszWebResource) &&
|
|
(DsoCompareStringsEx(pwszURL, -1, m_pwszWebResource, -1) == CSTR_EQUAL))
|
|
pwszURL = NULL;
|
|
|
|
if (pwszURL)
|
|
{
|
|
IStream *pstmT = NULL;
|
|
LPWSTR pwszFullUrl = NULL;
|
|
LPWSTR pwszTempFile;
|
|
|
|
IStream *pstmBkupStm;
|
|
IStorage *pstgBkupStg;
|
|
LPWSTR pwszBkupFile, pwszBkupUrl;
|
|
UINT idxBkup;
|
|
|
|
if (!GetTempPathForURLDownload(pwszURL, &pwszTempFile))
|
|
return E_INVALIDARG;
|
|
|
|
if (ValidateFileExtension(pwszURL, &pwszFullUrl))
|
|
pwszURL = pwszFullUrl;
|
|
|
|
// We are going to save out the current file info in case of
|
|
// an error we can restore it to do native saves back to open location...
|
|
pstmBkupStm = m_pstmWebResource; m_pstmWebResource = NULL;
|
|
pwszBkupUrl = m_pwszWebResource; m_pwszWebResource = NULL;
|
|
pwszBkupFile = m_pwszSourceFile; m_pwszSourceFile = NULL;
|
|
pstgBkupStg = m_pstgSourceFile; m_pstgSourceFile = NULL;
|
|
idxBkup = m_idxSourceName; m_idxSourceName = 0;
|
|
|
|
// Save the object to a new (temp) file on the local drive...
|
|
if (SUCCEEDED(hr = SaveToFile(pwszTempFile, TRUE)))
|
|
{
|
|
// Then upload from that file...
|
|
hr = IPPUploadWebResource(pwszTempFile, &pstmT, pwszURL, fOverwriteFile);
|
|
}
|
|
|
|
// If both calls succeed, we can free the old file/url location info
|
|
// and save the new information, otherwise restore the old info from backup...
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
SAFE_RELEASE_INTERFACE(pstgBkupStg);
|
|
|
|
if ((pstmBkupStm) && (pwszBkupFile))
|
|
FPerformShellOp(FO_DELETE, pwszBkupFile, NULL);
|
|
|
|
SAFE_RELEASE_INTERFACE(pstmBkupStm);
|
|
SAFE_FREESTRING(pwszBkupUrl);
|
|
SAFE_FREESTRING(pwszBkupFile);
|
|
|
|
m_pstmWebResource = pstmT;
|
|
m_pwszWebResource = DsoCopyString(pwszURL);
|
|
//m_pwszSourceFile already saved in SaveStorageToFile
|
|
//m_pstgSourceFile already saved in SaveStorageToFile
|
|
//m_idxSourceName already calced in SaveStorageToFile;
|
|
|
|
}
|
|
else
|
|
{
|
|
if (m_pstgSourceFile)
|
|
m_pstgSourceFile->Release();
|
|
|
|
if (m_pwszSourceFile)
|
|
{
|
|
FPerformShellOp(FO_DELETE, m_pwszSourceFile, NULL);
|
|
DsoMemFree(m_pwszSourceFile);
|
|
}
|
|
|
|
m_pstmWebResource = pstmBkupStm;
|
|
m_pwszWebResource = pwszBkupUrl;
|
|
m_pwszSourceFile = pwszBkupFile;
|
|
m_pstgSourceFile = pstgBkupStg;
|
|
m_idxSourceName = idxBkup;
|
|
}
|
|
|
|
if (pwszFullUrl)
|
|
DsoMemFree(pwszFullUrl);
|
|
|
|
DsoMemFree(pwszTempFile);
|
|
|
|
}
|
|
else if ((m_pstmWebResource) && (m_pwszSourceFile))
|
|
{
|
|
if (SUCCEEDED(hr = SaveToFile(NULL, TRUE)))
|
|
hr = IPPUploadWebResource(m_pwszSourceFile, &m_pstmWebResource, NULL, TRUE);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::DoOleCommand
|
|
//
|
|
// Calls IOleCommandTarget::Exec on the active object to do a specific
|
|
// command (like Print, SaveCopy, Zoom, etc.).
|
|
//
|
|
STDMETHODIMP CDsoDocObject::DoOleCommand(DWORD dwOleCmdId, DWORD dwOptions, VARIANT* vInParam, VARIANT* vInOutParam)
|
|
{
|
|
HRESULT hr;
|
|
OLECMD cmd = {dwOleCmdId, 0};
|
|
TRACE2("CDsoDocObject::DoOleCommand(cmd=%d, Opts=%d\n", dwOleCmdId, dwOptions);
|
|
|
|
// Can't issue OLECOMMANDs when in print preview mode (object calls us)...
|
|
if (InPrintPreview()) return E_ACCESSDENIED;
|
|
|
|
// The server must support IOleCommandTarget, the CmdID being requested, and
|
|
// the command should be enabled. If this is the case, do the command...
|
|
if ((m_pcmdt) && SUCCEEDED(m_pcmdt->QueryStatus(NULL, 1, &cmd, NULL)) &&
|
|
((cmd.cmdf & OLECMDF_SUPPORTED) && (cmd.cmdf & OLECMDF_ENABLED)))
|
|
{
|
|
TRACE1("QueryStatus say supported = 0x%X\n", cmd.cmdf);
|
|
|
|
// Do the command asked by caller on default command group...
|
|
hr = m_pcmdt->Exec(NULL, cmd.cmdID, dwOptions, vInParam, vInOutParam);
|
|
TRACE1("DocObj_IOleCommandTarget::Exec() = 0x%X\n", hr);
|
|
|
|
if ((dwOptions == OLECMDEXECOPT_PROMPTUSER))
|
|
{
|
|
// Handle bug issue for PPT when printing using prompt...
|
|
if ((hr == E_INVALIDARG) && (cmd.cmdID == OLECMDID_PRINT) && IsPPTObject())
|
|
{
|
|
ODS("Retry command for PPT\n");
|
|
hr = m_pcmdt->Exec(NULL, cmd.cmdID, OLECMDEXECOPT_DODEFAULT, vInParam, vInOutParam);
|
|
TRACE1("DocObj_IOleCommandTarget::Exec() = 0x%X\n", hr);
|
|
}
|
|
|
|
// If user canceled an Office dialog, that's OK...
|
|
if (hr == 0x80040103)
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE1("Command Not supportted (%d)\n", cmd.cmdf);
|
|
hr = DSO_E_COMMANDNOTSUPPORTED;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::Close
|
|
//
|
|
// Close down the object and disconnect us from any handlers/proxies.
|
|
//
|
|
STDMETHODIMP CDsoDocObject::Close()
|
|
{
|
|
HRESULT hr;
|
|
|
|
ODS("CDsoDocObject::Close\n");
|
|
m_fInClose = TRUE;
|
|
|
|
// Make sure we are not in print preview before close...
|
|
if (InPrintPreview())
|
|
ExitPrintPreview(TRUE);
|
|
|
|
// Go ahead an IP deactivate the object...
|
|
hr = IPDeactivateView();
|
|
|
|
SAFE_RELEASE_INTERFACE(m_pprtprv);
|
|
SAFE_RELEASE_INTERFACE(m_pcmdt);
|
|
SAFE_RELEASE_INTERFACE(m_pdocv);
|
|
SAFE_RELEASE_INTERFACE(m_pipactive);
|
|
SAFE_RELEASE_INTERFACE(m_pipobj);
|
|
|
|
// Release the OLE object and cleanup...
|
|
if (m_pole)
|
|
{
|
|
// Free running lock if we set it...
|
|
FreeRunningLock();
|
|
|
|
// Tell server to release our client site...
|
|
m_pole->SetClientSite(NULL);
|
|
|
|
// Finally, close the object and release the pointer...
|
|
hr = m_pole->Close(OLECLOSE_NOSAVE);
|
|
|
|
if (m_fDisconnectOnQuit)
|
|
CoDisconnectObject((IUnknown*)m_pole, 0);
|
|
|
|
SAFE_RELEASE_INTERFACE(m_pole);
|
|
}
|
|
|
|
SAFE_RELEASE_INTERFACE(m_pstgSourceFile);
|
|
SAFE_RELEASE_INTERFACE(m_pmkSourceFile);
|
|
SAFE_RELEASE_INTERFACE(m_pbctxSourceFile);
|
|
|
|
// Free any temp file we might have...
|
|
if ((m_pstmWebResource) && (m_pwszSourceFile))
|
|
FPerformShellOp(FO_DELETE, m_pwszSourceFile, NULL);
|
|
|
|
SAFE_RELEASE_INTERFACE(m_pstmWebResource);
|
|
SAFE_FREESTRING(m_pwszWebResource);
|
|
SAFE_FREESTRING(m_pwszSourceFile);
|
|
m_idxSourceName = 0;
|
|
|
|
if (m_fDisconnectOnQuit)
|
|
{
|
|
CoDisconnectObject((IUnknown*)this, 0);
|
|
m_fDisconnectOnQuit = FALSE;
|
|
}
|
|
|
|
SAFE_RELEASE_INTERFACE(m_pstmview);
|
|
SAFE_RELEASE_INTERFACE(m_pstgfile);
|
|
|
|
ClearMergedMenu();
|
|
m_fInClose = FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject Notification Functions - The OCX should call these to
|
|
// let the doc site update the object as needed.
|
|
//
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::OnNotifySizeChange
|
|
//
|
|
// Resets the size of the site window and tells UI active object to
|
|
// resize as well. If we are UI active, we'll call ResizeBorder to
|
|
// re-negotiate toolspace (allow toolbars to shrink and grow), otherwise
|
|
// we'll just set the IP active view rect (minus any toolspace, which
|
|
// should be none since object is not UI active!).
|
|
//
|
|
STDMETHODIMP_(void) CDsoDocObject::OnNotifySizeChange(LPRECT prc)
|
|
{
|
|
RECT rc;
|
|
|
|
SetRect(&rc, 0, 0, (prc->right - prc->left), (prc->bottom - prc->top));
|
|
if (rc.right < 0) rc.right = 0;
|
|
if (rc.top < 0) rc.top = 0;
|
|
|
|
// First, resize our frame site window tot he new size (don't change focus)...
|
|
if (m_hwnd)
|
|
{
|
|
m_rcViewRect = *prc;
|
|
|
|
SetWindowPos(m_hwnd, NULL, m_rcViewRect.left, m_rcViewRect.top,
|
|
rc.right, rc.bottom, SWP_NOACTIVATE | SWP_NOZORDER);
|
|
|
|
UpdateWindow(m_hwnd);
|
|
}
|
|
|
|
// If we have an active object (i.e., Document is still UI active) we should
|
|
// tell it of the resize so it can re-negotiate border space...
|
|
if ((m_fObjectUIActive) && (m_pipactive))
|
|
{
|
|
m_pipactive->ResizeBorder(&rc, (IOleInPlaceUIWindow*)&m_xOleInPlaceFrame, TRUE);
|
|
}
|
|
else if ((m_fObjectIPActive) && (m_pdocv))
|
|
{
|
|
rc.left += m_bwToolSpace.left; rc.right -= m_bwToolSpace.right;
|
|
rc.top += m_bwToolSpace.top; rc.bottom -= m_bwToolSpace.bottom;
|
|
m_pdocv->SetRect(&rc);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::OnNotifyAppActivate
|
|
//
|
|
// Notify doc object when the top-level frame window goes active and
|
|
// deactive so it can handle window focs and paiting correctly. Failure
|
|
// to not forward this notification leads to bad behavior.
|
|
//
|
|
STDMETHODIMP_(void) CDsoDocObject::OnNotifyAppActivate(BOOL fActive, DWORD dwThreadID)
|
|
{
|
|
// This is critical for DocObject servers, so forward these messages
|
|
// when the object is UI active...
|
|
if (m_pipactive)
|
|
{
|
|
// We should always tell obj server when our frame activates, but
|
|
// don't tell it to go deactive if the thread gaining focus is
|
|
// the server's since our frame may have lost focus because of
|
|
// a top-level modeless dialog (ex., the RefEdit dialog of Excel)...
|
|
if (!m_fObjectInModalCondition)
|
|
m_pipactive->OnFrameWindowActivate(fActive);
|
|
}
|
|
|
|
m_fAppWindowActive = fActive;
|
|
}
|
|
|
|
STDMETHODIMP_(void) CDsoDocObject::OnNotifyControlFocus(BOOL fGotFocus)
|
|
{
|
|
HWND hwnd;
|
|
|
|
// For UI Active DocObject server, we will tell them we gain/lose control focus...
|
|
if ((m_pipactive) && (!m_fObjectInModalCondition))
|
|
{
|
|
// TODO: Normally we would notify object of loss of control focus (such as user
|
|
// moving focus from framer control to another text box or something on the same
|
|
// form), but this can cause PPT to drop its toolbar and XL to drop its formula
|
|
// bar, so we are skipping this call. We really should determine if it is needed
|
|
// for another host (like Visio or something?) but that is to do...
|
|
//
|
|
// m_pipactive->OnDocWindowActivate(fGotFocus);
|
|
|
|
// We should forward the focus to the active window if window with the focus
|
|
// is not already one that is parented to us...
|
|
if ((fGotFocus) && !IsWindowChild(m_hwnd, GetFocus()) &&
|
|
SUCCEEDED(m_pipactive->GetWindow(&hwnd)))
|
|
SetFocus(hwnd);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::OnNotifyPaletteChanged
|
|
//
|
|
// Give the object first chance at realizing a palette. Important on
|
|
// 256 color machines, but not so critical these days when everyone is
|
|
// running full 32-bit True Color graphic cards.
|
|
//
|
|
STDMETHODIMP_(void) CDsoDocObject::OnNotifyPaletteChanged(HWND hwndPalChg)
|
|
{
|
|
if ((m_fObjectUIActive) && (m_hwndUIActiveObj))
|
|
SendMessage(m_hwndUIActiveObj, WM_PALETTECHANGED, (WPARAM)hwndPalChg, 0L);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::OnNotifyChangeToolState
|
|
//
|
|
// This should be called to get object to show/hide toolbars as needed.
|
|
//
|
|
STDMETHODIMP_(void) CDsoDocObject::OnNotifyChangeToolState(BOOL fShowTools)
|
|
{
|
|
// Can't change toolbar state in print preview (sorry)...
|
|
if (InPrintPreview()) return;
|
|
|
|
// If we want to show/hide toolbars, we can do the following...
|
|
if (fShowTools != (BOOL)m_fDisplayTools)
|
|
{
|
|
OLECMD cmd;
|
|
cmd.cmdID = OLECMDID_HIDETOOLBARS;
|
|
|
|
m_fDisplayTools = fShowTools;
|
|
|
|
// Use IOleCommandTarget(OLECMDID_HIDETOOLBARS) to toggle on/off. We have
|
|
// to check that server supports it and if its state matches our own so
|
|
// when toggle, we do the correct thing by the user...
|
|
if ((m_pcmdt) && SUCCEEDED(m_pcmdt->QueryStatus(NULL, 1, &cmd, NULL)) &&
|
|
((cmd.cmdf & OLECMDF_SUPPORTED) || (cmd.cmdf & OLECMDF_ENABLED)))
|
|
{
|
|
if (((m_fDisplayTools) && ((cmd.cmdf & OLECMDF_LATCHED) == OLECMDF_LATCHED)) ||
|
|
(!(m_fDisplayTools) && !((cmd.cmdf & OLECMDF_LATCHED) == OLECMDF_LATCHED)))
|
|
{
|
|
m_pcmdt->Exec(NULL, OLECMDID_HIDETOOLBARS, OLECMDEXECOPT_PROMPTUSER, NULL, NULL);
|
|
}
|
|
else if (m_fDisplayTools && (IsWordObject() || IsPPTObject()))
|
|
{
|
|
// HACK: Word and PowerPoint 2007 do not report the correct latched state for the ribbon,
|
|
// so if we are trying to show the tools after hiding them, they will say they are already
|
|
// visible when the ribbon is not. This is an apparant trick they are using to force the
|
|
// ribbon visible in IE embed cases, but it is causing us problems.
|
|
m_pcmdt->Exec(NULL, OLECMDID_HIDETOOLBARS, OLECMDEXECOPT_PROMPTUSER, NULL, NULL);
|
|
}
|
|
|
|
// There can be focus issues when turning them off, so make sure
|
|
// the object is on top of the z-order...
|
|
if ((!m_fDisplayTools) && (m_hwndIPObject))
|
|
BringWindowToTop(m_hwndIPObject);
|
|
|
|
// If user toggles off the toolbar while the object is UI active, and
|
|
// we are not still in activation process, we need to explictly tell Office
|
|
// apps to also hide the "Web" toolbar. For Office, OLECMDID_HIDETOOLBARS puts
|
|
// the app into a "web view" which (in some apps) brings up the web toolbar.
|
|
// Since we intend to have no tools, we have to turn it off by code...
|
|
if ((m_fObjectUIActive) && (m_fObjectActivateComplete))
|
|
TurnOffWebToolbar(!m_fDisplayTools);
|
|
|
|
}
|
|
else if (m_pdocv)
|
|
{
|
|
// If we have a DocObj server, but no IOleCommandTarget, do things the hard
|
|
// way and resize. When server attempts to resize window it will have to
|
|
// re-negotiate BorderSpace and we fail there, so server "should" not
|
|
// display its tools (at least that is the idea!<g>)...
|
|
RECT rc; GetClientRect(m_hwnd, &rc);
|
|
MapWindowPoints(m_hwnd, m_hwndCtl, (LPPOINT)&rc, 2);
|
|
OnNotifySizeChange(&rc);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject Protected Functions -- Helpers
|
|
//
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::InstantiateDocObjectServer (protected)
|
|
//
|
|
// Startup OLE Document Object server and get IOleObject pointer.
|
|
//
|
|
STDMETHODIMP CDsoDocObject::InstantiateDocObjectServer(REFCLSID rclsid, IOleObject **ppole)
|
|
{
|
|
HRESULT hr;
|
|
IUnknown *punk = NULL;
|
|
|
|
ODS("CDsoDocObject::InstantiateDocObjectServer()\n");
|
|
|
|
// We perform custom create in order of local server then inproc server...
|
|
if (SUCCEEDED(hr = CoCreateInstance(rclsid, NULL, CLSCTX_LOCAL_SERVER, IID_IUnknown, (void**)&punk)) ||
|
|
SUCCEEDED(hr = CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&punk)))
|
|
{
|
|
// Ask for IOleObject interface...
|
|
if (SUCCEEDED(hr = punk->QueryInterface(IID_IOleObject, (void**)ppole)))
|
|
{
|
|
// TODO: Add strong connection to remote server (IExternalConnection??)...
|
|
}
|
|
punk->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::CreateObjectStorage (protected)
|
|
//
|
|
// Makes the internal IStorage to host the object and assigns the CLSID.
|
|
//
|
|
STDMETHODIMP CDsoDocObject::CreateObjectStorage(REFCLSID rclsid)
|
|
{
|
|
HRESULT hr;
|
|
LPWSTR pwszName;
|
|
DWORD dwid;
|
|
CHAR szbuf[256];
|
|
|
|
if ((!m_pstgroot)) return E_UNEXPECTED;
|
|
|
|
// Next, create a new object storage (with unique name) in our
|
|
// temp root storage "file" (this keeps an OLE integrity some servers
|
|
// need to function correctly instead of IP activating from file directly).
|
|
|
|
// We make a fake object storage name...
|
|
dwid = ((rclsid.Data1)|GetTickCount());
|
|
wsprintf(szbuf, "OLEDocument%X", dwid);
|
|
|
|
if (!(pwszName = DsoConvertToLPWSTR(szbuf)))
|
|
return E_OUTOFMEMORY;
|
|
|
|
// Create the sub-storage...
|
|
hr = m_pstgroot->CreateStorage(pwszName,
|
|
STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &m_pstgfile);
|
|
|
|
DsoMemFree(pwszName);
|
|
|
|
if (FAILED(hr)) return hr;
|
|
|
|
// We'll also create a stream for OLE view settings (non-critical)...
|
|
if (pwszName = DsoConvertToLPWSTR(szbuf))
|
|
{
|
|
m_pstgroot->CreateStream(pwszName,
|
|
STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &m_pstmview);
|
|
DsoMemFree(pwszName);
|
|
}
|
|
|
|
// Finally, write out the CLSID for the new substorage...
|
|
hr = WriteClassStg(m_pstgfile, rclsid);
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::SaveObjectStorage (protected)
|
|
//
|
|
// Saves the object back to the internal IStorage. Returns S_FALSE if
|
|
// there is no file storage for this document (it depends on how file
|
|
// was loaded). In most cases we should have one, and this copies data
|
|
// into the internal storage for save.
|
|
//
|
|
STDMETHODIMP CDsoDocObject::SaveObjectStorage()
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
IPersistStorage *pipstg = NULL;
|
|
|
|
// Got to have object to save state...
|
|
if (!m_pole) return E_UNEXPECTED;
|
|
|
|
// If we have file storage, ask for IPersist and Save (commit changes)...
|
|
if ((m_pstgfile) &&
|
|
SUCCEEDED(hr = m_pole->QueryInterface(IID_IPersistStorage, (void**)&pipstg)))
|
|
{
|
|
if (SUCCEEDED(hr = pipstg->Save(m_pstgfile, TRUE)))
|
|
hr = pipstg->SaveCompleted(NULL);
|
|
|
|
hr = m_pstgfile->Commit(STGC_DEFAULT);
|
|
pipstg->Release();
|
|
}
|
|
|
|
// Go ahead and save the view state if view still active (non-critical)...
|
|
if ((m_pdocv) && (m_pstmview))
|
|
{
|
|
m_pdocv->SaveViewState(m_pstmview);
|
|
m_pstmview->Commit(STGC_DEFAULT);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::SaveDocToMoniker (protected)
|
|
//
|
|
// Saves document to location spcified by the moniker passed. The server
|
|
// needs to support either IPersistMoniker or IPersistFile. This is used
|
|
// primarily when document was opened by moniker instead of storage.
|
|
//
|
|
STDMETHODIMP CDsoDocObject::SaveDocToMoniker(IMoniker *pmk, IBindCtx *pbc, BOOL fKeepLock)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
IPersistMoniker *prstmk;
|
|
LPOLESTR pwszFullName = NULL;
|
|
|
|
CHECK_NULL_RETURN(m_pole, E_UNEXPECTED);
|
|
CHECK_NULL_RETURN(pmk, E_POINTER);
|
|
|
|
// Get IPersistMoniker interface and ask it to save context if it existing moniker...
|
|
if (SUCCEEDED(hr = m_pole->QueryInterface(IID_IPersistMoniker, (void**)&prstmk)))
|
|
{
|
|
hr = prstmk->Save(pmk, pbc, fKeepLock);
|
|
prstmk->Release();
|
|
}
|
|
|
|
// If that failed to work, switch to IPersistFile and use full path to the new file...
|
|
if (FAILED(hr) && SUCCEEDED(hr = pmk->GetDisplayName(pbc, NULL, &pwszFullName)))
|
|
{
|
|
hr = SaveDocToFile(pwszFullName, fKeepLock);
|
|
CoTaskMemFree(pwszFullName);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::SaveDocToFile (protected)
|
|
//
|
|
// Saves document to location spcified by the path passed. The server
|
|
// needs to support either IPersistFile.
|
|
//
|
|
STDMETHODIMP CDsoDocObject::SaveDocToFile(LPWSTR pwszFullName, BOOL fKeepLock)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
IPersistFile *pipfile = NULL;
|
|
|
|
BOOL fSameAsOpen = ((m_pwszSourceFile) &&
|
|
DsoCompareStringsEx(pwszFullName, lstrlenW(pwszFullName),
|
|
m_pwszSourceFile, lstrlenW(m_pwszSourceFile)) == CSTR_EQUAL);
|
|
|
|
if (SUCCEEDED(hr = m_pole->QueryInterface(IID_IPersistFile, (void**)&pipfile)))
|
|
{
|
|
#ifdef DSO_WORD12_PERSIST_BUG
|
|
LPOLESTR pwszWordCurFile = NULL;
|
|
// HACK: Word 2007 RTM has a bug in its IPersistFile::Save method which will cause it to save
|
|
// to the old file location and not the new one, so if the document being saved is a Word
|
|
// object and we are saving to a new file, then got to run this hack to copy the saved bits
|
|
// to the correct location...
|
|
if (IsWordObject())
|
|
{
|
|
hr = pipfile->GetCurFile(&pwszWordCurFile);
|
|
if ((fSameAsOpen) && (pwszWordCurFile))
|
|
{
|
|
// Check again if Word thinks the file is the same as we do...
|
|
fSameAsOpen = (DsoCompareStringsEx(pwszFullName, lstrlenW(pwszFullName),
|
|
pwszWordCurFile, lstrlenW(pwszWordCurFile)) == CSTR_EQUAL);
|
|
if (fSameAsOpen)
|
|
{ // If it is the same file after all, we don't need to do the hack...
|
|
CoTaskMemFree(pwszWordCurFile);
|
|
pwszWordCurFile = NULL;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Do the save using file path or NULL if we are saving to current file...
|
|
hr = pipfile->Save((fSameAsOpen ? NULL : pwszFullName), fKeepLock);
|
|
|
|
#ifdef DSO_WORD12_PERSIST_BUG
|
|
// HACK: If we have the pwszWordCurFile, we'll assume Word 12 RTM might have saved this, and we
|
|
// need to check if Word saved to the right path or not. So check the paths, and if the new
|
|
// file is not there, copy the file Word saved to the new location...
|
|
if (pwszWordCurFile)
|
|
{
|
|
if (SUCCEEDED(hr) && !FFileExists(pwszFullName) && FFileExists(pwszWordCurFile))
|
|
{
|
|
if (!FPerformShellOp(FO_COPY, pwszWordCurFile, pwszFullName))
|
|
hr = E_ACCESSDENIED;
|
|
}
|
|
CoTaskMemFree(pwszWordCurFile);
|
|
}
|
|
// Hopefully, Word should have this fixed by Office 2007 SP1 and we can remove this hack!
|
|
#endif
|
|
pipfile->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::ValidateDocObjectServer (protected)
|
|
//
|
|
// Quick validation check to see if CLSID is for DocObject server.
|
|
//
|
|
// Officially, the only way to determine if a server supports ActiveX
|
|
// Document embedding is to IP activate it and ask for IOleDocument,
|
|
// but that means going through the IP process just to fail if IOleDoc
|
|
// is not supported. Therefore, we are going to rely on the server's
|
|
// honesty in setting its reg keys to include the "DocObject" sub key
|
|
// under their CLSID.
|
|
//
|
|
// This is 99% accurate. For those servers that fail, too bad charlie!
|
|
//
|
|
STDMETHODIMP CDsoDocObject::ValidateDocObjectServer(REFCLSID rclsid)
|
|
{
|
|
HRESULT hr = DSO_E_INVALIDSERVER;
|
|
CHAR szKeyCheck[256];
|
|
LPSTR pszClsid;
|
|
HKEY hkey;
|
|
|
|
// We don't handle MSHTML even though it is DocObject server. If you plan
|
|
// to view web pages in browser-like context, best to use WebBrowser control...
|
|
if (rclsid == CLSID_MSHTML_DOCUMENT)
|
|
return hr;
|
|
|
|
// Convert the CLSID to a string and check for DocObject sub key...
|
|
if (pszClsid = DsoCLSIDtoLPSTR(rclsid))
|
|
{
|
|
wsprintf(szKeyCheck, "CLSID\\%s\\DocObject", pszClsid);
|
|
|
|
if (RegOpenKeyEx(HKEY_CLASSES_ROOT, szKeyCheck, 0, KEY_READ, &hkey) == ERROR_SUCCESS)
|
|
{
|
|
hr = S_OK;
|
|
RegCloseKey(hkey);
|
|
}
|
|
|
|
DsoMemFree(pszClsid);
|
|
}
|
|
else hr = E_OUTOFMEMORY;
|
|
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::GetDocumentTypeAndFileExtension
|
|
//
|
|
// Returns default file type and extension for a save of embedded object.
|
|
//
|
|
STDMETHODIMP_(BOOL) CDsoDocObject::GetDocumentTypeAndFileExtension(WCHAR** ppwszFileType, WCHAR** ppwszFileExt)
|
|
{
|
|
DWORD dwType, dwSize;
|
|
LPWSTR pwszExt = NULL;
|
|
LPWSTR pwszType = NULL;
|
|
LPSTR pszType = NULL;
|
|
LPSTR pszClsid;
|
|
CHAR szkey[255];
|
|
CHAR szbuf[255];
|
|
HKEY hk;
|
|
|
|
if ((ppwszFileType == NULL) && (ppwszFileExt == NULL))
|
|
return FALSE;
|
|
|
|
pszClsid = DsoCLSIDtoLPSTR(m_clsidObject);
|
|
if (!pszClsid) return FALSE;
|
|
|
|
wsprintf(szkey, "CLSID\\%s\\DefaultExtension", pszClsid);
|
|
if (RegOpenKeyEx(HKEY_CLASSES_ROOT, szkey, 0, KEY_READ, &hk) == ERROR_SUCCESS)
|
|
{
|
|
LPSTR pszT = szbuf; szbuf[0] = '\0'; dwSize = 255;
|
|
if (RegQueryValueEx(hk, NULL, 0, &dwType, (BYTE*)pszT, &dwSize) == ERROR_SUCCESS)
|
|
{
|
|
while (*(pszT++) && (*pszT != ','))
|
|
(void)(0);
|
|
|
|
if (*pszT == ',')
|
|
(pszType = pszT)++;
|
|
|
|
*pszT = '\0';
|
|
}
|
|
else lstrcpy(szbuf, ".ole");
|
|
|
|
RegCloseKey(hk);
|
|
}
|
|
else lstrcpy(szbuf, ".ole");
|
|
|
|
pwszExt = DsoConvertToLPWSTR(szbuf);
|
|
if (ppwszFileExt) *ppwszFileExt = pwszExt;
|
|
|
|
if (ppwszFileType)
|
|
{
|
|
ULONG cb1, cb2;
|
|
LPWSTR pwszCombined;
|
|
CHAR szUnknownType[255];
|
|
|
|
if (!(*pszType))
|
|
{
|
|
szUnknownType[0] = '\0';
|
|
wsprintf(szUnknownType, "Native Document (*%s)", szbuf);
|
|
pszType = szUnknownType;
|
|
}
|
|
pwszType = DsoConvertToLPWSTR(pszType);
|
|
|
|
cb1 = lstrlenW(pwszExt);
|
|
cb2 = lstrlenW(pwszType);
|
|
|
|
pwszCombined = (LPWSTR)DsoMemAlloc(((cb1 + cb2 + 4) * sizeof(WCHAR)));
|
|
if (pwszCombined)
|
|
{
|
|
memcpy(&pwszCombined[0], pwszType,(cb2 * sizeof(WCHAR)));
|
|
pwszCombined[cb2] = L'\0';
|
|
|
|
pwszCombined[cb2 + 1] = L'*';
|
|
memcpy(&pwszCombined[cb2 + 2], pwszExt, (cb1 * sizeof(WCHAR)));
|
|
pwszCombined[cb2 + 2 + cb1] = L'\0';
|
|
pwszCombined[cb2 + 3 + cb1] = L'\0';
|
|
|
|
DsoMemFree(pwszType);
|
|
}
|
|
|
|
*ppwszFileType = ((pwszCombined) ? pwszCombined : pwszType);
|
|
}
|
|
|
|
if ((ppwszFileExt == NULL) && (pwszExt))
|
|
DsoMemFree(pwszExt);
|
|
|
|
DsoMemFree(pszClsid);
|
|
return (((ppwszFileExt) && (*ppwszFileExt)) ||
|
|
((ppwszFileType) && (*ppwszFileType)));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::ValidateFileExtension (protected)
|
|
//
|
|
// Adds a default extension to save file path if user didn't provide
|
|
// one (uses CLSID and registry to determine default extension).
|
|
//
|
|
STDMETHODIMP_(BOOL) CDsoDocObject::ValidateFileExtension(WCHAR* pwszFile, WCHAR** ppwszOut)
|
|
{
|
|
BOOL fHasExt = FALSE;
|
|
BOOL fChangedExt = FALSE;
|
|
LPWSTR pwszT;
|
|
LPWSTR pwszExt = NULL;
|
|
DWORD dw;
|
|
|
|
if ((pwszFile) && (dw = lstrlenW(pwszFile)) && (ppwszOut))
|
|
{
|
|
*ppwszOut = NULL;
|
|
|
|
pwszT = (pwszFile + dw);
|
|
while ((pwszT != pwszFile) &&
|
|
(*(--pwszT)) && ((*pwszT != L'\\') && (*pwszT != L'/')))
|
|
{
|
|
if (*pwszT == L'.') fHasExt = TRUE;
|
|
}
|
|
|
|
if (!(fHasExt) && GetDocumentTypeAndFileExtension(NULL, &pwszExt))
|
|
{
|
|
*ppwszOut = DsoCopyStringCat(pwszFile, pwszExt);
|
|
fChangedExt = ((*ppwszOut) != NULL);
|
|
}
|
|
}
|
|
|
|
return fChangedExt;
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::EnsureOleServerRunning (protected)
|
|
//
|
|
// Verifies the DocObject server is running and is optionally locked
|
|
// while the embedding is taking place.
|
|
//
|
|
STDMETHODIMP CDsoDocObject::EnsureOleServerRunning(BOOL fLockRunning)
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
IBindCtx *pbc;
|
|
IRunnableObject *pro;
|
|
IOleContainer *pocnt;
|
|
BIND_OPTS bnd = {sizeof(BIND_OPTS), BIND_MAYBOTHERUSER, (STGM_READWRITE|STGM_SHARE_EXCLUSIVE), 10000};
|
|
|
|
TRACE1("CDsoDocObject::EnsureOleServerRunning(%d)\n", (DWORD)fLockRunning);
|
|
if (m_pole == NULL) return E_FAIL;
|
|
|
|
// If we are already locked, don't need to do this again...
|
|
if (m_fLockedServerRunning)
|
|
return hr;
|
|
|
|
// Create a bind ctx...
|
|
if (FAILED(CreateBindCtx(0, &pbc)))
|
|
return E_UNEXPECTED;
|
|
|
|
// Setup default bind options for the run operation...
|
|
pbc->SetBindOptions(&bnd);
|
|
|
|
// Get IRunnableObject and set server to run as OLE object. We check the
|
|
// running state first since this is proper OLE, but note that this is not
|
|
// returned from out-of-proc server, but the in-proc handler. Also note, we
|
|
// specify a timeout in case the object never starts, and "check" the object
|
|
// runs without IMessageFilter errors...
|
|
if (SUCCEEDED(m_pole->QueryInterface(IID_IRunnableObject, (void**)&pro)))
|
|
{
|
|
|
|
// If the object is not currently running, let's run it...
|
|
if (!(pro->IsRunning()))
|
|
hr = pro->Run(pbc);
|
|
|
|
// Set the object server as a contained object (i.e., OLE object)...
|
|
pro->SetContainedObject(TRUE);
|
|
|
|
// Lock running if desired...
|
|
if (fLockRunning)
|
|
m_fLockedServerRunning = SUCCEEDED(pro->LockRunning(TRUE, TRUE));
|
|
|
|
pro->Release();
|
|
}
|
|
else if (SUCCEEDED(m_pole->QueryInterface(IID_IOleContainer, (void**)&pocnt)))
|
|
{
|
|
if (fLockRunning)
|
|
m_fLockedServerRunning = SUCCEEDED(pocnt->LockContainer(TRUE));
|
|
|
|
pocnt->Release();
|
|
}
|
|
|
|
pbc->Release();
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::FreeRunningLock (protected)
|
|
//
|
|
// Free any previous lock made by EnsureOleServerRunning.
|
|
//
|
|
STDMETHODIMP_(void) CDsoDocObject::FreeRunningLock()
|
|
{
|
|
IRunnableObject *pro;
|
|
IOleContainer *pocnt;
|
|
|
|
ODS("CDsoDocObject::FreeRunningLock(%d)\n");
|
|
ASSERT(m_pole);
|
|
|
|
// Don't do anything if we didn't lock the server...
|
|
if (m_fLockedServerRunning == FALSE)
|
|
return;
|
|
|
|
// Get IRunnableObject and free lock...
|
|
if (SUCCEEDED(m_pole->QueryInterface(IID_IRunnableObject, (void**)&pro)))
|
|
{
|
|
pro->LockRunning(FALSE, TRUE);
|
|
pro->Release();
|
|
}
|
|
else if (SUCCEEDED(m_pole->QueryInterface(IID_IOleContainer, (void**)&pocnt)))
|
|
{
|
|
pocnt->LockContainer(FALSE);
|
|
pocnt->Release();
|
|
}
|
|
|
|
m_fLockedServerRunning = FALSE;
|
|
return;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::SetRunningServerLock
|
|
//
|
|
// Sets an external lock on the running object server.
|
|
//
|
|
STDMETHODIMP CDsoDocObject::SetRunningServerLock(BOOL fLock)
|
|
{
|
|
HRESULT hr;
|
|
TRACE1("CDsoDocObject::SetRunningServerLock(%d)\n", (DWORD)fLock);
|
|
|
|
// Word doesn't obey normal running lock, but does have method to explicitly
|
|
// lock the server for mail, so we'll use that in the Word case...
|
|
if (IsWordObject())
|
|
{
|
|
IClassFactory *pcf = NULL;
|
|
interface ifoo : public IUnknown
|
|
{
|
|
STDMETHOD(_uncall)() PURE;
|
|
STDMETHOD(lock)(BOOL f) PURE;
|
|
} *pi = NULL;
|
|
const GUID iidifoo = {0x0006729A, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
|
|
|
|
SEH_TRY
|
|
hr = CoGetClassObject(CLSID_WORD_DOCUMENT_DOC, CLSCTX_LOCAL_SERVER, NULL, IID_IClassFactory, (void**)&pcf);
|
|
if (SUCCEEDED(hr) && (pcf))
|
|
{
|
|
hr = pcf->QueryInterface(iidifoo, (void**)&pi);
|
|
if (SUCCEEDED(hr) && (pi))
|
|
{
|
|
hr = pi->lock(fLock);
|
|
pi->Release();
|
|
}
|
|
pcf->Release();
|
|
}
|
|
SEH_EXCEPT(hr)
|
|
}
|
|
else
|
|
{
|
|
if (fLock)
|
|
{
|
|
hr = EnsureOleServerRunning(TRUE);
|
|
}
|
|
else
|
|
{
|
|
hr = (FreeRunningLock(), S_OK);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::CreateIPPBindResource (protected)
|
|
//
|
|
// Returns an instance of MSDAIPP URL resource provider which is used by
|
|
// Office/Windows for Web Folders (DAV/FPSE server).
|
|
//
|
|
STDMETHODIMP_(IUnknown*) CDsoDocObject::CreateIPPBindResource()
|
|
{
|
|
HRESULT hr;
|
|
IDBProperties* pdbprops = NULL;
|
|
IBindResource* pres = NULL;
|
|
DBPROPSET rdbpset;
|
|
DBPROP rdbp[4];
|
|
BSTR bstrLock;
|
|
DWORD dw = 256;
|
|
CHAR szUserName[256];
|
|
|
|
if (FAILED(CoCreateInstance(CLSID_MSDAIPP_BINDER, NULL,
|
|
CLSCTX_INPROC, IID_IDBProperties, (void**)&pdbprops)))
|
|
return NULL;
|
|
|
|
bstrLock = (GetUserName(szUserName, &dw) ? DsoConvertToBSTR(szUserName) : NULL);
|
|
|
|
memset(rdbp, 0, sizeof(4 * sizeof(DBPROP)));
|
|
|
|
rdbpset.cProperties = 4;
|
|
rdbpset.guidPropertySet = DBPROPSET_DBINIT;
|
|
rdbpset.rgProperties = rdbp;
|
|
|
|
rdbp[0].dwPropertyID = DBPROP_INIT_BINDFLAGS;
|
|
rdbp[0].vValue.vt = VT_I4;
|
|
rdbp[0].vValue.lVal = DBBINDURLFLAG_OUTPUT;
|
|
|
|
rdbp[1].dwPropertyID = DBPROP_INIT_LOCKOWNER;
|
|
rdbp[1].vValue.vt = VT_BSTR;
|
|
rdbp[1].vValue.bstrVal = bstrLock;
|
|
|
|
rdbp[2].dwPropertyID = DBPROP_INIT_LCID;
|
|
rdbp[2].vValue.vt = VT_I4;
|
|
rdbp[2].vValue.lVal = GetThreadLocale();
|
|
|
|
rdbp[3].dwPropertyID = DBPROP_INIT_PROMPT;
|
|
rdbp[3].vValue.vt = VT_I2;
|
|
rdbp[3].vValue.iVal = DBPROMPT_COMPLETE;
|
|
|
|
if (pdbprops->SetProperties(1, &rdbpset) == S_OK)
|
|
{
|
|
#ifdef DSO_MSDAIPP_USE_DAVONLY
|
|
BSTR bstrClsid = SysAllocString(L"{9FECD570-B9D4-11D1-9C78-0000F875AC61}");
|
|
if (bstrClsid)
|
|
{
|
|
rdbpset.cProperties = 1;
|
|
rdbpset.guidPropertySet = DBPROPSET_MSDAIPP_INIT;
|
|
rdbpset.rgProperties = rdbp;
|
|
|
|
rdbp[0].dwPropertyID = 6; // DBPROP_INIT_PROTOCOLPROVIDER;
|
|
rdbp[0].vValue.vt = VT_BSTR;
|
|
rdbp[0].vValue.bstrVal = bstrClsid;
|
|
|
|
hr = pdbprops->SetProperties(1, &rdbpset);
|
|
SysFreeString(bstrClsid);
|
|
}
|
|
#endif
|
|
hr = pdbprops->QueryInterface(IIDX_IBindResource, (void**)&pres);
|
|
}
|
|
|
|
SAFE_FREEBSTR(bstrLock);
|
|
pdbprops->Release();
|
|
|
|
return (IUnknown*)pres;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::IPPDownloadWebResource (protected)
|
|
//
|
|
// Downloads the file specified by the URL to the given temp file. Locks
|
|
// the web resource for editing if ppstmKeepForSave is requested.
|
|
//
|
|
STDMETHODIMP CDsoDocObject::IPPDownloadWebResource(LPWSTR pwszURL, LPWSTR pwszFile, IStream** ppstmKeepForSave)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
IStream *pstm = NULL;
|
|
IBindResource *pres = NULL;
|
|
BYTE *rgbBuf;
|
|
DWORD dwStatus, dwBindFlags;
|
|
ULONG cbRead, cbWritten;
|
|
HANDLE hFile;
|
|
|
|
// Sanity check...
|
|
if ((pwszURL == NULL) || (pwszFile == NULL) || (ppstmKeepForSave == NULL))
|
|
return E_INVALIDARG;
|
|
|
|
// We need MSDAIPP for full write access...
|
|
if (!(m_punkIPPResource) && !(m_punkIPPResource = CreateIPPBindResource()))
|
|
return DSO_E_REQUIRESMSDAIPP;
|
|
|
|
rgbBuf = new BYTE[10240]; //a 10-k buffer for reading
|
|
if (!rgbBuf) return E_OUTOFMEMORY;
|
|
|
|
// Use IBindResource::Bind to open an IStream and copy out the date to
|
|
// the file given. This will then be used to load the object from the file...
|
|
if (SUCCEEDED(m_punkIPPResource->QueryInterface(IIDX_IBindResource, (void**)&pres)))
|
|
{
|
|
dwBindFlags = (DBBINDURLFLAG_READ | DBBINDURLFLAG_WRITE | DBBINDURLFLAG_OUTPUT | DBBINDURLFLAG_SHARE_DENY_WRITE);
|
|
callagain:
|
|
if (SUCCEEDED(hr = pres->Bind(NULL, pwszURL, dwBindFlags, DBGUIDX_STREAM,
|
|
IID_IStream, (IAuthenticate*)&m_xAuthenticate, NULL, &dwStatus, (IUnknown**)&pstm)))
|
|
{
|
|
LARGE_INTEGER lintStart; lintStart.QuadPart = 0;
|
|
hr = pstm->Seek(lintStart, STREAM_SEEK_SET, NULL);
|
|
|
|
if (FOpenLocalFile(pwszFile, GENERIC_WRITE, 0, CREATE_ALWAYS, &hFile))
|
|
{
|
|
while (SUCCEEDED(hr))
|
|
{
|
|
if (FAILED(hr = pstm->Read((void*)rgbBuf, 10240, &cbRead)) ||
|
|
(cbRead == 0))
|
|
break;
|
|
|
|
if (FALSE == WriteFile(hFile, rgbBuf, cbRead, &cbWritten, NULL))
|
|
{
|
|
hr = E_WIN32_LASTERROR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
}
|
|
else hr = E_WIN32_LASTERROR;
|
|
|
|
SAFE_SET_INTERFACE(*ppstmKeepForSave, pstm);
|
|
|
|
pstm->Release();
|
|
}
|
|
else if ((hr == DB_E_NOTSUPPORTED) && ((dwBindFlags & DBBINDURLFLAG_OUTPUT) == DBBINDURLFLAG_OUTPUT))
|
|
{
|
|
// WEC4 does not support DBBINDURLFLAG_OUTPUT flag, but if we are using WEC
|
|
// we don't really need the flag since this is not an HTTP GET call. Flip the
|
|
// flag off and call the method again to connect to server...
|
|
dwBindFlags &= ~DBBINDURLFLAG_OUTPUT;
|
|
goto callagain;
|
|
}
|
|
|
|
pres->Release();
|
|
}
|
|
|
|
// Map an OLEDB error to a common "file" error so a user
|
|
// (and VB/VBScript) would better understand...
|
|
if (FAILED(hr))
|
|
{
|
|
switch (hr)
|
|
{
|
|
case DB_E_NOTFOUND: hr = STG_E_FILENOTFOUND; break;
|
|
case DB_E_READONLY:
|
|
case DB_E_RESOURCELOCKED: hr = STG_E_LOCKVIOLATION; break;
|
|
case DB_SEC_E_PERMISSIONDENIED:
|
|
case DB_SEC_E_SAFEMODE_DENIED: hr = E_ACCESSDENIED; break;
|
|
case DB_E_CANNOTCONNECT:
|
|
case DB_E_TIMEOUT: hr = E_VBA_NOREMOTESERVER; break;
|
|
case E_NOINTERFACE: hr = E_UNEXPECTED; break;
|
|
}
|
|
}
|
|
|
|
delete [] rgbBuf;
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::IPPUploadWebResource (protected)
|
|
//
|
|
// Uploads the file to a URL. The code can be used two ways:
|
|
//
|
|
// 1.) If ppstmSave contains a pointer to an existing IStream*, then we
|
|
// just upload to the existing stream. This allows for normal "Save"
|
|
// on an open web resource.
|
|
//
|
|
// 2.) If ppstmSave is NULL (or contains a NULL IStream*), we create a new
|
|
// web resource at the location given by pwszURLSaveTo and save to its
|
|
// stream. If ppstmSave is passed, we return the new IStream* to the
|
|
// caller who can then use it to do the other type of save next time.
|
|
//
|
|
STDMETHODIMP CDsoDocObject::IPPUploadWebResource(LPWSTR pwszFile, IStream** ppstmSave, LPWSTR pwszURLSaveTo, BOOL fOverwriteFile)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
ICreateRow *pcrow = NULL;
|
|
IStream *pstm = NULL;
|
|
BYTE *rgbBuf;
|
|
HANDLE hFile;
|
|
BOOL fstmIn = FALSE;
|
|
ULONG cbRead, cbWritten;
|
|
DWORD dwStatus, dwBindFlags;
|
|
|
|
// We need MSDAIPP for full write access...
|
|
if (!(m_punkIPPResource) && !(m_punkIPPResource = CreateIPPBindResource()))
|
|
return DSO_E_REQUIRESMSDAIPP;
|
|
|
|
// Check if this a "Save" on existing IStream* and jump to loop...
|
|
if ((ppstmSave) && (fstmIn = (BOOL)(pstm = *ppstmSave)))
|
|
goto uploadfrominstm;
|
|
|
|
// Check the URL string and ask for ICreateRow (to make new web resource)...
|
|
if (!(pwszURLSaveTo) || !LooksLikeHTTP(pwszURLSaveTo) ||
|
|
FAILED(m_punkIPPResource->QueryInterface(IIDX_ICreateRow, (void**)&pcrow)))
|
|
return hr;
|
|
|
|
dwBindFlags = ( DBBINDURLFLAG_READ |
|
|
DBBINDURLFLAG_WRITE |
|
|
DBBINDURLFLAG_SHARE_DENY_WRITE |
|
|
(fOverwriteFile ? DBBINDURLFLAG_OVERWRITE : 0));
|
|
|
|
if (SUCCEEDED(hr = pcrow->CreateRow(NULL, pwszURLSaveTo, dwBindFlags, DBGUIDX_STREAM,
|
|
IID_IStream, (IAuthenticate*)&m_xAuthenticate, NULL, &dwStatus, NULL, (IUnknown**)&pstm)))
|
|
{
|
|
|
|
// Once we are here, we have a stream (either handed in or opened from above).
|
|
// We just loop through and read from the file to the stream...
|
|
uploadfrominstm:
|
|
if (rgbBuf = new BYTE[10240]) //a 10-k buffer for reading
|
|
{
|
|
if (FOpenLocalFile(pwszFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, &hFile))
|
|
{
|
|
LARGE_INTEGER lintStart; lintStart.QuadPart = 0;
|
|
hr = pstm->Seek(lintStart, STREAM_SEEK_SET, NULL);
|
|
|
|
while (SUCCEEDED(hr))
|
|
{
|
|
if (FALSE == ReadFile(hFile, rgbBuf, 10240, &cbRead, NULL))
|
|
{
|
|
hr = E_WIN32_LASTERROR;
|
|
break;
|
|
}
|
|
|
|
if (0 == cbRead) break;
|
|
|
|
if (FAILED(hr = pstm->Write((void*)rgbBuf, cbRead, &cbWritten)))
|
|
break;
|
|
}
|
|
|
|
// Need to commit the changes to make it official...
|
|
if (SUCCEEDED(hr))
|
|
hr = pstm->Commit(STGC_DEFAULT);
|
|
|
|
CloseHandle(hFile);
|
|
}
|
|
else hr = E_WIN32_LASTERROR;
|
|
|
|
delete [] rgbBuf;
|
|
}
|
|
else hr = E_OUTOFMEMORY;
|
|
|
|
// If we are not using a passed in IStream (and therefore created one), we
|
|
// should AddRef and pass back (if caller asked us to)...
|
|
if (!fstmIn)
|
|
{
|
|
if (SUCCEEDED(hr) && (ppstmSave) && (!(*ppstmSave)))
|
|
{
|
|
SAFE_SET_INTERFACE(*ppstmSave, pstm);
|
|
}
|
|
pstm->Release();
|
|
}
|
|
|
|
}
|
|
|
|
// Map an OLEDB error to a common "file" error so a user
|
|
// (and VB/VBScript) would better understand...
|
|
if (FAILED(hr))
|
|
{
|
|
switch (hr)
|
|
{
|
|
case DB_E_RESOURCEEXISTS: hr = STG_E_FILEALREADYEXISTS; break;
|
|
case DB_E_NOTFOUND: hr = STG_E_PATHNOTFOUND; break;
|
|
case DB_E_READONLY:
|
|
case DB_E_RESOURCELOCKED: hr = STG_E_LOCKVIOLATION; break;
|
|
case DB_SEC_E_PERMISSIONDENIED:
|
|
case DB_SEC_E_SAFEMODE_DENIED: hr = E_ACCESSDENIED; break;
|
|
case DB_E_CANNOTCONNECT:
|
|
case DB_E_TIMEOUT: hr = E_VBA_NOREMOTESERVER; break;
|
|
case DB_E_OUTOFSPACE: hr = STG_E_MEDIUMFULL; break;
|
|
case E_NOINTERFACE: hr = E_UNEXPECTED; break;
|
|
}
|
|
}
|
|
|
|
if (pcrow)
|
|
pcrow->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::TurnOffWebToolbar (protected)
|
|
//
|
|
// This function "turns off" the Web toolbar used by Office apps to
|
|
// do in-site navigation. The problem is when toggling tools off the
|
|
// bar may still be visible, so we have to explicitly turn it off if
|
|
// we want a true "no tool" state.
|
|
//
|
|
// This should *only* be called when turnning off the bars after object
|
|
// if inplace active. The call to OLECMDID_HIDETOOLBARS should hide all
|
|
// the primay toolbars, so we are just catching those attached to the
|
|
// document collection itself that are not in primary set.
|
|
//
|
|
STDMETHODIMP_(void) CDsoDocObject::TurnOffWebToolbar(BOOL fTurnedOff)
|
|
{
|
|
IDispatch *pdisp;
|
|
VARIANT vtT[5];
|
|
|
|
ODS("CDsoDocObject::TurnOffWebToolbar()\n");
|
|
|
|
// Can't change toolbar state in print preview...
|
|
if (InPrintPreview()) return;
|
|
|
|
if ((m_pipactive) &&
|
|
(SUCCEEDED(m_pipactive->QueryInterface(IID_IDispatch, (void**)&pdisp))) && (pdisp))
|
|
{
|
|
// Get the commandbars on the document object....
|
|
if (SUCCEEDED(DsoDispatchInvoke(pdisp,
|
|
L"CommandBars", 0, DISPATCH_PROPERTYGET, 0, NULL, &vtT[0])))
|
|
{
|
|
// Turn off Web toolbar...
|
|
ASSERT(vtT[0].vt == VT_DISPATCH);
|
|
vtT[1].vt = VT_BSTR; vtT[1].bstrVal = SysAllocString(L"Web");
|
|
|
|
if (SUCCEEDED(DsoDispatchInvoke(vtT[0].pdispVal,
|
|
L"Item", 0, DISPATCH_PROPERTYGET, 1, &vtT[1], &vtT[2])))
|
|
{
|
|
ASSERT(vtT[2].vt == VT_DISPATCH);
|
|
vtT[3].vt = VT_BOOL; vtT[3].boolVal = 0;
|
|
DsoDispatchInvoke(vtT[2].pdispVal,
|
|
L"Visible", 0, DISPATCH_PROPERTYPUT, 1, &vtT[3], &vtT[2]);
|
|
VariantClear(&vtT[2]);
|
|
}
|
|
|
|
VariantClear(&vtT[1]);
|
|
|
|
// Word 2002/2003 also displays Reviewing toolbar, so kill it too...
|
|
if (fTurnedOff && IsWordObject())
|
|
{
|
|
ASSERT(vtT[0].vt == VT_DISPATCH);
|
|
vtT[1].vt = VT_BSTR; vtT[1].bstrVal = SysAllocString(L"Reviewing");
|
|
|
|
if (SUCCEEDED(DsoDispatchInvoke(vtT[0].pdispVal,
|
|
L"Item", 0, DISPATCH_PROPERTYGET, 1, &vtT[1], &vtT[2])))
|
|
{
|
|
ASSERT(vtT[2].vt == VT_DISPATCH);
|
|
vtT[3].vt = VT_BOOL; vtT[3].boolVal = 0;
|
|
DsoDispatchInvoke(vtT[2].pdispVal,
|
|
L"Visible", 0, DISPATCH_PROPERTYPUT, 1, &vtT[3], &vtT[2]);
|
|
VariantClear(&vtT[2]);
|
|
}
|
|
|
|
VariantClear(&vtT[1]);
|
|
}
|
|
|
|
VariantClear(&vtT[0]);
|
|
}
|
|
|
|
pdisp->Release();
|
|
}
|
|
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::ClearMergedMenu (protected)
|
|
//
|
|
// Frees the merged menu set by host.
|
|
//
|
|
STDMETHODIMP_(void) CDsoDocObject::ClearMergedMenu()
|
|
{
|
|
if (m_hMenuMerged)
|
|
{
|
|
int cbMenuCnt = GetMenuItemCount(m_hMenuMerged);
|
|
for (int i = cbMenuCnt; i >= 0; --i)
|
|
RemoveMenu(m_hMenuMerged, i, MF_BYPOSITION);
|
|
|
|
DestroyMenu(m_hMenuMerged);
|
|
m_hMenuMerged = NULL;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::CalcDocNameIndex (protected)
|
|
//
|
|
// Calculates position of the name portion of the full path string.
|
|
//
|
|
STDMETHODIMP_(DWORD) CDsoDocObject::CalcDocNameIndex(LPCWSTR pwszPath)
|
|
{
|
|
DWORD cblen, idx = 0;
|
|
if ((pwszPath) && ((cblen = lstrlenW(pwszPath)) > 1))
|
|
{
|
|
for (idx = cblen; idx > 0; --idx)
|
|
{
|
|
if (pwszPath[idx] == L'\\')
|
|
break;
|
|
}
|
|
|
|
if ((idx) && !(++idx < cblen))
|
|
idx = 0;
|
|
}
|
|
return idx;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::OnDraw (protected)
|
|
//
|
|
// Site drawing (does nothing in this version).
|
|
//
|
|
STDMETHODIMP_(void) CDsoDocObject::OnDraw(DWORD dvAspect, HDC hdcDraw, LPRECT prcBounds, LPRECT prcWBounds, HDC hicTargetDev, BOOL fOptimize)
|
|
{
|
|
// Don't have to draw anything, object does all this because we are
|
|
// always UI active. If we allowed for multiple objects and had some
|
|
// non-UI active, we would have to do some drawing, but that will not
|
|
// happen in this sample.
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ActiveX Document Site Interfaces
|
|
//
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDsoDocObject IUnknown Interface Methods
|
|
//
|
|
// STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
|
|
// STDMETHODIMP_(ULONG) AddRef(void);
|
|
// STDMETHODIMP_(ULONG) Release(void);
|
|
//
|
|
STDMETHODIMP CDsoDocObject::QueryInterface(REFIID riid, void** ppv)
|
|
{
|
|
ODS("CDsoDocObject::QueryInterface\n");
|
|
CHECK_NULL_RETURN(ppv, E_POINTER);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
if (IID_IUnknown == riid)
|
|
{
|
|
*ppv = (IUnknown*)this;
|
|
}
|
|
else if (IID_IOleClientSite == riid)
|
|
{
|
|
*ppv = (IOleClientSite*)&m_xOleClientSite;
|
|
}
|
|
else if ((IID_IOleInPlaceSite == riid) || (IID_IOleWindow == riid))
|
|
{
|
|
*ppv = (IOleInPlaceSite*)&m_xOleInPlaceSite;
|
|
}
|
|
else if (IID_IOleDocumentSite == riid)
|
|
{
|
|
*ppv = (IOleDocumentSite*)&m_xOleDocumentSite;
|
|
}
|
|
else if ((IID_IOleInPlaceFrame == riid) || (IID_IOleInPlaceUIWindow == riid))
|
|
{
|
|
*ppv = (IOleInPlaceFrame*)&m_xOleInPlaceFrame;
|
|
}
|
|
else if (IID_IOleCommandTarget == riid)
|
|
{
|
|
*ppv = (IOleCommandTarget*)&m_xOleCommandTarget;
|
|
}
|
|
else if (IIDX_IAuthenticate == riid)
|
|
{
|
|
*ppv = (IAuthenticate*)&m_xAuthenticate;
|
|
}
|
|
else if (IID_IServiceProvider == riid)
|
|
{
|
|
*ppv = (IServiceProvider*)&m_xServiceProvider;
|
|
}
|
|
else if (IID_IContinueCallback == riid)
|
|
{
|
|
*ppv = (IContinueCallback*)&m_xContinueCallback;
|
|
}
|
|
else if (IID_IOlePreviewCallback == riid)
|
|
{
|
|
*ppv = (IOlePreviewCallback*)&m_xPreviewCallback;
|
|
}
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
hr = E_NOINTERFACE;
|
|
}
|
|
|
|
if (NULL != *ppv)
|
|
((IUnknown*)(*ppv))->AddRef();
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CDsoDocObject::AddRef(void)
|
|
{
|
|
TRACE1("CDsoDocObject::AddRef - %d\n", m_cRef + 1);
|
|
return ++m_cRef;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CDsoDocObject::Release(void)
|
|
{
|
|
TRACE1("CDsoDocObject::Release - %d\n", m_cRef - 1);
|
|
return --m_cRef;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDsoDocObject::XOleClientSite -- IOleClientSite Implementation
|
|
//
|
|
// STDMETHODIMP SaveObject(void);
|
|
// STDMETHODIMP GetMoniker(DWORD dwAssign, DWORD dwWhich, LPMONIKER* ppmk);
|
|
// STDMETHODIMP GetContainer(LPOLECONTAINER* ppContainer);
|
|
// STDMETHODIMP ShowObject(void);
|
|
// STDMETHODIMP OnShowWindow(BOOL fShow);
|
|
// STDMETHODIMP RequestNewObjectLayout(void);
|
|
//
|
|
IMPLEMENT_INTERFACE_UNKNOWN(CDsoDocObject, OleClientSite)
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleClientSite::SaveObject(void)
|
|
{
|
|
ODS("CDsoDocObject::XOleClientSite::SaveObject\n");
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleClientSite::GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker** ppmk)
|
|
{
|
|
HRESULT hr = OLE_E_CANT_GETMONIKER;
|
|
METHOD_PROLOGUE(CDsoDocObject, OleClientSite);
|
|
|
|
TRACE2("CDsoDocObject::XOleClientSite::GetMoniker(%d, %d)\n", dwAssign, dwWhichMoniker);
|
|
CHECK_NULL_RETURN(ppmk, E_POINTER);
|
|
*ppmk = NULL;
|
|
|
|
// Provide moniker for object opened by moniker...
|
|
switch(dwWhichMoniker)
|
|
{
|
|
case OLEWHICHMK_OBJREL:
|
|
case OLEWHICHMK_OBJFULL:
|
|
{
|
|
if (pThis->m_pmkSourceFile)
|
|
{
|
|
*ppmk = pThis->m_pmkSourceFile;
|
|
hr = S_OK;
|
|
}
|
|
else if (dwAssign == OLEGETMONIKER_FORCEASSIGN)
|
|
{
|
|
// TODO: Should we allow frce create of moniker if we don't start with it?
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Need to AddRef returned value to caller on success...
|
|
if ((hr == S_OK) && (*ppmk))
|
|
((IUnknown*)*ppmk)->AddRef();
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleClientSite::GetContainer(IOleContainer** ppContainer)
|
|
{
|
|
ODS("CDsoDocObject::XOleClientSite::GetContainer\n");
|
|
if (ppContainer) *ppContainer = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleClientSite::ShowObject(void)
|
|
{
|
|
ODS("CDsoDocObject::XOleClientSite::ShowObject\n");
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleClientSite::OnShowWindow(BOOL fShow)
|
|
{
|
|
ODS("CDsoDocObject::XOleClientSite::OnShowWindow\n");
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleClientSite::RequestNewObjectLayout(void)
|
|
{
|
|
ODS("CDsoDocObject::XOleClientSite::RequestNewObjectLayout\n");
|
|
return S_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDsoDocObject::XOleInPlaceSite -- IOleInPlaceSite Implementation
|
|
//
|
|
// STDMETHODIMP GetWindow(HWND* phWnd);
|
|
// STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode);
|
|
// STDMETHODIMP CanInPlaceActivate(void);
|
|
// STDMETHODIMP OnInPlaceActivate(void);
|
|
// STDMETHODIMP OnUIActivate(void);
|
|
// STDMETHODIMP GetWindowContext(LPOLEINPLACEFRAME* ppIIPFrame, LPOLEINPLACEUIWINDOW* ppIIPUIWindow, LPRECT prcPos, LPRECT prcClip, LPOLEINPLACEFRAMEINFO pFI);
|
|
// STDMETHODIMP Scroll(SIZE sz);
|
|
// STDMETHODIMP OnUIDeactivate(BOOL fUndoable);
|
|
// STDMETHODIMP OnInPlaceDeactivate(void);
|
|
// STDMETHODIMP DiscardUndoState(void);
|
|
// STDMETHODIMP DeactivateAndUndo(void);
|
|
// STDMETHODIMP OnPosRectChange(LPCRECT prcPos);
|
|
//
|
|
IMPLEMENT_INTERFACE_UNKNOWN(CDsoDocObject, OleInPlaceSite)
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceSite::GetWindow(HWND* phwnd)
|
|
{
|
|
METHOD_PROLOGUE(CDsoDocObject, OleInPlaceSite);
|
|
ODS("CDsoDocObject::XOleInPlaceSite::GetWindow\n");
|
|
if (phwnd) *phwnd = pThis->m_hwnd;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceSite::ContextSensitiveHelp(BOOL fEnterMode)
|
|
{
|
|
ODS("CDsoDocObject::XOleInPlaceSite::ContextSensitiveHelp\n");
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceSite::CanInPlaceActivate(void)
|
|
{
|
|
ODS("CDsoDocObject::XOleInPlaceSite::CanInPlaceActivate\n");
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceSite::OnInPlaceActivate(void)
|
|
{
|
|
METHOD_PROLOGUE(CDsoDocObject, OleInPlaceSite);
|
|
ODS("CDsoDocObject::XOleInPlaceSite::OnInPlaceActivate\n");
|
|
|
|
if ((!pThis->m_pole) ||
|
|
FAILED(pThis->m_pole->QueryInterface(IID_IOleInPlaceObject, (void **)&(pThis->m_pipobj))))
|
|
return E_UNEXPECTED;
|
|
|
|
pThis->m_fObjectIPActive = TRUE;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceSite::OnUIActivate(void)
|
|
{
|
|
METHOD_PROLOGUE(CDsoDocObject, OleInPlaceSite);
|
|
ODS("CDsoDocObject::XOleInPlaceSite::OnUIActivate\n");
|
|
pThis->m_fObjectUIActive = TRUE;
|
|
pThis->m_pipobj->GetWindow(&(pThis->m_hwndIPObject));
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceSite::GetWindowContext(IOleInPlaceFrame** ppFrame,
|
|
IOleInPlaceUIWindow** ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo)
|
|
{
|
|
METHOD_PROLOGUE(CDsoDocObject, OleInPlaceSite);
|
|
ODS("CDsoDocObject::XOleInPlaceSite::GetWindowContext\n");
|
|
|
|
if (ppFrame)
|
|
{ SAFE_SET_INTERFACE(*ppFrame, &(pThis->m_xOleInPlaceFrame)); }
|
|
|
|
if (ppDoc)
|
|
*ppDoc = NULL;
|
|
|
|
if (lprcPosRect)
|
|
*lprcPosRect = pThis->m_rcViewRect;
|
|
|
|
if (lprcClipRect)
|
|
*lprcClipRect = *lprcPosRect;
|
|
|
|
memset(lpFrameInfo, 0, sizeof(OLEINPLACEFRAMEINFO));
|
|
lpFrameInfo->cb = sizeof(OLEINPLACEFRAMEINFO);
|
|
lpFrameInfo->hwndFrame = pThis->m_hwnd;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceSite::Scroll(SIZE sz)
|
|
{
|
|
ODS("CDsoDocObject::XOleInPlaceSite::Scroll\n");
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceSite::OnUIDeactivate(BOOL fUndoable)
|
|
{
|
|
METHOD_PROLOGUE(CDsoDocObject, OleInPlaceSite);
|
|
ODS("CDsoDocObject::XOleInPlaceSite::OnUIDeactivate\n");
|
|
|
|
pThis->m_fObjectUIActive = FALSE;
|
|
pThis->m_xOleInPlaceFrame.SetMenu(NULL, NULL, NULL);
|
|
SetFocus(pThis->m_hwnd);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceSite::OnInPlaceDeactivate(void)
|
|
{
|
|
METHOD_PROLOGUE(CDsoDocObject, OleInPlaceSite);
|
|
ODS("CDsoDocObject::XOleInPlaceSite::OnInPlaceDeactivate\n");
|
|
|
|
pThis->m_fObjectIPActive = FALSE;
|
|
pThis->m_hwndIPObject = NULL;
|
|
SAFE_RELEASE_INTERFACE((pThis->m_pipobj));
|
|
|
|
pThis->m_fAttemptPptPreview = FALSE;
|
|
pThis->m_fObjectActivateComplete = FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceSite::DiscardUndoState(void)
|
|
{
|
|
ODS("CDsoDocObject::XOleInPlaceSite::DiscardUndoState\n");
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceSite::DeactivateAndUndo(void)
|
|
{
|
|
METHOD_PROLOGUE(CDsoDocObject, OleInPlaceSite);
|
|
ODS("CDsoDocObject::XOleInPlaceSite::DeactivateAndUndo\n");
|
|
if (pThis->m_pipobj) pThis->m_pipobj->InPlaceDeactivate();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceSite::OnPosRectChange(LPCRECT lprcPosRect)
|
|
{
|
|
ODS("CDsoDocObject::XOleInPlaceSite::OnPosRectChange\n");
|
|
return S_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDsoDocObject::XOleDocumentSite -- IOleDocumentSite Implementation
|
|
//
|
|
// STDMETHODIMP ActivateMe(IOleDocumentView* pView);
|
|
//
|
|
IMPLEMENT_INTERFACE_UNKNOWN(CDsoDocObject, OleDocumentSite)
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleDocumentSite::ActivateMe(IOleDocumentView* pView)
|
|
{
|
|
METHOD_PROLOGUE(CDsoDocObject, OleDocumentSite);
|
|
ODS("CDsoDocObject::XOleDocumentSite::ActivateMe\n");
|
|
|
|
HRESULT hr = E_FAIL;
|
|
IOleDocument* pmsodoc;
|
|
|
|
// If we're passed a NULL view pointer, then try to get one from
|
|
// the document object (the object within us).
|
|
if (pView)
|
|
{
|
|
// Make sure that the view has our client site
|
|
hr = pView->SetInPlaceSite((IOleInPlaceSite*)&(pThis->m_xOleInPlaceSite));
|
|
|
|
pView->AddRef(); // we will be keeping the object if successful..
|
|
}
|
|
else if (pThis->m_pole)
|
|
{
|
|
// Create a new view from the OleDocument...
|
|
if (FAILED(pThis->m_pole->QueryInterface(IID_IOleDocument, (void **)&pmsodoc)))
|
|
return E_FAIL;
|
|
|
|
hr = pmsodoc->CreateView((IOleInPlaceSite*)&(pThis->m_xOleInPlaceSite),
|
|
pThis->m_pstmview, 0, &pView);
|
|
|
|
pmsodoc->Release();
|
|
}
|
|
|
|
// If we have the view, apply view state...
|
|
if (SUCCEEDED(hr) && (pThis->m_pstmview))
|
|
hr = pView->ApplyViewState(pThis->m_pstmview);
|
|
|
|
// If any of the above failed, release the view and return...
|
|
if (FAILED(hr))
|
|
{
|
|
SAFE_RELEASE_INTERFACE(pView);
|
|
return hr;
|
|
}
|
|
|
|
// keep the view pointer...
|
|
pThis->m_pdocv = pView;
|
|
|
|
// Get a command target (if available)...
|
|
pView->QueryInterface(IID_IOleCommandTarget, (void**)&(pThis->m_pcmdt));
|
|
|
|
// Make sure that the view has our client site
|
|
pView->SetInPlaceSite((IOleInPlaceSite*)&(pThis->m_xOleInPlaceSite));
|
|
|
|
// This sets up toolbars and menus first
|
|
if (SUCCEEDED(hr = pView->UIActivate(TRUE)))
|
|
{
|
|
// Set the window size sensitive to new toolbars
|
|
pView->SetRect(&(pThis->m_rcViewRect));
|
|
|
|
// Makes it all active
|
|
pView->Show(TRUE);
|
|
|
|
pThis->m_fAppWindowActive = TRUE;
|
|
pThis->m_fObjectActivateComplete = TRUE;
|
|
|
|
// Toogle tools off if that's what user wants...
|
|
if (!(pThis->m_fDisplayTools))
|
|
{
|
|
pThis->m_fDisplayTools = TRUE;
|
|
pThis->OnNotifyChangeToolState(FALSE);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDsoDocObject::XOleInPlaceFrame -- IOleInPlaceFrame Implementation
|
|
//
|
|
// STDMETHODIMP GetWindow(HWND* phWnd);
|
|
// STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode);
|
|
// STDMETHODIMP GetBorder(LPRECT prcBorder);
|
|
// STDMETHODIMP RequestBorderSpace(LPCBORDERWIDTHS pBW);
|
|
// STDMETHODIMP SetBorderSpace(LPCBORDERWIDTHS pBW);
|
|
// STDMETHODIMP SetActiveObject(LPOLEINPLACEACTIVEOBJECT pIIPActiveObj, LPCOLESTR pszObj);
|
|
// STDMETHODIMP InsertMenus(HMENU hMenu, LPOLEMENUGROUPWIDTHS pMGW);
|
|
// STDMETHODIMP SetMenu(HMENU hMenu, HOLEMENU hOLEMenu, HWND hWndObj);
|
|
// STDMETHODIMP RemoveMenus(HMENU hMenu);
|
|
// STDMETHODIMP SetStatusText(LPCOLESTR pszText);
|
|
// STDMETHODIMP EnableModeless(BOOL fEnable);
|
|
// STDMETHODIMP TranslateAccelerator(LPMSG pMSG, WORD wID);
|
|
//
|
|
IMPLEMENT_INTERFACE_UNKNOWN(CDsoDocObject, OleInPlaceFrame)
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceFrame::GetWindow(HWND* phWnd)
|
|
{
|
|
METHOD_PROLOGUE(CDsoDocObject, OleInPlaceFrame);
|
|
ODS("CDsoDocObject::XOleInPlaceFrame::GetWindow\n");
|
|
return pThis->m_xOleInPlaceSite.GetWindow(phWnd);
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceFrame::ContextSensitiveHelp(BOOL fEnterMode)
|
|
{
|
|
METHOD_PROLOGUE(CDsoDocObject, OleInPlaceFrame);
|
|
ODS("CDsoDocObject::XOleInPlaceFrame::ContextSensitiveHelp\n");
|
|
return pThis->m_xOleInPlaceSite.ContextSensitiveHelp(fEnterMode);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceFrame::GetBorder(LPRECT prcBorder)
|
|
{
|
|
METHOD_PROLOGUE(CDsoDocObject, OleInPlaceFrame);
|
|
ODS("CDsoDocObject::XOleInPlaceFrame::GetBorder\n");
|
|
CHECK_NULL_RETURN(prcBorder, E_POINTER);
|
|
|
|
// If we don't allow Toolspace, and we are already active, give
|
|
// no space for tools (ie, hide toolabrs), otherwise give as much we can...
|
|
if (!(pThis->m_fDisplayTools) && (pThis->m_pipactive))
|
|
SetRectEmpty(prcBorder);
|
|
else
|
|
GetClientRect(pThis->m_hwnd, prcBorder);
|
|
|
|
TRACE_LPRECT("prcBorder", prcBorder);
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceFrame::RequestBorderSpace(LPCBORDERWIDTHS pBW)
|
|
{
|
|
ODS("CDsoDocObject::XOleInPlaceFrame::RequestBorderSpace\n");
|
|
CHECK_NULL_RETURN(pBW, E_POINTER);
|
|
TRACE_LPRECT("pBW", pBW);
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceFrame::SetBorderSpace(LPCBORDERWIDTHS pBW)
|
|
{
|
|
RECT rc;
|
|
|
|
METHOD_PROLOGUE(CDsoDocObject, OleInPlaceFrame);
|
|
ODS("CDsoDocObject::XOleInPlaceFrame::SetBorderSpace\n");
|
|
|
|
if (pBW){TRACE_LPRECT("pBW", pBW);}
|
|
|
|
GetClientRect(pThis->m_hwnd, &rc);
|
|
SetRectEmpty((RECT*)&(pThis->m_bwToolSpace));
|
|
|
|
if (pBW)
|
|
{
|
|
pThis->m_bwToolSpace = *pBW;
|
|
rc.left += pBW->left; rc.right -= pBW->right;
|
|
rc.top += pBW->top; rc.bottom -= pBW->bottom;
|
|
}
|
|
|
|
// Save the current view RECT (space minus tools)...
|
|
pThis->m_rcViewRect = rc;
|
|
|
|
// Update the active document (if alive)...
|
|
if (pThis->m_pdocv)
|
|
pThis->m_pdocv->SetRect(&(pThis->m_rcViewRect));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceFrame::SetActiveObject(LPOLEINPLACEACTIVEOBJECT pIIPActiveObj, LPCOLESTR pszObj)
|
|
{
|
|
METHOD_PROLOGUE(CDsoDocObject, OleInPlaceFrame);
|
|
ODS("CDsoDocObject::XOleInPlaceFrame::SetActiveObject\n");
|
|
|
|
SAFE_RELEASE_INTERFACE((pThis->m_pipactive));
|
|
pThis->m_hwndUIActiveObj = NULL;
|
|
pThis->m_dwObjectThreadID = 0;
|
|
|
|
if (pIIPActiveObj)
|
|
{
|
|
SAFE_SET_INTERFACE(pThis->m_pipactive, pIIPActiveObj);
|
|
pIIPActiveObj->GetWindow(&(pThis->m_hwndUIActiveObj));
|
|
pThis->m_dwObjectThreadID = GetWindowThreadProcessId(pThis->m_hwndUIActiveObj, NULL);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceFrame::InsertMenus(HMENU hMenu, LPOLEMENUGROUPWIDTHS pMGW)
|
|
{
|
|
ODS("CDsoDocObject::XOleInPlaceFrame::InsertMenus\n");
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceFrame::SetMenu(HMENU hMenu, HOLEMENU hOLEMenu, HWND hWndObj)
|
|
{
|
|
METHOD_PROLOGUE(CDsoDocObject, OleInPlaceFrame);
|
|
ODS("CDsoDocObject::XOleInPlaceFrame::SetMenu\n");
|
|
|
|
pThis->CheckForPPTPreviewChange();
|
|
|
|
// We really don't do anything here. We will merge the menu and set it
|
|
// later if the menubar is visible or user chooses dropdown from titlebar.
|
|
// All we do is stash current values or clear them depending on call.
|
|
if (hMenu)
|
|
{
|
|
pThis->m_hMenuActive = hMenu;
|
|
pThis->m_holeMenu = hOLEMenu;
|
|
pThis->m_hwndMenuObj = hWndObj;
|
|
|
|
// Handle a special case where menu is being updated after initial set
|
|
// and not in close. In such a case we should force redraw control we
|
|
// are in so new menu changes are visible as soon as possible...
|
|
if ((!(pThis->m_fInClose)) && (pThis->m_hwndCtl))
|
|
InvalidateRect(pThis->m_hwndCtl, NULL, FALSE);
|
|
}
|
|
else
|
|
{
|
|
pThis->m_hMenuActive = NULL;
|
|
pThis->m_holeMenu = NULL;
|
|
pThis->m_hwndMenuObj = NULL;
|
|
}
|
|
|
|
// Regardless of call, make sure to cleanup a merged menu if
|
|
// one was previously created for the control...
|
|
pThis->ClearMergedMenu();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceFrame::RemoveMenus(HMENU hMenu)
|
|
{
|
|
ODS("CDsoDocObject::XOleInPlaceFrame::RemoveMenus\n");
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceFrame::SetStatusText(LPCOLESTR pszText)
|
|
{
|
|
METHOD_PROLOGUE(CDsoDocObject, OleInPlaceFrame);
|
|
ODS("CDsoDocObject::XOleInPlaceFrame::SetStatusText\n");
|
|
if ((pszText) && (*pszText)){TRACE1(" Status Text = %S \n", pszText);}
|
|
return ((pThis->m_psiteCtl) ? pThis->m_psiteCtl->SetStatusText(pszText) : S_OK);
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceFrame::EnableModeless(BOOL fEnable)
|
|
{
|
|
METHOD_PROLOGUE(CDsoDocObject, OleInPlaceFrame);
|
|
TRACE1("CDsoDocObject::XOleInPlaceFrame::EnableModeless(%d)\n", fEnable);
|
|
pThis->m_fObjectInModalCondition = !fEnable;
|
|
SendMessage(pThis->m_hwndCtl, DSO_WM_ASYNCH_STATECHANGE, DSO_STATE_MODAL, (LPARAM)fEnable);
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleInPlaceFrame::TranslateAccelerator(LPMSG pMSG, WORD wID)
|
|
{
|
|
ODS("CDsoDocObject::XOleInPlaceFrame::TranslateAccelerator\n");
|
|
return S_FALSE;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDsoDocObject::XOleCommandTarget -- IOleCommandTarget Implementation
|
|
//
|
|
// STDMETHODIMP QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText);
|
|
// STDMETHODIMP Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvaIn, VARIANTARG *pvaOut);
|
|
//
|
|
IMPLEMENT_INTERFACE_UNKNOWN(CDsoDocObject, OleCommandTarget)
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleCommandTarget::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText)
|
|
{
|
|
HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;
|
|
METHOD_PROLOGUE(CDsoDocObject, OleCommandTarget);
|
|
if (pThis->m_pcmdCtl)
|
|
hr = pThis->m_pcmdCtl->QueryStatus(pguidCmdGroup, cCmds, prgCmds, pCmdText);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XOleCommandTarget::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvaIn, VARIANTARG *pvaOut)
|
|
{
|
|
HRESULT hr = OLECMDERR_E_NOTSUPPORTED;
|
|
METHOD_PROLOGUE(CDsoDocObject, OleCommandTarget);
|
|
if (pThis->m_pcmdCtl)
|
|
hr = pThis->m_pcmdCtl->Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut);
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDsoDocObject::XServiceProvider -- IServiceProvider Implementation
|
|
//
|
|
// STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void **ppv);
|
|
//
|
|
IMPLEMENT_INTERFACE_UNKNOWN(CDsoDocObject, ServiceProvider)
|
|
|
|
STDMETHODIMP CDsoDocObject::XServiceProvider::QueryService(REFGUID guidService, REFIID riid, void **ppv)
|
|
{
|
|
METHOD_PROLOGUE(CDsoDocObject, ServiceProvider);
|
|
ODS("CDsoDocObject::XServiceProvider::QueryService\n");
|
|
|
|
if (pThis->m_psiteCtl) // Forward to host control/container...
|
|
return pThis->m_psiteCtl->QueryService(guidService, riid, ppv);
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDsoDocObject::XAuthenticate -- IAuthenticate Implementation
|
|
//
|
|
// STDMETHODIMP Authenticate(HWND *phwnd, LPWSTR *pszUsername, LPWSTR *pszPassword);
|
|
//
|
|
IMPLEMENT_INTERFACE_UNKNOWN(CDsoDocObject, Authenticate)
|
|
|
|
STDMETHODIMP CDsoDocObject::XAuthenticate::Authenticate(HWND *phwnd, LPWSTR *pszUsername, LPWSTR *pszPassword)
|
|
{
|
|
METHOD_PROLOGUE(CDsoDocObject, Authenticate);
|
|
ODS("CDsoDocObject::XAuthenticate::Authenticate\n");
|
|
if (phwnd) *phwnd = ((pThis->m_pwszUsername) ? (HWND)INVALID_HANDLE_VALUE : pThis->m_hwndCtl);
|
|
if (pszUsername) *pszUsername = DsoConvertToLPOLESTR(pThis->m_pwszUsername);
|
|
if (pszPassword) *pszPassword = DsoConvertToLPOLESTR(pThis->m_pwszPassword);
|
|
return S_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDsoDocObject::XContinueCallback -- IContinueCallback Implementation
|
|
//
|
|
// STDMETHODIMP FContinue(void);
|
|
// STDMETHODIMP FContinuePrinting(LONG cPagesPrinted, LONG nCurrentPage, LPOLESTR pwszPrintStatus);
|
|
//
|
|
IMPLEMENT_INTERFACE_UNKNOWN(CDsoDocObject, ContinueCallback)
|
|
|
|
STDMETHODIMP CDsoDocObject::XContinueCallback::FContinue(void)
|
|
{ // We don't support asynchronous cancel of printing, but if you wanted to add
|
|
// such functionality, this is where you could do so...
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDsoDocObject::XContinueCallback::FContinuePrinting(LONG cPagesPrinted, LONG nCurrentPage, LPOLESTR pwszPrintStatus)
|
|
{
|
|
TRACE3("CDsoDocObject::XContinueCallback::FContinuePrinting(%d, %d, %S)\n", cPagesPrinted, nCurrentPage, pwszPrintStatus);
|
|
return S_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDsoDocObject::XPreviewCallback -- IPreviewCallback Implementation
|
|
//
|
|
// STDMETHODIMP Notify(DWORD wStatus, LONG nLastPage, LPOLESTR pwszPreviewStatus);
|
|
//
|
|
IMPLEMENT_INTERFACE_UNKNOWN(CDsoDocObject, PreviewCallback)
|
|
|
|
STDMETHODIMP CDsoDocObject::XPreviewCallback::Notify(DWORD wStatus, LONG nLastPage, LPOLESTR pwszPreviewStatus)
|
|
{
|
|
METHOD_PROLOGUE(CDsoDocObject, PreviewCallback);
|
|
TRACE3("CDsoDocObject::XPreviewCallback::Notify(%d, %d, %S)\n", wStatus, nLastPage, pwszPreviewStatus);
|
|
|
|
// The only notification we act on is when the preview is done...
|
|
if ((wStatus == NOTIFY_FORCECLOSEPREVIEW) ||
|
|
(wStatus == NOTIFY_FINISHED) ||
|
|
(wStatus == NOTIFY_UNABLETOPREVIEW))
|
|
pThis->ExitPrintPreview(FALSE);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDsoDocObject::HrGetDataFromObject
|
|
//
|
|
// Function designed to give indirect access to the content in an existing
|
|
// loaded object, in a clipboard format specified in vtType. In other words,
|
|
// a call to HrGetDataFromObject("Rich Text Format", [out] array) will get the
|
|
// content of the current document in RTF format and return it as a byte array
|
|
// vector in VB6-safe safearray. This array can be converted to a string for
|
|
// display or saved to database (etc.) without saving to disk.
|
|
//
|
|
// For this to work, the object embedded must support IDataObject and the clip
|
|
// format that you request.
|
|
//
|
|
STDMETHODIMP CDsoDocObject::HrGetDataFromObject(VARIANT *pvtType, VARIANT *pvtOutput)
|
|
{
|
|
HRESULT hr;
|
|
IDataObject *pdo = NULL;
|
|
LPWSTR pwszTypeName;
|
|
LPSTR pszFormatName;
|
|
SAFEARRAY* psa;
|
|
VOID HUGEP* prawdata;
|
|
FORMATETC ftc;
|
|
STGMEDIUM stgm;
|
|
LONG cfType;
|
|
|
|
if ((pvtType == NULL) || PARAM_IS_MISSING(pvtType) ||
|
|
(pvtOutput == NULL) || PARAM_IS_MISSING(pvtOutput))
|
|
return E_INVALIDARG;
|
|
|
|
VariantClear(pvtOutput);
|
|
|
|
// We take the name and find the right clipformat for the data type...
|
|
pwszTypeName = LPWSTR_FROM_VARIANT(*pvtType);
|
|
if ((pwszTypeName) && (pszFormatName = DsoConvertToMBCS(pwszTypeName)))
|
|
{
|
|
cfType = RegisterClipboardFormat(pszFormatName);
|
|
DsoMemFree(pszFormatName);
|
|
}
|
|
else cfType = LONG_FROM_VARIANT(*pvtType, 0);
|
|
CHECK_NULL_RETURN(cfType, E_INVALIDARG);
|
|
|
|
// We must be able to get IDataObject for the transfer to work...
|
|
if ((m_pole == NULL) ||
|
|
(FAILED(m_pole->GetClipboardData(0, &pdo)) &&
|
|
FAILED(m_pole->QueryInterface(IID_IDataObject, (void**)&pdo))))
|
|
return OLE_E_CANT_GETMONIKER;
|
|
|
|
ASSERT(pdo); CHECK_NULL_RETURN(pdo, E_UNEXPECTED);
|
|
|
|
// We are going to ask for HGLOBAL data format only. This is majority
|
|
// of the non-binary formats, which should be sufficient here...
|
|
memset(&ftc, 0, sizeof(ftc));
|
|
ftc.cfFormat = (WORD)cfType;
|
|
ftc.dwAspect = DVASPECT_CONTENT;
|
|
ftc.lindex = -1; ftc.tymed = TYMED_HGLOBAL;
|
|
|
|
memset(&stgm, 0, sizeof(stgm));
|
|
stgm.tymed = TYMED_HGLOBAL;
|
|
|
|
// Ask the object for the data...
|
|
if (SUCCEEDED(hr = pdo->QueryGetData(&ftc)) &&
|
|
SUCCEEDED(hr = pdo->GetData(&ftc, &stgm)))
|
|
{
|
|
ULONG ulSize;
|
|
if ((stgm.tymed == TYMED_HGLOBAL) && (stgm.hGlobal) &&
|
|
(ulSize = GlobalSize(stgm.hGlobal)))
|
|
{
|
|
LPVOID lpv = GlobalLock(stgm.hGlobal);
|
|
if (lpv)
|
|
{
|
|
// We will return data as safearray vector of VB6 Byte type...
|
|
psa = SafeArrayCreateVector(VT_UI1, 1, ulSize);
|
|
if (psa)
|
|
{
|
|
pvtOutput->vt = VT_ARRAY|VT_UI1;
|
|
pvtOutput->parray = psa;
|
|
prawdata = NULL;
|
|
|
|
if (SUCCEEDED(SafeArrayAccessData(psa, &prawdata)))
|
|
{
|
|
memcpy(prawdata, lpv, ulSize);
|
|
SafeArrayUnaccessData(psa);
|
|
}
|
|
}
|
|
else hr = E_OUTOFMEMORY;
|
|
|
|
GlobalUnlock(stgm.hGlobal);
|
|
}
|
|
else hr = E_ACCESSDENIED;
|
|
}
|
|
else hr = E_FAIL;
|
|
|
|
ReleaseStgMedium(&stgm);
|
|
}
|
|
|
|
pdo->Release();
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDsoDocObject::HrSetDataInObject
|
|
//
|
|
// Function to take format type and either string or VB6-safe byte array
|
|
// as data and set it into the current object (that is, it will replace the
|
|
// current document content with this content). The fMbcsString flag determines
|
|
// if BSTR passed should be converted to MBCS format before being set. For example,
|
|
// if you previously got data as RTF (MBCS), then converted to Unicode BSTR and
|
|
// passed it back, it would need to be converted back to MBCS for the set to work.
|
|
// If the string data is binary, pass FALSE. If data is array, the param is ignored.
|
|
//
|
|
// The function sets the data using IDataObject::SetData. Note that some objects
|
|
// will allow you to get a format but not set it, so this may not work for a previously
|
|
// acquired data type. It is up to the host DocObject server, so test carefully before
|
|
// depending on this functionality.
|
|
//
|
|
STDMETHODIMP CDsoDocObject::HrSetDataInObject(VARIANT *pvtType, VARIANT *pvtInput, BOOL fMbcsString)
|
|
{
|
|
HRESULT hr;
|
|
IDataObject *pdo = NULL;
|
|
LPWSTR pwszTypeName;
|
|
LPSTR pszFormatName;
|
|
SAFEARRAY* psa;
|
|
VOID HUGEP* prawdata;
|
|
FORMATETC ftc;
|
|
STGMEDIUM stgm;
|
|
LONG cfType;
|
|
ULONG ulSize;
|
|
BOOL fIsArrayData = FALSE;
|
|
BOOL fCleanupString = FALSE;
|
|
|
|
if ((pvtType == NULL) || PARAM_IS_MISSING(pvtType) ||
|
|
(pvtInput == NULL) || PARAM_IS_MISSING(pvtInput))
|
|
return E_INVALIDARG;
|
|
|
|
// Find the clipboard format for the given data type...
|
|
pwszTypeName = LPWSTR_FROM_VARIANT(*pvtType);
|
|
if ((pwszTypeName) && (pszFormatName = DsoConvertToMBCS(pwszTypeName)))
|
|
{
|
|
cfType = RegisterClipboardFormat(pszFormatName);
|
|
DsoMemFree(pszFormatName);
|
|
}
|
|
else cfType = LONG_FROM_VARIANT(*pvtType, 0);
|
|
CHECK_NULL_RETURN(cfType, E_INVALIDARG);
|
|
|
|
// Depending on the type of data passed, and if we need to do any Unicode-to-MBCS
|
|
// conversion for the caller, set up the data size and data pointer we will use
|
|
// to create the global memory object we'll pass to the doc object server...
|
|
prawdata = LPWSTR_FROM_VARIANT(*pvtInput);
|
|
if (prawdata)
|
|
{
|
|
if (fMbcsString)
|
|
{
|
|
prawdata = (void*)DsoConvertToMBCS((BSTR)prawdata);
|
|
CHECK_NULL_RETURN(prawdata, E_OUTOFMEMORY);
|
|
fCleanupString = TRUE;
|
|
ulSize = lstrlen((LPSTR)prawdata);
|
|
}
|
|
else
|
|
ulSize = SysStringByteLen((BSTR)prawdata);
|
|
}
|
|
else
|
|
{
|
|
psa = PSARRAY_FROM_VARIANT(*pvtInput);
|
|
if (psa)
|
|
{
|
|
LONG lb, ub, elSize;
|
|
|
|
if ((SafeArrayGetDim(psa) > 1) ||
|
|
FAILED(SafeArrayGetLBound(psa, 1, &lb)) || (lb < 0) ||
|
|
FAILED(SafeArrayGetUBound(psa, 1, &ub)) || (ub < lb) ||
|
|
((elSize = SafeArrayGetElemsize(psa)) < 1))
|
|
return E_INVALIDARG;
|
|
|
|
ulSize = (((ub + 1) - lb) * elSize);
|
|
fIsArrayData = TRUE;
|
|
if (FAILED(SafeArrayAccessData(psa, &prawdata)))
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
CHECK_NULL_RETURN(prawdata, E_INVALIDARG);
|
|
}
|
|
|
|
// We must have a server and it must support IDataObject...
|
|
if ((m_pole) && SUCCEEDED(m_pole->QueryInterface(IID_IDataObject, (void**)&pdo)) && (pdo))
|
|
{
|
|
memset(&ftc, 0, sizeof(ftc));
|
|
ftc.cfFormat = (WORD)cfType;
|
|
ftc.dwAspect = DVASPECT_CONTENT;
|
|
ftc.lindex = -1; ftc.tymed = TYMED_HGLOBAL;
|
|
|
|
memset(&stgm, 0, sizeof(stgm));
|
|
stgm.tymed = TYMED_HGLOBAL;
|
|
stgm.hGlobal = GlobalAlloc(GPTR, ulSize);
|
|
if (stgm.hGlobal)
|
|
{
|
|
LPVOID lpv = GlobalLock(stgm.hGlobal);
|
|
if ((lpv) && (ulSize))
|
|
{
|
|
// Copy the data into transfer object...
|
|
memcpy(lpv, prawdata, ulSize);
|
|
GlobalUnlock(stgm.hGlobal);
|
|
|
|
// Do the actual SetData call to transfer the data...
|
|
hr = pdo->SetData(&ftc, &stgm, TRUE);
|
|
}
|
|
else hr = E_UNEXPECTED;
|
|
|
|
if (FAILED(hr))
|
|
ReleaseStgMedium(&stgm);
|
|
}
|
|
else hr = E_OUTOFMEMORY;
|
|
|
|
pdo->Release();
|
|
}
|
|
else hr = OLE_E_CANT_GETMONIKER;
|
|
|
|
if (fIsArrayData)
|
|
SafeArrayUnaccessData(psa);
|
|
|
|
if (fCleanupString)
|
|
DsoMemFree(prawdata);
|
|
|
|
return hr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// CDsoDocObject::FrameWindowProc
|
|
//
|
|
// Site window procedure. Not much to do here except forward focus.
|
|
//
|
|
STDMETHODIMP_(LRESULT) CDsoDocObject::FrameWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CDsoDocObject* pbndr = (CDsoDocObject*)GetWindowLong(hwnd, GWL_USERDATA);
|
|
if (pbndr)
|
|
{
|
|
switch (msg)
|
|
{
|
|
case WM_PAINT:
|
|
{
|
|
PAINTSTRUCT ps;
|
|
RECT rc; GetClientRect(hwnd, &rc);
|
|
BeginPaint(hwnd, &ps);
|
|
pbndr->OnDraw(DVASPECT_CONTENT, ps.hdc, (RECT*)&rc, NULL, NULL, TRUE);
|
|
EndPaint(hwnd, &ps);
|
|
}
|
|
return 0;
|
|
|
|
case WM_NCDESTROY:
|
|
SetWindowLong(hwnd, GWL_USERDATA, 0);
|
|
pbndr->m_hwnd = NULL;
|
|
break;
|
|
|
|
case WM_SETFOCUS:
|
|
if (pbndr->m_hwndUIActiveObj)
|
|
SetFocus(pbndr->m_hwndUIActiveObj);
|
|
return 0;
|
|
|
|
case WM_SYSCOMMAND:
|
|
if ((wParam & 0xFFF0) == SC_KEYMENU)
|
|
{
|
|
if ((pbndr->m_psiteCtl) && (pbndr->m_psiteCtl->SysMenuCommand((UINT)lParam) == S_OK))
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case WM_ERASEBKGND:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
|
|
|