windows-nt/Source/XPSP1/NT/shell/ext/pack/ioleobj.cpp
2020-09-26 16:20:57 +08:00

742 lines
26 KiB
C++

#include "privcpp.h"
HWND g_hTaskWnd;
BOOL CALLBACK GetTaskWndProc(HWND hwnd, DWORD lParam);
DWORD CALLBACK MainWaitOnChildThreadProc(void *lpv);
typedef struct {
CPackage_IOleObject *pObj;
HANDLE h;
} MAINWAITONCHILD;
CPackage_IOleObject::CPackage_IOleObject(CPackage *pPackage) :
_pPackage(pPackage)
{
ASSERT(_cRef == 0);
}
CPackage_IOleObject::~CPackage_IOleObject()
{
DebugMsg(DM_TRACE,"CPackage_IOleObject destroyed with ref count %d",_cRef);
}
//////////////////////////////////
//
// IUnknown Methods...
//
HRESULT CPackage_IOleObject::QueryInterface(REFIID iid, void ** ppv)
{
return _pPackage->QueryInterface(iid,ppv);
}
ULONG CPackage_IOleObject::AddRef(void)
{
_cRef++; // interface ref count for debugging
return _pPackage->AddRef();
}
ULONG CPackage_IOleObject::Release(void)
{
_cRef--; // interface ref count for debugging
return _pPackage->Release();
}
//////////////////////////////////
//
// IOleObject Methods...
//
HRESULT CPackage_IOleObject::SetClientSite(LPOLECLIENTSITE pClientSite)
{
DebugMsg(DM_TRACE, "pack oo - SetClientSite() called.");
if (!pClientSite)
return E_POINTER;
if (_pPackage->_pIOleClientSite != NULL)
_pPackage->_pIOleClientSite->Release();
_pPackage->_pIOleClientSite = pClientSite;
_pPackage->_pIOleClientSite->AddRef();
return S_OK;
}
HRESULT CPackage_IOleObject::GetClientSite(LPOLECLIENTSITE *ppClientSite)
{
DebugMsg(DM_TRACE, "pack oo - GetClientSite() called.");
if (ppClientSite == NULL)
return E_INVALIDARG;
// Be sure to AddRef the pointer we're giving away.
*ppClientSite = _pPackage->_pIOleClientSite;
_pPackage->_pIOleClientSite->AddRef();
return S_OK;
}
HRESULT CPackage_IOleObject::SetHostNames(LPCOLESTR szContainerApp, LPCOLESTR szContainerObj)
{
DebugMsg(DM_TRACE, "pack oo - SetHostNames() called.");
delete _pPackage->_lpszContainerApp;
if (NULL != (_pPackage->_lpszContainerApp = new OLECHAR[lstrlenW(szContainerApp) + 1]))
{
lstrcpyW(_pPackage->_lpszContainerApp,szContainerApp);
}
delete _pPackage->_lpszContainerObj;
if (NULL != (_pPackage->_lpszContainerObj = new OLECHAR[lstrlenW(szContainerObj) + 1]))
{
lstrcpyW(_pPackage->_lpszContainerObj,szContainerObj);
}
switch(_pPackage->_panetype) {
case PEMBED:
if (_pPackage->_pEmbed->poo)
_pPackage->_pEmbed->poo->SetHostNames(szContainerApp,szContainerObj);
break;
case CMDLINK:
// nothing to do...we're a link to a file, so we don't ever get
// opened and need to be edited or some such thing.
break;
}
return S_OK;
}
HRESULT CPackage_IOleObject::Close(DWORD dwSaveOption)
{
DebugMsg(DM_TRACE, "pack oo - Close() called.");
switch (_pPackage->_panetype) {
case PEMBED:
if (_pPackage->_pEmbed == NULL)
return S_OK;
// tell the server to close, and release our pointer to it
if (_pPackage->_pEmbed->poo) {
_pPackage->_pEmbed->poo->Close(dwSaveOption);
_pPackage->_pEmbed->poo->Unadvise(_pPackage->_dwCookie);
_pPackage->_pEmbed->poo->Release();
_pPackage->_pEmbed->poo = NULL;
}
break;
case CMDLINK:
// again, nothing to do...we shouldn't be getting close
// messages since we're never activated through OLE.
break;
}
if ((dwSaveOption != OLECLOSE_NOSAVE) && (_pPackage->_fIsDirty))
{
_pPackage->_pIOleClientSite->SaveObject();
if (_pPackage->_pIOleAdviseHolder)
_pPackage->_pIOleAdviseHolder->SendOnSave();
}
return S_OK;
}
HRESULT CPackage_IOleObject::SetMoniker(DWORD dwWhichMoniker, LPMONIKER pmk)
{
DebugMsg(DM_TRACE, "pack oo - SetMoniker() called.");
// NOTE: Uninteresting for embeddings only.
return (E_NOTIMPL);
}
HRESULT CPackage_IOleObject::GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker,
LPMONIKER *ppmk)
{
DebugMsg(DM_TRACE, "pack oo - GetMoniker() called.");
// NOTE: Unintersting for embeddings only.
return (E_NOTIMPL);
}
HRESULT CPackage_IOleObject::InitFromData(LPDATAOBJECT pDataObject, BOOL fCreation,
DWORD dwReserved)
{
DebugMsg(DM_TRACE, "pack oo - InitFromData() called.");
// NOTE: This isn't supported at this time
return (E_NOTIMPL);
}
HRESULT CPackage_IOleObject::GetClipboardData(DWORD dwReserved, LPDATAOBJECT *ppDataObject)
{
DebugMsg(DM_TRACE, "pack oo - GetClipboardData() called.");
if (ppDataObject == NULL)
return E_INVALIDARG;
*ppDataObject = _pPackage->_pIDataObject;
AddRef();
return S_OK;
}
HRESULT CPackage_IOleObject::DoVerb(LONG iVerb, LPMSG lpmsg, LPOLECLIENTSITE pActiveSite,
LONG lindex, HWND hwndParent, LPCRECT lprcPosRect)
{
void *lpFileData = NULL;
BOOL fError = TRUE;
DWORD id;
SHELLEXECUTEINFO sheinf = {sizeof(SHELLEXECUTEINFO)};
DebugMsg(DM_TRACE, "pack oo - DoVerb() called.");
DebugMsg(DM_TRACE, " iVerb==%d",iVerb);
// We allow show, primary verb, edit, and context menu verbs on our packages...
//
if (iVerb < OLEIVERB_SHOW)
return E_NOTIMPL;
/////////////////////////////////////////////////////////////////
// SHOW VERB
/////////////////////////////////////////////////////////////////
//
if (iVerb == OLEIVERB_SHOW) {
PACKAGER_INFO packInfo = {0};
// Run the Wizard...
PackWiz_CreateWizard(hwndParent, &packInfo);
return _pPackage->InitFromPackInfo(&packInfo);
}
/////////////////////////////////////////////////////////////////
// EDIT PACKAGE VERB
/////////////////////////////////////////////////////////////////
//
else if (iVerb == OLEIVERB_EDITPACKAGE) {
// Call the edit package dialog. Which dialog is ultimately called will
// depend on whether we're a cmdline package or an embedded file
// package.
int idDlg;
PACKAGER_INFO packInfo;
int ret;
lstrcpy(packInfo.szLabel,_pPackage->_lpic->szIconText);
lstrcpy(packInfo.szIconPath,_pPackage->_lpic->szIconPath);
packInfo.iIcon = _pPackage->_lpic->iDlgIcon;
switch(_pPackage->_panetype) {
case PEMBED:
lstrcpy(packInfo.szFilename, _pPackage->_pEmbed->fd.cFileName);
idDlg = IDD_EDITEMBEDPACKAGE;
break;
case CMDLINK:
lstrcpy(packInfo.szFilename, _pPackage->_pCml->szCommandLine);
idDlg = IDD_EDITCMDPACKAGE;
break;
}
ret = PackWiz_EditPackage(hwndParent,idDlg, &packInfo);
// If User cancells the edit package...just return.
if (ret == -1)
return S_OK;
switch(_pPackage->_panetype) {
case PEMBED:
// if we have a tempfile, we'll want to delete it
if (_pPackage->_pEmbed->pszTempName) {
DeleteFile(_pPackage->_pEmbed->pszTempName);
delete _pPackage->_pEmbed->pszTempName;
_pPackage->_pEmbed->pszTempName = NULL;
_pPackage->ReleaseContextMenu();
}
// fall through
case CMDLINK:
_pPackage->InitFromPackInfo(&packInfo);
break;
}
return S_OK;
}
else if (iVerb == OLEIVERB_PRIMARY)
{
/////////////////////////////////////////////////////////////////
// ACTIVATE CONTENTS VERB
/////////////////////////////////////////////////////////////////
// NOTE: This is kind of crazy looking code, partially because we have
// to worry about two ways of launching things--ShellExecuteEx and
// calling through OLE.
//
switch(_pPackage->_panetype)
{
case PEMBED:
if (FAILED(_pPackage->CreateTempFile())) // will just return S_OK
return E_FAIL; // if we have a temp file
// if this is an OLE file then, activate through OLE
//
if (_pPackage->_pEmbed->fIsOleFile)
{
// If we've activated the server, then we can just pass this
// call along to it.
if (_pPackage->_pEmbed->poo)
{
return _pPackage->_pEmbed->poo->DoVerb(iVerb,lpmsg,
pActiveSite,lindex, hwndParent, lprcPosRect);
}
// We don't want to use OleCreateFromFile since that can turn around and create a packaged object...
CLSID clsid;
HRESULT hr = GetClassFile(_pPackage->_pEmbed->pszTempName, &clsid);
if (SUCCEEDED(hr))
{
IOleObject* poo;
hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_IOleObject, (void **)&poo);
if (SUCCEEDED(hr))
{
hr = poo->Advise(_pPackage->_pIAdviseSink, &_pPackage->_dwCookie);
if (SUCCEEDED(hr))
{
// NOTE: apparently we have to call
// OleRun before we can get IPersistFile from some apps, namely
// Word and Excel. If we don't call OleRun, they fail our QI
// for IPersistfile.
OleRun(poo);
IPersistFile* ppf;
hr = poo->QueryInterface(IID_IPersistFile, (void **)&ppf);
if (SUCCEEDED(hr))
{
hr = ppf->Load(_pPackage->_pEmbed->pszTempName, STGM_READWRITE | STGM_SHARE_DENY_WRITE);
if (SUCCEEDED(hr))
{
hr = poo->DoVerb(iVerb, lpmsg, pActiveSite, lindex, hwndParent, lprcPosRect);
if (SUCCEEDED(hr))
{
poo->SetHostNames(_pPackage->_lpszContainerApp, _pPackage->_lpszContainerObj);
if (!_pPackage->_fNoIOleClientSiteCalls)
{
_pPackage->_pIOleClientSite->ShowObject();
_pPackage->_pIOleClientSite->OnShowWindow(TRUE);
}
_pPackage->_pEmbed->poo = poo; // save this so when we get a
poo = NULL;
}
}
ppf->Release();
}
if (FAILED(hr))
poo->Unadvise(_pPackage->_dwCookie);
}
if (FAILED(hr))
poo->Release();
}
}
if (SUCCEEDED(hr))
return hr;
// We weren't an OLE file after all, change our state to reflect this
_pPackage->_pEmbed->fIsOleFile = FALSE;
_pPackage->_fIsDirty = TRUE;
}
// Try to execute the file
_pPackage->_pEmbed->hTask = NULL;
sheinf.fMask = SEE_MASK_NOCLOSEPROCESS;
sheinf.lpFile = _pPackage->_pEmbed->pszTempName;
sheinf.nShow = SW_SHOWNORMAL;
if (ShellExecuteEx(&sheinf))
{
// if we get a valid process handle, we want to create a thread
// to wait for the process to exit so we know when we can load
// the tempfile back into memory.
//
if (sheinf.hProcess)
{
_pPackage->_pEmbed->hTask = sheinf.hProcess;
MAINWAITONCHILD *pmwoc = new MAINWAITONCHILD;
if (pmwoc)
{
pmwoc->pObj = this;
pmwoc->h = sheinf.hProcess;
if (CreateThread(NULL, 0, MainWaitOnChildThreadProc, pmwoc, 0, &id))
fError = FALSE;
else
{
CloseHandle(sheinf.hProcess);
return E_FAIL;
}
}
}
// NOTE: there's not much we can do if the ShellExecute
// succeeds and we don't get a valid handle back. we'll just
// load from the temp file when we're asked to save and hope
// for the best.
}
else // ShellExecuteEx failed!
{
return E_FAIL;
}
// show that the object is now active
if (!fError && !_pPackage->_fNoIOleClientSiteCalls)
{
_pPackage->_pIOleClientSite->ShowObject();
_pPackage->_pIOleClientSite->OnShowWindow(TRUE);
}
return fError ? E_FAIL : S_OK;
case CMDLINK:
{
TCHAR szPath[MAX_PATH];
TCHAR szArgs[CBCMDLINKMAX-MAX_PATH];
lstrcpy(szPath, _pPackage->_pCml->szCommandLine);
PathSeparateArgs(szPath, szArgs);
sheinf.fMask = SEE_MASK_NOCLOSEPROCESS;
sheinf.lpFile = szPath;
sheinf.lpParameters = szArgs;
sheinf.nShow = SW_SHOWNORMAL;
// NOTE: This code is much nicer than ShellExec-ing an embedded
// file. Here, we just need to ShellExec the command line and
// the we're done. We don't need to know when that process
// finishes or anything else.
return ShellExecuteEx(&sheinf)? S_OK:E_FAIL;
}
break;
case PACKAGE:
{
PACKAGER_INFO packInfo = {0};
ASSERT(_pPackage->_pCml);
lstrcpyn(packInfo.szFilename, _pPackage->_pCml->szCommandLine, ARRAYSIZE(packInfo.szFilename));
// Run the Wizard...
PackWiz_CreateWizard(hwndParent, &packInfo);
return _pPackage->InitFromPackInfo(&packInfo);
}
break;
}
}
else
{
// Let's see if it is a context menu verb:
HRESULT hr;
IContextMenu* pcm;
if (SUCCEEDED(hr = _pPackage->GetContextMenu(&pcm)))
{
HMENU hmenu = CreatePopupMenu();
if (NULL != hmenu)
{
if (SUCCEEDED(hr = pcm->QueryContextMenu(hmenu,
0,
OLEIVERB_FIRST_CONTEXT,
OLEIVERB_LAST_CONTEXT,
CMF_NORMAL)))
{
MENUITEMINFO mii;
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_ID;
if (GetMenuItemInfo(hmenu, (UINT) (iVerb - OLEIVERB_FIRST_CONTEXT), TRUE, &mii))
{
if (PEMBED == _pPackage->_panetype)
{
// If we have an embedding, we have to make sure that
// the temp file is created before we execute a command:
hr =_pPackage->CreateTempFile();
}
if (SUCCEEDED(hr))
{
CMINVOKECOMMANDINFO ici;
ici.cbSize = sizeof(ici);
ici.fMask = 0;
ici.hwnd = NULL;
ici.lpVerb = (LPCSTR) IntToPtr(mii.wID - OLEIVERB_FIRST_CONTEXT);
ici.lpParameters = NULL;
ici.lpDirectory = NULL;
ici.nShow = SW_SHOWNORMAL;
// REVIEW: should we return OLEOBJ_S_CANNOT_DOVERB_NOW if this fails?
hr = pcm->InvokeCommand(&ici);
}
}
else
{
hr = OLEOBJ_S_CANNOT_DOVERB_NOW;
}
}
DestroyMenu(hmenu);
}
else
{
hr = E_OUTOFMEMORY;
}
pcm->Release();
}
return hr;
}
return E_FAIL;
}
HRESULT CPackage_IOleObject::EnumVerbs(LPENUMOLEVERB *ppEnumOleVerb)
{
DebugMsg(DM_TRACE, "pack oo - EnumVerbs() called.");
HRESULT hr;
IContextMenu* pcm;
// tell the package to release the cached context menu:
_pPackage->ReleaseContextMenu();
if (SUCCEEDED(hr = _pPackage->GetContextMenu(&pcm)))
{
HMENU hmenu = CreatePopupMenu();
if (NULL != hmenu)
{
if (SUCCEEDED(hr = pcm->QueryContextMenu(hmenu,
0,
OLEIVERB_FIRST_CONTEXT,
OLEIVERB_LAST_CONTEXT,
CMF_NORMAL)))
{
// FEATURE: remove problematic items by canonical names
int nItems = GetMenuItemCount(hmenu);
int cOleVerbs = 0;
if (nItems > 0)
{
// NOTE: we allocate nItems, but we may not use all of them
// FEATURE: add in menu items from the registry: "activate" & "edit"
OLEVERB* pVerbs = new OLEVERB[nItems];
if (NULL != pVerbs)
{
MENUITEMINFO mii;
TCHAR szMenuName[MAX_PATH];
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_DATA | MIIM_TYPE | MIIM_STATE | MIIM_ID;
for (int i = 0; i < nItems; i++)
{
mii.dwTypeData = szMenuName;
mii.cch = ARRAYSIZE(szMenuName);
// NOTE: use GetMenuState() to avoid converting flags:
DWORD dwState = GetMenuState(hmenu, i, MF_BYPOSITION);
if (0 == (dwState & (MF_BITMAP | MF_OWNERDRAW | MF_POPUP)))
{
if (GetMenuItemInfo(hmenu, i, TRUE, &mii) && (MFT_STRING == mii.fType))
{
TCHAR szVerb[MAX_PATH];
if (FAILED(pcm->GetCommandString(mii.wID - OLEIVERB_FIRST_CONTEXT,
GCS_VERB,
NULL,
(LPSTR) szVerb,
ARRAYSIZE(szVerb))))
{
// Some commands don't have canonical names - just
// set the verb string to empty
szVerb[0] = TEXT('\0');
}
if ((0 != lstrcmp(szVerb, TEXT("cut"))) &&
(0 != lstrcmp(szVerb, TEXT("delete"))))
{
// In the first design, the context menu ID was used as
// the lVerb - however MFC apps only give us a range of
// 16 ID's and context menu ID's are often over 100
// (they aren't contiguous)
// Instead, we use the menu position plus the verb offset
pVerbs[cOleVerbs].lVerb = (LONG) OLEIVERB_FIRST_CONTEXT + i;
int cchMenu = lstrlen(mii.dwTypeData) + 1;
if (NULL != (pVerbs[cOleVerbs].lpszVerbName = new WCHAR[cchMenu]))
{
SHTCharToUnicode(mii.dwTypeData, pVerbs[cOleVerbs].lpszVerbName, cchMenu);
}
pVerbs[cOleVerbs].fuFlags = dwState;
pVerbs[cOleVerbs].grfAttribs = OLEVERBATTRIB_ONCONTAINERMENU;
DebugMsg(DM_TRACE, " Adding verb: id==%d,name=%s,verb=%s",mii.wID,mii.dwTypeData,szVerb);
cOleVerbs++;
}
}
}
}
if (SUCCEEDED(hr = _pPackage->InitVerbEnum(pVerbs, cOleVerbs)))
{
hr = _pPackage->QueryInterface(IID_IEnumOLEVERB, (void**) ppEnumOleVerb);
}
else
{
delete pVerbs;
}
}
else
{
hr = E_OUTOFMEMORY;
}
}
else
{
hr = OLEOBJ_E_NOVERBS;
}
}
DestroyMenu(hmenu);
}
else
{
hr = E_OUTOFMEMORY;
}
pcm->Release();
}
return hr; // OleRegEnumVerbs(CLSID_CPackage, ppEnumOleVerb);
}
HRESULT CPackage_IOleObject::Update(void)
{
return S_OK;
}
HRESULT CPackage_IOleObject::IsUpToDate(void)
{
return S_OK;
}
HRESULT CPackage_IOleObject::GetUserClassID(LPCLSID pClsid)
{
*pClsid = CLSID_CPackage;
return S_OK;
}
HRESULT CPackage_IOleObject::GetUserType(DWORD dwFromOfType, LPOLESTR *pszUserType)
{
return OleRegGetUserType(CLSID_CPackage, dwFromOfType, pszUserType);
}
HRESULT CPackage_IOleObject::SetExtent(DWORD dwDrawAspect, SIZEL *psizel)
{
return E_FAIL;
}
HRESULT CPackage_IOleObject::GetExtent(DWORD dwDrawAspect, SIZEL *psizel)
{
return _pPackage->_pIViewObject2->GetExtent(dwDrawAspect,-1,NULL,psizel);
}
HRESULT CPackage_IOleObject::Advise(IAdviseSink *pAdvSink, DWORD *pdwConnection)
{
if (_pPackage->_pIOleAdviseHolder == NULL)
{
HRESULT hr = CreateOleAdviseHolder(&_pPackage->_pIOleAdviseHolder);
if (FAILED(hr))
return hr;
}
return _pPackage->_pIOleAdviseHolder->Advise(pAdvSink, pdwConnection);
}
HRESULT CPackage_IOleObject::Unadvise(DWORD dwConnection)
{
DebugMsg(DM_TRACE, "pack oo - Unadvise() called.");
if (_pPackage->_pIOleAdviseHolder != NULL)
return _pPackage->_pIOleAdviseHolder->Unadvise(dwConnection);
return E_FAIL;
}
HRESULT CPackage_IOleObject::EnumAdvise(LPENUMSTATDATA *ppenumAdvise)
{
DebugMsg(DM_TRACE, "pack oo - EnumAdvise() called.");
if (_pPackage->_pIOleAdviseHolder != NULL)
return _pPackage->_pIOleAdviseHolder->EnumAdvise(ppenumAdvise);
return E_FAIL;
}
HRESULT CPackage_IOleObject::GetMiscStatus(DWORD dwAspect, LPDWORD pdwStatus)
{
return OleRegGetMiscStatus(CLSID_CPackage, dwAspect, pdwStatus);
}
HRESULT CPackage_IOleObject::SetColorScheme(LPLOGPALETTE pLogpal)
{
return E_NOTIMPL;
}
DWORD CALLBACK MainWaitOnChildThreadProc(void *lpv)
{
INT ret;
MAINWAITONCHILD *pmwoc = (MAINWAITONCHILD *)lpv;
CPackage *pPack = pmwoc->pObj->_pPackage;
DebugMsg(DM_TRACE, "pack oo - MainWaitOnChildThreadProc() called.");
DebugMsg(DM_TRACE, " handle = %d",(DWORD_PTR)lpv);
ret = WaitForSingleObject(pmwoc->h, INFINITE);
DebugMsg(DM_TRACE,"WaitForSingObject exits...ret==%d",ret);
if (ret == -1)
DebugMsg(DM_TRACE,"GetLastError==%d",GetLastError());
CloseHandle(pmwoc->h);
// BUGBUG: NONE OF THE BELOW CALLS ARE THREAD-SAFE!!!
// this will set our dirty flag...
if (FAILED(pPack->EmbedInitFromFile(pPack->_pEmbed->pszTempName,FALSE)))
{
ShellMessageBox(g_hinst,
NULL,
MAKEINTRESOURCE(IDS_UPDATE_ERROR),
MAKEINTRESOURCE(IDS_APP_TITLE),
MB_ICONERROR | MB_TASKMODAL | MB_OK);
}
pPack->_pIOleClientSite->SaveObject();
if (pPack->_pIOleAdviseHolder)
{
pPack->_pIOleAdviseHolder->SendOnSave();
pPack->_pIOleAdviseHolder->SendOnClose();
}
if (pPack->_pIOleClientSite && !pPack->_fNoIOleClientSiteCalls)
pPack->_pIOleClientSite->OnShowWindow(FALSE);
pPack->_pEmbed->hTask = NULL;
// NOTE: we should probably pop up some sort of message box here if we
// can't reinit from the temp file.
DebugMsg(DM_TRACE, " MainWaitOnChildThreadProc exiting.");
delete pmwoc;
return 0;
}
BOOL CALLBACK GetTaskWndProc(HWND hwnd, DWORD lParam)
{
DebugMsg(DM_TRACE, "pack oo - GetTaskWndProc() called.");
if (IsWindowVisible(hwnd))
{
g_hTaskWnd = hwnd;
return FALSE;
}
return TRUE;
}