|
|
#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; }
|