Windows2003-3790/inetcore/outlookexpress/wabw/wabapi/cntxtmnu.c
2020-09-30 16:53:55 +02:00

620 lines
21 KiB
C

/**********************************************************************************
*
*
* contxtmnu.c - contains functions for handling/creating context menu extension
*
* Created - 9/97 - vikramm
*
**********************************************************************************/
#include "_apipch.h"
static const TCHAR szActionPropsRegKey[] = TEXT("Software\\Microsoft\\WAB\\WAB4\\ExtContext");
BOOL fContextExtCoinit = FALSE;
//$$//////////////////////////////////////////////////////////////////////
//
// UninitContextExtInfo
//
// OLE Unintialization
//
//////////////////////////////////////////////////////////////////////////
void UninitContextExtInfo()
{
if(fContextExtCoinit)
{
CoUninitialize();
fContextExtCoinit = FALSE;
}
}
/*
- FreeActionItemList
-
* Frees up the Action Items list cached on the IAB object
*
*/
void FreeActionItemList(LPIAB lpIAB)
{
LPWABACTIONITEM lpItem = lpIAB->lpActionList;
while(lpItem)
{
lpIAB->lpActionList = lpItem->lpNext;
SafeRelease(lpItem->lpWABExtInit);
SafeRelease(lpItem->lpContextMenu);
LocalFree(lpItem);
lpItem = lpIAB->lpActionList;
}
lpIAB->lpActionList = NULL;
}
/*
- HrUpdateActionItemList
-
* Apps can register with the WAB for rt-click and toolbar Action items
* We load a list of registered action items here upfront and cache it on
* the IAB object.
*
*/
HRESULT HrUpdateActionItemList(LPIAB lpIAB)
{
HRESULT hr = E_FAIL;
HKEY hKey = NULL;
LPWABACTIONITEM lpList = NULL;
DWORD dwIndex = 0, dwSize = 0;
int nCmdID = IDM_EXTENDED_START, nActionItems = 0;
EnterCriticalSection(&lpIAB->cs);
if(lpIAB->lpActionList)
FreeActionItemList(lpIAB);
lpIAB->lpActionList = NULL;
//
// We will look in the registry under HKLM\Software\Microsoft\WAB\WAB4\Actions
// If this key exists, we get all the key values under it - these key values
// are all GUIDs
// The format for this key is
//
// HKLM\Software\Microsoft\WAB\WAB4\Action Extensions
// GUID1
// GUID2
// GUID3 etc
//
if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,
szActionPropsRegKey,
0, KEY_READ,&hKey))
{
goto out;
}
{
// Enumerate the GUIDs under this key one by one
//
TCHAR szGUIDName[MAX_PATH];
DWORD dwGUIDIndex = 0, dwGUIDSize = CharSizeOf(szGUIDName), dwType = 0;
*szGUIDName = '\0';
while(ERROR_SUCCESS == RegEnumValue(hKey, dwGUIDIndex,
szGUIDName, &dwGUIDSize,
0, &dwType,
NULL, NULL))
{
// The values under this entry are all GUIDs
// Read the GUID string and translate it into a GUID
//
GUID guidTmp = {0};
WCHAR szW[MAX_PATH];
StrCpyNW(szW, szGUIDName, ARRAYSIZE(szW));
if( !(HR_FAILED(hr = CLSIDFromString(szW, &guidTmp))) )
{
LPWABACTIONITEM lpTemp = LocalAlloc(LMEM_ZEROINIT, sizeof(WABACTIONITEM));
if(!lpTemp)
{
hr = MAPI_E_NOT_ENOUGH_MEMORY;
goto out;
}
// Temporarily cache the GUID
CopyMemory(&(lpTemp->guidContextMenu), &guidTmp, sizeof(GUID));
lpTemp->lpNext = lpList;
lpList = lpTemp;
}
dwGUIDIndex++;
*szGUIDName = '\0';
dwGUIDSize = CharSizeOf(szGUIDName);
}
}
if(lpList)
{
// If we have a list of GUIDs from the registry, we now
// need to open CoCreateInstance them one by one and get a handle
// to their method pointers
LPWABACTIONITEM lpItem = lpList;
if (CoInitialize(NULL) == S_FALSE)
CoUninitialize(); // Already initialized, undo the extra.
else
fContextExtCoinit = TRUE;
while(lpItem)
{
hr = CoCreateInstance( &(lpItem->guidContextMenu),
NULL,
CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
&IID_IContextMenu,
(LPVOID *)&(lpItem->lpContextMenu));
if(lpItem->lpContextMenu && !HR_FAILED(hr))
{
// Found an IContextMenu object, aslo want a IWABExtInit object
hr = lpItem->lpContextMenu->lpVtbl->QueryInterface(lpItem->lpContextMenu,
&IID_IWABExtInit,
(LPVOID *)&(lpItem->lpWABExtInit));
if(HR_FAILED(hr) || !lpItem->lpWABExtInit)
{
// Can't work without an IWABExtInit object
SafeRelease(lpItem->lpContextMenu);
}
}
else
{
hr = S_OK; //ignore error
lpItem->lpContextMenu = NULL;
}
lpItem = lpItem->lpNext;
}
}
lpIAB->lpActionList = lpList;
hr = S_OK;
out:
if(hKey)
RegCloseKey(hKey);
LeaveCriticalSection(&lpIAB->cs);
return hr;
}
/*
- GetActionAdrList
-
* Based on the parameters for a particular rt-click action,
* scans the entries in the list view and creates an adrlist
* for the entries
*
* If only one entry is selected and it is an LDAP entry, then
* also creates an LDAP URL representing that entry .. this way
* if we are displaying properties or doing actions on a single
* entry, the property sheet extenstions can determine if they
* want to do anything extra for the entry. People most interested
* in this are the NTDS
*
* For now,we only look at the selected items in the list view
*
lpAdrBook - IAB object
hWndLV - the list view on which this action was initiated
*lppAdrList - created AdrList
*lpURL - returned URL
*lpbIsNTDSEntry - flag NTDS entries so they can be special treated
Note performance suffers for a large number of entries so we want to
really only return a list of entryids
*/
HRESULT HrGetActionAdrList(LPADRBOOK lpAdrBook,
HWND hWndLV,
LPADRLIST * lppAdrList,
LPTSTR * lppURL, BOOL * lpbIsNTDSEntry)
{
HRESULT hr = S_OK;
LPADRLIST lpAdrList = NULL;
int i = 0, iItemIndex = 0, nIndex= 0;
int nSel = ListView_GetSelectedCount(hWndLV);
SCODE sc;
if(!nSel)
goto out;
sc = MAPIAllocateBuffer(sizeof(ADRLIST) + nSel * sizeof(ADRENTRY), &lpAdrList);
if(sc)
{
hr = ResultFromScode(sc);
goto out;
}
// Get index of selected item
iItemIndex = ListView_GetNextItem(hWndLV,-1,LVNI_SELECTED);
while(iItemIndex != -1)
{
LPRECIPIENT_INFO lpItem = GetItemFromLV(hWndLV, iItemIndex);
if(lpItem)
{
ULONG ulObjType = 0;
LPSPropValue lpProps = NULL;
LPMAILUSER lpEntry = NULL;
ULONG cValues = 0;
if(lpItem->cbEntryID && lpItem->lpEntryID)
{
if (hr = lpAdrBook->lpVtbl->OpenEntry(lpAdrBook,
lpItem->cbEntryID,
lpItem->lpEntryID,
NULL, // interface
0, // flags
&ulObjType,
(LPUNKNOWN *)&lpEntry))
{
goto out;
}
hr = lpEntry->lpVtbl->GetProps( lpEntry, NULL, MAPI_UNICODE,
&cValues, &lpProps);
if(HR_FAILED(hr))
{
UlRelease(lpEntry);
goto out;
}
lpAdrList->aEntries[nIndex].cValues = cValues;
lpAdrList->aEntries[nIndex].rgPropVals = lpProps;
nIndex++;
UlRelease(lpEntry);
if(nSel == 1 && lppURL)
{
CreateLDAPURLFromEntryID(lpItem->cbEntryID, lpItem->lpEntryID,
lppURL, lpbIsNTDSEntry);
}
}
}
iItemIndex = ListView_GetNextItem(hWndLV,iItemIndex,LVNI_SELECTED);
}
lpAdrList->cEntries = nIndex;
*lppAdrList = lpAdrList;
lpAdrList = NULL;
hr = S_OK;
out:
if (lpAdrList)
MAPIFreeBuffer(lpAdrList);
return hr;
}
extern void MAILUSERAssociateContextData(LPMAILUSER lpMailUser, LPWABEXTDISPLAY lpWEC);
/*
- HrCreateContextDataStruct
-
* Creates the data necessary to initialize a ContextMenu implementor
* This structure is passed into the IWABExtInit::Initialize call
*
hWndLV - ListView containing the WAB entries
lppWABExt - returned data
*/
HRESULT HrCreateContextDataStruct( LPIAB lpIAB,
HWND hWndLV,
LPWABEXTDISPLAY * lppWABExt)
{
LPADRLIST lpAdrList = NULL;
LPWABEXTDISPLAY lpWEC = NULL;
LPMAILUSER lpMailUser = NULL;
LPTSTR lpURL = NULL;
BOOL bIsNTDSEntry = FALSE;
HRESULT hr = E_FAIL;
// Get an AdrList Corresponding to the LV contents
hr = HrGetActionAdrList((LPADRBOOK)lpIAB, hWndLV, &lpAdrList, &lpURL, &bIsNTDSEntry);
if(HR_FAILED(hr) || !lpAdrList || !lpAdrList->cEntries)
goto out; //dont bother invoking
// Create a dummy mailuser so callers can call GetIDsFromNames
// on this dummy mailuser - saves them the trouble of creating entries
// just to call GetIDsFromNames
hr = HrCreateNewObject((LPADRBOOK)lpIAB, NULL, MAPI_MAILUSER, CREATE_CHECK_DUP_STRICT, (LPMAPIPROP *) &lpMailUser);
if(HR_FAILED(hr))
goto out; //dont bother invoking
lpWEC = LocalAlloc(LMEM_ZEROINIT, sizeof(WABEXTDISPLAY));
if(!lpWEC)
{
hr = MAPI_E_NOT_ENOUGH_MEMORY;
goto out;
}
lpWEC->cbSize = sizeof(WABEXTDISPLAY);
lpWEC->ulFlags = WAB_CONTEXT_ADRLIST; // indicates ADRLIST is valid and in the lpv member
lpWEC->lpAdrBook = (LPADRBOOK) lpIAB;
lpWEC->lpWABObject = (LPWABOBJECT) lpIAB->lpWABObject;
lpWEC->lpPropObj = (LPMAPIPROP) lpMailUser;
lpWEC->lpv = (LPVOID) lpAdrList;
if(lpURL && lstrlen(lpURL))
{
lpWEC->lpsz = lpURL;
lpWEC->ulFlags |= WAB_DISPLAY_LDAPURL;
lpWEC->ulFlags |= MAPI_UNICODE;
if(bIsNTDSEntry)
lpWEC->ulFlags |= WAB_DISPLAY_ISNTDS;
}
// We associate this entire WABEXT structure with the mailuser
// so that when we get the IMailUser release, we can go ahead and clean
// up all the WABEXT memory .. since there is no other good time that we can
// free the memory since we won't know who is doing what withthe structure.
// Freeing it at IMailUser::Release time works out quite well
MAILUSERAssociateContextData(lpMailUser, lpWEC);
//
// Cache this mailUser on the IAB object
//
// At any given point of time, we are going to cache one ContextMenu related MailUser
// This is because we set this up before QueryCommandMenu and then we dont know whether
// or not the user actually managed to select a menu item, or went off and did something new.
// We set the data on a MailUser and we don't release the MailUser till the next time we are
// here. If someone is currently using the MailUser, they will addref it - the memory attached
// to the MailUser will be freed up with the last caller so we dont leak it ..
// If we never come back here, the mailuser will be released at shutdown
//
UlRelease(lpIAB->lpCntxtMailUser);
lpIAB->lpCntxtMailUser = lpMailUser;
*lppWABExt = lpWEC;
hr = S_OK;
out:
if(HR_FAILED(hr))
{
UlRelease(lpMailUser);
if(lpAdrList)
FreePadrlist(lpAdrList);
if(lpWEC)
LocalFree(lpWEC);
}
return hr;
}
//$$////////////////////////////////////////////////////////////////////////////
//
// AddExtendedMenuItems - Creates a list of extension menu items and adds them
// to the specified menu
//
// hMenuAction - menu on which to add this item
// bUpdateStatus - if TRUE, means only find the existing item in the menu and update
// its status - if FALSE, means add to the menu
// bAddSendMailToItems - if TRUE, means attempt to modify the SendMailTo menu item
//
//////////////////////////////////////////////////////////////////////////////
void AddExtendedMenuItems( LPADRBOOK lpAdrBook,
HWND hWndLV,
HMENU hMenuAction,
BOOL bUpdateStatus,
BOOL bAddSendMailToItems)
{
HRESULT hr = S_OK;
LPWABEXTDISPLAY lpWEC = NULL;
LPIAB lpIAB = (LPIAB) lpAdrBook;
// Intialize the context menu extensions
if(!lpIAB->lpActionList)
HrUpdateActionItemList(lpIAB);
if(bUpdateStatus)
{
// This is only set to true from the call from ui_abook.c which means this
// is the Tools Menu item we are talking about.
// To update the status of the Tools menu item, we need to remove all the
// items we added before and then re-add them
// For indexing purposes, we will assume that the last pre-configured item in
// this list is the Internet Call item (since this menu list is quite variable)
// First get the position of the IDM_TOOLS_INTERNET_CALL item
int i, nPos = -1, nId = 0;
int nCmdCount = GetMenuItemCount(hMenuAction); // Append all items at end of this menu
for(i=0;i<nCmdCount;i++)
{
if(GetMenuItemID(hMenuAction, i) == IDM_TOOLS_INTERNET_CALL)
{
nPos = i;
break;
}
}
if(nPos >= 0 && nPos < nCmdCount-1)
{
for(i=nPos+1;i<nCmdCount;i++)
{
DeleteMenu(hMenuAction, nPos+1, MF_BYPOSITION);
}
}
}
// Do any special treatment we need to do for SendMailTo
AddExtendedSendMailToItems(lpAdrBook, hWndLV, hMenuAction, bAddSendMailToItems);
// Before we can call QueryContextMenu - we must already have a list of all
// the selected items from the ListView and provide such items to the ContextMenu
// implementors so that they can decide how to handle the data being provided to them
// (e.g. they may want to disable their item for multi-selections etc)...
//
HrCreateContextDataStruct(lpIAB, hWndLV, &lpWEC);
if(lpIAB->lpActionList && lpWEC && lpWEC->lpv)
{
LPWABACTIONITEM lpItem = lpIAB->lpActionList;
int nCmdIdPos = GetMenuItemCount(hMenuAction); // Append all items at end of this menu
while(lpItem)
{
if(lpItem->lpContextMenu && lpItem->lpWABExtInit)// && !bUpdateStatus)
{
int nNumCmd = 0;
// Get the menu item added to this menu
hr = lpItem->lpWABExtInit->lpVtbl->Initialize(lpItem->lpWABExtInit, lpWEC);
if(!HR_FAILED(hr))
{
hr = lpItem->lpContextMenu->lpVtbl->QueryContextMenu(lpItem->lpContextMenu,
hMenuAction,
nCmdIdPos,
lpItem->nCmdIdFirst ? lpItem->nCmdIdFirst : IDM_EXTENDED_START+nCmdIdPos,
IDM_EXTENDED_END,
CMF_NODEFAULT | CMF_NORMAL);
if(!HR_FAILED(hr))
{
nNumCmd = HRESULT_CODE(hr);
if(nNumCmd)
{
// Record the range of IDs taken up by this menu ext implementor
if(!lpItem->nCmdIdFirst)
lpItem->nCmdIdFirst = nCmdIdPos+IDM_EXTENDED_START;
if(!lpItem->nCmdIdLast)
lpItem->nCmdIdLast = lpItem->nCmdIdFirst + nNumCmd - 1;
}
// Update the next available starting pos
nCmdIdPos = nCmdIdPos+nNumCmd;
}
}
}
lpItem = lpItem->lpNext;
}
}
}
/*
- ProcessActionCommands
-
* Process a WM_COMMAND message to see if it matches any of the extended
* rt-click action items ...
*
* Also process SendMailTo extended email-address mail processing here since
* this is a convenient place to do so ..
*
*/
LRESULT ProcessActionCommands(LPIAB lpIAB, HWND hWndLV, HWND hWnd,
UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int nCmdID = GET_WM_COMMAND_ID(wParam, lParam);
int i = 0;
switch(nCmdID)
{
case IDM_DIALDLG_START:
HrExecDialDlg(hWndLV, (LPADRBOOK)lpIAB);
return 0;
break;
case IDM_LVCONTEXT_INTERNET_CALL:
case IDM_TOOLS_INTERNET_CALL:
HrShellExecInternetCall((LPADRBOOK)lpIAB, hWndLV);
return 0;
break;
case IDM_LVCONTEXT_SENDMAIL:
case IDM_FILE_SENDMAIL:
HrSendMailToSelectedContacts(hWndLV, (LPADRBOOK)lpIAB, 0);
break;
}
if( (nCmdID>=IDM_SENDMAILTO_START) && (nCmdID<=IDM_SENDMAILTO_START+IDM_SENDMAILTO_MAX))
{
HrSendMailToSelectedContacts(hWndLV, (LPADRBOOK)lpIAB, nCmdID - IDM_SENDMAILTO_START);
return 0;
}
// Check if this is any of the context menu extensions ..
if(lpIAB->lpActionList)
{
LPWABACTIONITEM lpListItem = lpIAB->lpActionList;
while(lpListItem)
{
if(nCmdID >= lpListItem->nCmdIdFirst && nCmdID <= lpListItem->nCmdIdLast)
{
CMINVOKECOMMANDINFO cmici = {0};
cmici.cbSize = sizeof(CMINVOKECOMMANDINFO);
cmici.fMask = 0;
cmici.hwnd = hWnd;
cmici.lpVerb = (LPCSTR) IntToPtr(nCmdID - lpListItem->nCmdIdFirst);
cmici.lpParameters = NULL;
cmici.lpDirectory = NULL;
cmici.nShow = SW_SHOWNORMAL;
lpListItem->lpContextMenu->lpVtbl->InvokeCommand(lpListItem->lpContextMenu,
&cmici);
return 0;
}
lpListItem = lpListItem->lpNext;
}
}
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
/*
- GetContextMenuExtCommandString
-
* gets the status bar helptext for context menu extensions
*
*/
void GetContextMenuExtCommandString(LPIAB lpIAB, int nCmdId, LPTSTR sz, ULONG cbsz)
{
int nStringID = 0;
switch(nCmdId)
{
case IDM_DIALDLG_START:
nStringID = idsMenuDialer;
break;
case IDM_LVCONTEXT_INTERNET_CALL:
case IDM_TOOLS_INTERNET_CALL:
nStringID = idsMenuInternetCall;
break;
case IDM_LVCONTEXT_SENDMAIL:
case IDM_FILE_SENDMAIL:
nStringID = idsMenuSendMail;
break;
}
if(nStringID)
{
LoadString(hinstMapiX, nStringID, sz, cbsz);
return;
}
if(lpIAB->lpActionList)
{
LPWABACTIONITEM lpListItem = lpIAB->lpActionList;
while(lpListItem)
{
if(nCmdId >= lpListItem->nCmdIdFirst && nCmdId <= lpListItem->nCmdIdLast)
{
char szC[MAX_PATH];
ULONG cbszC = CharSizeOf(szC);
lpListItem->lpContextMenu->lpVtbl->GetCommandString(lpListItem->lpContextMenu,
nCmdId - lpListItem->nCmdIdFirst,
GCS_HELPTEXT,
NULL,
szC,
cbszC);
{
LPTSTR lp = ConvertAtoW(szC);
if(lp)
{
StrCpyN(sz, lp, cbsz);
LocalFreeAndNull(&lp);
}
}
break;
}
lpListItem = lpListItem->lpNext;
}
}
}