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