Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2827 lines
92 KiB

// THIS CODE AND INFORMATION 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.
//
// Copyright 2001 - 2003 Microsoft Corporation. All Rights Reserved.
//
// FILE: Features.cpp
//
//
// PURPOSE: Implementation wrapper class for WinXP PS Driver Features and Options.
//
//
//
// PLATFORMS: Windows XP, Windows Server 2003
//
//
#include "precomp.h"
#include "debug.h"
#include "oemui.h"
#include "stringutils.h"
#include "features.h"
#include "resource.h"
// StrSafe.h needs to be included last
// to disallow bad string functions.
#include <STRSAFE.H>
////////////////////////////////////////////////////////
// Internal Defines and Macros
////////////////////////////////////////////////////////
#define INITIAL_ENUM_FEATURES_SIZE 1024
#define INITIAL_ENUM_OPTIONS_SIZE 64
#define INITIAL_FEATURE_DISPLAY_NAME_SIZE 64
#define INITIAL_OPTION_DISPLAY_NAME_SIZE 32
#define INITIAL_GET_OPTION_SIZE 64
#define INITIAL_GET_REASON_SIZE 1024
#define DRIVER_FEATURE_PREFIX '%'
#define IS_DRIVER_FEATURE(f) (DRIVER_FEATURE_PREFIX == (f)[0])
// Flags that the uDisplayNameID should be returned as
// MAKEINTRESOURCE() instead of loading the string resource.
#define RETURN_INT_RESOURCE 1
// Macros to test for conditions of KEYWORDMAP entry.
#define IS_MAPPING_INT_RESOURCE(p) ((p)->dwFlags & RETURN_INT_RESOURCE)
// TAG the identifies feature OPTITEM data stuct.
#define FEATURE_OPTITEM_TAG 'FETR'
////////////////////////////////////////////////////////
// Type Definitions
////////////////////////////////////////////////////////
// Struct used to identify OPTITEM as
// feature OPTITEM and to map back from
// an OPTITEM to the feature.
typedef struct _tagFeatureOptitemData
{
DWORD dwSize;
DWORD dwTag;
PCSTR pszFeatureKeyword;
COptions *pOptions;
} FEATUREOPTITEMDATA, *PFEATUREOPTITEMDATA;
////////////////////////////////////////////////////////
// Internal Constants
////////////////////////////////////////////////////////
static KEYWORDMAP gkmFeatureMap[] =
{
"%AddEuro", NULL, IDS_ADD_EURO, OEMCUIP_PRNPROP, 0,
"%CtrlDAfter", NULL, IDS_CTRLD_AFTER, OEMCUIP_PRNPROP, 0,
"%CtrlDBefore", NULL, IDS_CTRLD_BEFORE, OEMCUIP_PRNPROP, 0,
//"%CustomPageSize", NULL, IDS_PSCRIPT_CUSTOMSIZE, OEMCUIP_DOCPROP, 0,
"%GraphicsTrueGray", NULL, IDS_TRUE_GRAY_GRAPH, OEMCUIP_PRNPROP, 0,
"%JobTimeout", NULL, IDS_JOBTIMEOUT, OEMCUIP_PRNPROP, 0,
"%MaxFontSizeAsBitmap", NULL, IDS_PSMAXBITMAP, OEMCUIP_PRNPROP, 0,
"%MetafileSpooling", NULL, IDS_METAFILE_SPOOLING, OEMCUIP_DOCPROP, 0,
"%MinFontSizeAsOutline", NULL, IDS_PSMINOUTLINE, OEMCUIP_PRNPROP, 0,
"%Mirroring", NULL, IDS_MIRROR, OEMCUIP_DOCPROP, 0,
"%Negative", NULL, IDS_NEGATIVE_PRINT, OEMCUIP_DOCPROP, 0,
"%Orientation", TEXT("COMPSTUI.DLL"), IDS_CPSUI_ORIENTATION, OEMCUIP_DOCPROP, RETURN_INT_RESOURCE,
"%OutputFormat", NULL, IDS_PSOUTPUT_OPTION, OEMCUIP_DOCPROP, 0,
"%OutputProtocol", NULL, IDS_PSPROTOCOL, OEMCUIP_PRNPROP, 0,
"%OutputPSLevel", NULL, IDS_PSLEVEL, OEMCUIP_DOCPROP, 0,
"%PageOrder", TEXT("COMPSTUI.DLL"), IDS_CPSUI_PAGEORDER, OEMCUIP_DOCPROP, RETURN_INT_RESOURCE,
"%PagePerSheet", TEXT("COMPSTUI.DLL"), IDS_CPSUI_NUP, OEMCUIP_DOCPROP, RETURN_INT_RESOURCE,
"%PSErrorHandler", NULL, IDS_PSERROR_HANDLER, OEMCUIP_DOCPROP, 0,
"%PSMemory", NULL, IDS_POSTSCRIPT_VM, OEMCUIP_PRNPROP, 0,
"%TextTrueGray", NULL, IDS_TRUE_GRAY_TEXT, OEMCUIP_PRNPROP, 0,
"%TTDownloadFormat", NULL, IDS_PSTT_DLFORMAT, OEMCUIP_DOCPROP, 0,
"%WaitTimeout", NULL, IDS_WAITTIMEOUT, OEMCUIP_PRNPROP, 0,
};
static const NUM_FEATURE_MAP = (sizeof(gkmFeatureMap)/sizeof(gkmFeatureMap[0]));
static KEYWORDMAP gkmOptionMap[] =
{
"True", TEXT("COMPSTUI.DLL"), IDS_CPSUI_TRUE, 0, RETURN_INT_RESOURCE,
"False", TEXT("COMPSTUI.DLL"), IDS_CPSUI_FALSE, 0, RETURN_INT_RESOURCE,
"Portrait", TEXT("COMPSTUI.DLL"), IDS_CPSUI_PORTRAIT, 0, RETURN_INT_RESOURCE,
"Landscape", TEXT("COMPSTUI.DLL"), IDS_CPSUI_LANDSCAPE, 0, RETURN_INT_RESOURCE,
"RotatedLandscape", TEXT("COMPSTUI.DLL"), IDS_CPSUI_ROT_LAND, 0, RETURN_INT_RESOURCE,
"Speed", NULL, IDS_PSOPT_SPEED, 0, RETURN_INT_RESOURCE,
"Portability", NULL, IDS_PSOPT_PORTABILITY, 0, 0,
"EPS", NULL, IDS_PSOPT_EPS, 0, 0,
"Archive", NULL, IDS_PSOPT_ARCHIVE, 0, 0,
"ASCII", NULL, IDS_PSPROTOCOL_ASCII, 0, 0,
"BCP", NULL, IDS_PSPROTOCOL_BCP, 0, 0,
"TBCP", NULL, IDS_PSPROTOCOL_TBCP, 0, 0,
"Binary", NULL, IDS_PSPROTOCOL_BINARY, 0, 0,
"FrontToBack", TEXT("COMPSTUI.DLL"), IDS_CPSUI_FRONTTOBACK, 0, RETURN_INT_RESOURCE,
"BackToFront", TEXT("COMPSTUI.DLL"), IDS_CPSUI_BACKTOFRONT, 0, RETURN_INT_RESOURCE,
"1", TEXT("COMPSTUI.DLL"), IDS_CPSUI_NUP_NORMAL, 0, RETURN_INT_RESOURCE,
"2", TEXT("COMPSTUI.DLL"), IDS_CPSUI_NUP_TWOUP, 0, RETURN_INT_RESOURCE,
"4", TEXT("COMPSTUI.DLL"), IDS_CPSUI_NUP_FOURUP, 0, RETURN_INT_RESOURCE,
"6", TEXT("COMPSTUI.DLL"), IDS_CPSUI_NUP_SIXUP, 0, RETURN_INT_RESOURCE,
"9", TEXT("COMPSTUI.DLL"), IDS_CPSUI_NUP_NINEUP, 0, RETURN_INT_RESOURCE,
"16", TEXT("COMPSTUI.DLL"), IDS_CPSUI_NUP_SIXTEENUP, 0, RETURN_INT_RESOURCE,
"Booklet", TEXT("COMPSTUI.DLL"), IDS_CPSUI_BOOKLET, 0, RETURN_INT_RESOURCE,
"Automatic", NULL, IDS_TTDL_DEFAULT, 0, 0,
"Outline", NULL, IDS_TTDL_TYPE1, 0, 0,
"Bitmap", NULL, IDS_TTDL_TYPE3, 0, 0,
"NativeTrueType", NULL, IDS_TTDL_TYPE42, 0, 0,
};
static const NUM_OPTION_MAP = (sizeof(gkmOptionMap)/sizeof(gkmOptionMap[0]));
////////////////////////////////////////////
//
// COptions Methods
//
//
// Private Methods
//
// Initializes class data members.
void COptions::Init()
{
m_wOptions = 0;
m_cType = TVOT_COMBOBOX;
m_pmszRaw = NULL;
m_pszFeature = NULL;
m_ppszOptions = NULL;
m_ptRange.x = 0;
m_ptRange.y = 0;
m_dwSize = 0;
m_pszUnits = NULL;
m_hHeap = NULL;
m_pInfo = NULL;
}
void COptions::Clear()
{
// Free memory associated with data members.
if(NULL != m_pmszRaw) HeapFree(m_hHeap, 0, m_pmszRaw);
if(NULL != m_ppszOptions) HeapFree(m_hHeap, 0, m_ppszOptions);
if( (NULL != m_pszUnits) && !IS_INTRESOURCE(m_pszUnits)) HeapFree(m_hHeap, 0, m_pszUnits);
// Free option info.
FreeOptionInfo();
// Initialize data members.
Init();
}
// Frees memory associated with Option Info array.
void COptions::FreeOptionInfo()
{
// Validate parameters.
if( (NULL == m_hHeap)
||
(NULL == m_pInfo)
)
{
return;
}
// Free strings in the array.
for(WORD wIndex = 0; wIndex < m_wOptions; ++wIndex)
{
PWSTR pszDisplay = m_pInfo[wIndex].pszDisplayName;
if( (NULL != pszDisplay)
&&
!(IS_INTRESOURCE(pszDisplay))
)
{
HeapFree(m_hHeap, 0, pszDisplay);
}
}
// Free the array.
HeapFree(m_hHeap, 0, m_pInfo);
m_pInfo = NULL;
}
// Will do init for features that need special handling.
HRESULT COptions::GetOptionsForSpecialFeatures(CUIHelper &Helper, POEMUIOBJ poemuiobj)
{
HRESULT hrResult = E_NOTIMPL;
// See if this is a special feature.
// If it is, then call the special option init for the
// feature.
if(!lstrcmpA(m_pszFeature, "%CustomPageSize"))
{
// There is no option init for CustomPageSize.
// The item itself must be handled specially.
hrResult = S_OK;
}
else if( !lstrcmpA(m_pszFeature, "%JobTimeout")
||
!lstrcmpA(m_pszFeature, "%WaitTimeout")
)
{
// JobTimeout and WaitTimeout feature options are string representations
// of an integer that represents number of seconds in the range 0 through
// 2,147,483,647 (i.e. LONG_MAX). However, COMPSTUI limits us to
// WORD size, which has range of 0 to 32,767 (i.e. SHRT_MAX).
m_cType = TVOT_UDARROW;
m_ptRange.x = 0;
m_ptRange.y = SHRT_MAX;
hrResult = GetOptionSelectionShort(Helper, poemuiobj);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("COptions::GetOptionsForSpecialFeatures() failed to get current selection for feature %hs. (hrResult = 0x%x)\r\n"),
m_pszFeature,
hrResult);
goto Exit;
}
}
else if( !lstrcmpA(m_pszFeature, "%MaxFontSizeAsBitmap")
||
!lstrcmpA(m_pszFeature, "%MinFontSizeAsOutline")
)
{
// MaxFontSizeAsBitmap, and MinFontSizeAsOutline feature options
// are string representations of an integer that represents number
// of pixels in the range 0 through 32,767 (i.e. SHRT_MAX).
m_cType = TVOT_UDARROW;
m_ptRange.x = 0;
m_ptRange.y = SHRT_MAX;
hrResult = GetOptionSelectionShort(Helper, poemuiobj);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("COptions::GetOptionsForSpecialFeatures() failed to get current selection for feature %hs. (hrResult = 0x%x)\r\n"),
m_pszFeature,
hrResult);
goto Exit;
}
}
else if( !lstrcmpA(m_pszFeature, "%PSMemory") )
{
DWORD dwType;
DWORD dwLevel;
DWORD dwNeeded;
// PSMemory option is string representation of an integer
// that represents number of seconds in the range 0
// through 32,767 (i.e. SHRT_MAX).
// However, the minimum is 172 KB for Level 1 and 249 KB for level 2
m_cType = TVOT_UDARROW;
m_ptRange.y = SHRT_MAX;
hrResult = GetOptionSelectionShort(Helper, poemuiobj);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("COptions::GetOptionsForSpecialFeatures() failed to get current selection for feature %hs. (hrResult = 0x%x)\r\n"),
m_pszFeature,
hrResult);
goto Exit;
}
// Get the global attribute for max language level.
hrResult = Helper.GetGlobalAttribute(poemuiobj,
0,
"LanguageLevel",
&dwType,
(PBYTE) &dwLevel,
sizeof(dwLevel),
&dwNeeded);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("COptions::GetOptionsForSpecialFeatures() failed to get global attribute \"LanguageLevel\". (hrResult = 0x%x)\r\n"),
hrResult);
goto Exit;
}
// Set minimum range based on PS Max Language level.
switch(dwLevel)
{
case 1:
m_ptRange.x = 172;
break;
default:
case 2:
case 3:
m_ptRange.x = 249;
break;
}
// Get Units string.
//GetStringResource(m_pszUnits
}
else if(!lstrcmpA(m_pszFeature, "%OutputPSLevel"))
{
hrResult = GetOptionsForOutputPSLevel(Helper, poemuiobj);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("COptions::GetOptionsForSpecialFeatures() failed to get current selection for feature %hs. (hrResult = 0x%x)\r\n"),
m_pszFeature,
hrResult);
goto Exit;
}
}
Exit:
return hrResult;
}
// Do init for PS Level options.
HRESULT COptions::GetOptionsForOutputPSLevel(CUIHelper &Helper, POEMUIOBJ poemuiobj)
{
WORD wCount = 0;
DWORD dwLevel = 0;
DWORD dwType = -1;
DWORD dwNeeded = 0;
HRESULT hrResult = E_NOTIMPL;
// PS Level is integers from 1 to "LanguageLevel"
// global atribute.
m_cType = TVOT_COMBOBOX;
// Get the global attribute for max language level.
hrResult = Helper.GetGlobalAttribute(poemuiobj,
0,
"LanguageLevel",
&dwType,
(PBYTE) &dwLevel,
sizeof(dwLevel),
&dwNeeded);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("COptions::GetOptionsForOutputPSLevel() failed to get global attribute \"LanguageLevel\". (hrResult = 0x%x)\r\n"),
hrResult);
goto Exit;
}
//
// Create Options for PS Level
//
// Set the number of options to the PS Level supported.
m_wOptions = (WORD) dwLevel;
// Allocate keyword list.
// Allocate memory for pointer to keyword and the keyword itself, so that
// the memory for the keyword strings will get de-allocated with the keyword list
// on object destruction, just like regular features for which EnumOptions works.
// User the size of a pointer (4 bytes on x86, and 8 on IA64) so that
// it begining of the keyword strings will be DWORD or QUADWORD aligned
// for x86 and IA64 respectively. Keyword strings aren't required to
// be DWORD or QUADWORD aligned, but it is more optimal. Also, this gives
// some additional space for the case of %OutputPSLevel keywords, which are
// in the range of 1 through the max PostScript level supported, and only
// require 2 CHARs (1 for the digit and one for the NULL terminator).
m_ppszOptions = (PCSTR *) HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY, m_wOptions * ( sizeof(PSTR) + sizeof(PCSTR *) ) );
if(NULL == m_ppszOptions)
{
ERR(ERRORTEXT("COptions::GetOptionsForOutputPSLevel() failed to allocate option keyword array for PS Level.\r\n"));
hrResult = E_OUTOFMEMORY;
goto Exit;
}
// Allocate option info array.
m_pInfo = (POPTION_INFO) HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY, m_wOptions * sizeof(OPTION_INFO));
if(NULL == m_pInfo)
{
ERR(ERRORTEXT("COptions::GetOptionsForOutputPSLevel() failed to allocate info array for PS Level.\r\n"));
hrResult = E_OUTOFMEMORY;
goto Exit;
}
// Init the option info.
for(wCount = 0; wCount < m_wOptions; ++wCount)
{
// Init keyword.
// The memory for both the keyword list and the keyword strings was allocated above.
m_ppszOptions[wCount] = (PSTR)(m_ppszOptions + m_wOptions) + (sizeof(PSTR) * wCount);
hrResult = StringCbPrintfA(const_cast<PSTR>(m_ppszOptions[wCount]), sizeof(PSTR), "%d", wCount + 1);
if(FAILED(hrResult))
{
ERR(ERRORTEXT("COptions::GetOptionsForOutputPSLevel() failed to string representation of item %d.\r\n"),
wCount + 1);
goto Exit;
}
// Init option display name.
m_pInfo[wCount].pszDisplayName = (PWSTR) HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY, 2 * sizeof(WCHAR));
if(NULL == m_pInfo[wCount].pszDisplayName)
{
ERR(ERRORTEXT("COptions::GetOptionsForOutputPSLevel() failed to allocate display string for Level %d.\r\n"),
wCount);
hrResult = E_OUTOFMEMORY;
goto Exit;
}
// Init option display name.
hrResult = StringCchPrintfW(m_pInfo[wCount].pszDisplayName, 2, TEXT("%d"), wCount + 1);
if(FAILED(hrResult))
{
ERR(ERRORTEXT("COptions::GetOptionsForOutputPSLevel() failed to create display name for item %d.\r\n"),
wCount + 1);
goto Exit;
}
}
//
// Get Current Selection
//
hrResult = GetOptionSelectionIndex(Helper, poemuiobj);
Exit:
// Don't need to clean up memory allocation on error, since
// all memory allocated are assigned to data members, which
// will be cleaned up in the object destructor.
return hrResult;
}
HRESULT COptions::GetOptionSelectionString(CUIHelper &Helper, POEMUIOBJ poemuiobj, PSTR *ppszSel)
{
PSTR pmszFeature = NULL;
PSTR pmszBuf = NULL;
WORD wCount = 0;
PCSTR *ppszList = NULL;
DWORD dwFeatureSize = 0;
DWORD dwNeeded = 0;
DWORD dwSize = INITIAL_GET_OPTION_SIZE;
HRESULT hrResult = S_OK;
//
// Make single feature multi-sz.
//
// Allocate singe feature multi-sz buffer.
dwFeatureSize = lstrlenA(m_pszFeature) + 2;
pmszFeature = (PSTR) HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY, dwFeatureSize);
if(NULL == pmszFeature)
{
ERR(ERRORTEXT("COptions::GetOptionSelectionString() failed to allocate buffer for single feature multi-sz for feature %hs.\r\n"),
m_pszFeature);
hrResult = E_OUTOFMEMORY;
goto Exit;
}
// Just need to do a regular string copy, since the buffer is already zero filled.
hrResult = StringCbCopyA(pmszFeature, dwFeatureSize, m_pszFeature);
if(FAILED(hrResult))
{
ERR(ERRORTEXT("COptions::GetOptionSelectionString() failed to copy feature string %hs.\r\n"), m_pszFeature);
}
// Allocated initial buffer of reasonible size.
pmszBuf = (PSTR) HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY, dwSize);
if(NULL == pmszBuf)
{
ERR(ERRORTEXT("COptions::GetOptionSelectionString() failed to allocate buffer to get current setting for feature %hs.\r\n"),
m_pszFeature);
hrResult = E_OUTOFMEMORY;
goto Exit;
}
// Get current option selection.
hrResult = Helper.GetOptions(poemuiobj,
0,
pmszFeature,
dwFeatureSize,
pmszBuf,
dwSize,
&dwNeeded);
if( (E_OUTOFMEMORY == hrResult) && (dwSize < dwNeeded) )
{
PSTR pTemp = NULL;
// INVARIANT: initial buffer not large enough.
// Re-alloc buffer and try again.
pTemp = (PSTR) HeapReAlloc(m_hHeap, HEAP_ZERO_MEMORY, pmszBuf, dwNeeded);
if(NULL == pTemp)
{
ERR(ERRORTEXT("COptions::GetOptionSelectionString() failed to re-allocate buffer to get current setting for feature %hs.\r\n"),
m_pszFeature);
hrResult = E_OUTOFMEMORY;
goto Exit;
}
pmszBuf = pTemp;
// Try to get current option selection again.
hrResult = Helper.GetOptions(poemuiobj,
0,
pmszFeature,
dwFeatureSize,
pmszBuf,
dwNeeded,
&dwNeeded);
}
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("COptions::GetOptionSelectionString() failed to get current setting for feature %hs. (hrResult = 0x%x)\r\n"),
m_pszFeature,
hrResult);
goto Exit;
}
// NOTE: The return string from GetOptions() may return
// contain no strings and not return a HRESULT error
// when the feature isn't supported in the current document or
// printer sticky mode.
if('\0' == pmszBuf[0])
{
// Feature not supported for this sticky mode.
goto Exit;
}
// Parse the results buffer to see what the current setting is.
hrResult = MakeStrPtrList(m_hHeap, pmszBuf, &ppszList, &wCount);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("COptions::GetOptionSelectionString() failed to make string list for GetOptions() return for feature %hs. (hrResult = 0x%x)\r\n"),
m_pszFeature,
hrResult);
goto Exit;
}
// Check that we got 2 strings back.
if(2 != wCount)
{
WARNING(DLLTEXT("COptions::GetOptionSelectionString() the GetOption() return string list for \r\n\tfeature %hs is not of size 2.\r\n\tNumber of string is %d\r\n"),
m_pszFeature,
wCount);
// Bail if we don't have at least 2 strings.
if(2 > wCount)
{
goto Exit;
}
}
// Return copy of just the GetOption() result.
*ppszSel = MakeStringCopy(m_hHeap, ppszList[1]);
if(NULL == *ppszSel)
{
ERR(ERRORTEXT("COptions::GetOptionSelectionString() failed to duplicate string GetOptions() return for feature %hs. (hrResult = 0x%x)\r\n"),
m_pszFeature,
hrResult);
hrResult = E_OUTOFMEMORY;
goto Exit;
}
Exit:
// Free local buffers.
if(NULL != pmszFeature) HeapFree(m_hHeap, 0, pmszFeature);
if(NULL != pmszBuf) HeapFree(m_hHeap, 0, pmszBuf);
if(NULL != ppszList) HeapFree(m_hHeap, 0, ppszList);
return hrResult;
}
// Gets current Options selection for LONG value.
HRESULT COptions::GetOptionSelectionLong(CUIHelper &Helper, POEMUIOBJ poemuiobj)
{
PSTR pszSel = NULL;
HRESULT hrResult = S_OK;
// Get option selection string.
hrResult = GetOptionSelectionString(Helper, poemuiobj, &pszSel);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("COptions::GetOptionSelectionLong() failed to get string for GetOptions() return for feature %hs. (hrResult = 0x%x)\r\n"),
m_pszFeature,
hrResult);
goto Exit;
}
// Convert string option to LONG and uses that as the selection.
if(NULL != pszSel) m_Sel = atol(pszSel);
Exit:
// Free local buffers.
if(NULL != pszSel) HeapFree(m_hHeap, 0, pszSel);
return hrResult;
}
// Gets current Options selection for SHORT value.
HRESULT COptions::GetOptionSelectionShort(CUIHelper &Helper, POEMUIOBJ poemuiobj)
{
PSTR pszSel = NULL;
HRESULT hrResult = S_OK;
// Get option selection string.
hrResult = GetOptionSelectionString(Helper, poemuiobj, &pszSel);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("COptions::GetOptionSelectionLong() failed to get string for GetOptions() return for feature %hs. (hrResult = 0x%x)\r\n"),
m_pszFeature,
hrResult);
goto Exit;
}
// Convert string option to LONG and uses that as the selection.
if(NULL != pszSel) m_Sel = atoi(pszSel) & 0x00ffff;
Exit:
// Free local buffers.
if(NULL != pszSel) HeapFree(m_hHeap, 0, pszSel);
return hrResult;
}
// Gets current option selection for feature.
HRESULT COptions::GetOptionSelectionIndex(CUIHelper &Helper, POEMUIOBJ poemuiobj)
{
PSTR pszSel = NULL;
HRESULT hrResult = S_OK;
// Get option selection string.
hrResult = GetOptionSelectionString(Helper, poemuiobj, &pszSel);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("COptions::GetOptionSelectionIndex() failed to get string for GetOptions() return for feature %hs. (hrResult = 0x%x)\r\n"),
m_pszFeature,
hrResult);
goto Exit;
}
// Find the matching option for the string returned from GetOption.
m_Sel = FindOption(pszSel, m_wOptions - 1);
Exit:
// Free local buffers.
if(NULL != pszSel) HeapFree(m_hHeap, 0, pszSel);
return hrResult;
}
//
// Public Methods
//
// Default constructor
COptions::COptions()
{
Init();
}
// Destructor
COptions::~COptions()
{
Clear();
}
// Get the option list for a feature
HRESULT COptions::Acquire(HANDLE hHeap,
CUIHelper &Helper,
POEMUIOBJ poemuiobj,
PCSTR pszFeature)
{
DWORD dwNeeded = 0;
HRESULT hrResult = S_OK;
VERBOSE(DLLTEXT("COptions::Acquire(0x%p, Helper, 0x%p, %hs) entered.\r\n"),
hHeap,
poemuiobj,
pszFeature ? pszFeature : "NULL");
// Don't retreive the Options again if we already got them.
if( (0 < m_wOptions)
&&
(NULL != m_pszFeature)
&&
!lstrcmpA(m_pszFeature, pszFeature)
)
{
VERBOSE(DLLTEXT("COptions::Acquire() already have options for feature %hs.\r\n"), m_pszFeature);
VERBOSE(DLLTEXT("COptions::Acquire() returning with HRESULT of S_OK\r\n"));
return S_OK;
}
// Save the heap handle for use later, such as freeing memory at destruction.
m_hHeap = hHeap;
// Store Keyword string.
m_pszFeature = pszFeature;
//
// Enumerate Options.
//
// Some features require special handling for initializing their options.
// EnumOpionts isn't implemented for these features.
// Return of E_NOTIMPL indicates it isn't the feature doesn't
// need special handling.
hrResult = GetOptionsForSpecialFeatures(Helper, poemuiobj);
if( SUCCEEDED(hrResult)
||
(!SUCCEEDED(hrResult) && (E_NOTIMPL != hrResult) )
)
{
// We either dealt with the special feature or incounter an error
// trying to deal with the special feature.
goto Exit;
}
// To try to cut down on having to call EnumOptions more than once,
// pre-allocate a buffer of reasonable size.
m_dwSize = INITIAL_ENUM_OPTIONS_SIZE;
m_pmszRaw = (PSTR) HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY, m_dwSize);
if(NULL == m_pmszRaw)
{
ERR(ERRORTEXT("COptions::Acquire() alloc for options for feature %hs failed.\r\n"), m_pszFeature);
hrResult = E_OUTOFMEMORY;
goto Exit;
}
// Try to get options list with initial buffer.
hrResult = Helper.EnumOptions(poemuiobj, 0, m_pszFeature, m_pmszRaw, m_dwSize, &dwNeeded);
if( (E_OUTOFMEMORY == hrResult) && (m_dwSize < dwNeeded))
{
PSTR pTemp;
// INVARIANT: options list multi-sz wasn't large enough.
// Re-allocate the buffer and try again.
pTemp = (PSTR) HeapReAlloc(m_hHeap, HEAP_ZERO_MEMORY, m_pmszRaw, dwNeeded);
if(NULL == pTemp)
{
ERR(ERRORTEXT("COptions::Acquire() re-alloc for options list for feature %hs failed.\r\n"), m_pszFeature);
hrResult = E_OUTOFMEMORY;
goto Exit;
}
m_pmszRaw = pTemp;
m_dwSize = dwNeeded;
// Try again to get the options list.
hrResult = Helper.EnumOptions(poemuiobj, 0, m_pszFeature, m_pmszRaw, m_dwSize, &dwNeeded);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("COptions::Acquire() failed to EnumOptions() for feature %hs after re-allocating buffer.\r\n"), m_pszFeature);
goto Exit;
}
}
// Make sure we got the option list.
// Can't do anything more with out it.
if(!SUCCEEDED(hrResult))
{
if(E_NOTIMPL != hrResult) ERR(ERRORTEXT("COptions::Acquire() failed to enumerate options for feature %hs. (hrResult = 0x%x)\r\n"), m_pszFeature, hrResult);
goto Exit;
}
// INVARIANT: successfully got option keyword list.
// Create array of string pointers to the Option names
// in the multi-sz we got from EnumOptions().
hrResult = MakeStrPtrList(m_hHeap, m_pmszRaw, &m_ppszOptions, &m_wOptions);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("COptions::Acquire() failed to create pointer list to options. (hrResult = 0x%x)\r\n"), hrResult);
goto Exit;
}
//
// Build Option Information
//
// Allocate array to hold feature info
m_pInfo = (POPTION_INFO) HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY, m_wOptions * sizeof(OPTION_INFO));
if(NULL == m_pInfo)
{
ERR(ERRORTEXT("COptions::Acquire() failed to alloc feature info array.\r\n"));
hrResult = E_OUTOFMEMORY;
goto Exit;
}
// For each option, build or get useful information, such as display name.
for(WORD wIndex = 0; wIndex < m_wOptions; ++wIndex)
{
POPTION_INFO pCurrent = m_pInfo + wIndex;
// Get or build a keyword mapping entry
// that maps from keyword to usefully where to get info, such as
// display name, icon, option type, for keywords that may not be
// able to get info for from Helper.
pCurrent->pMapping = FindKeywordMapping(gkmOptionMap, NUM_OPTION_MAP, m_ppszOptions[wIndex]);
// Get display names for each of the Options.
// The function implements a heuristic for detemining the display name,
// since can't get the display name from the UI Helper for all Options.
hrResult = DetermineOptionDisplayName(m_hHeap,
Helper,
poemuiobj,
m_pszFeature,
m_ppszOptions[wIndex],
pCurrent->pMapping,
&pCurrent->pszDisplayName);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("COptions::Acquire() failed to get display name for %hs of feature %hs. (hrResult = 0x%x)\r\n"),
m_ppszOptions[wIndex],
m_pszFeature,
hrResult);
goto Exit;
}
}
//
// Get current option selection.
//
hrResult = GetOptionSelectionIndex(Helper, poemuiobj);
Exit:
// Clean up if weren't successful.
if(!SUCCEEDED(hrResult))
{
Clear();
}
VERBOSE(DLLTEXT("COptions::Acquire() returning with HRESULT of 0x%x\r\n"), hrResult);
return hrResult;
}
// Return nth options keyword.
PCSTR COptions::GetKeyword(WORD wIndex) const
{
// Validate parameters.
if( (wIndex >= m_wOptions)
||
(NULL == m_ppszOptions)
)
{
return NULL;
}
return m_ppszOptions[wIndex];
}
// Return nth options display name.
PCWSTR COptions::GetName(WORD wIndex) const
{
// Validate parameters.
if( (wIndex >= m_wOptions)
||
(NULL == m_pInfo)
)
{
ERR(ERRORTEXT("COptions::GetName() invalid parameters.\r\n"));
return NULL;
}
if(NULL == m_pInfo[wIndex].pszDisplayName) ERR(ERRORTEXT("COptions::GetName() returning NULL option display name.\r\n"));
return m_pInfo[wIndex].pszDisplayName;
}
// Find option with matching keyword string.
WORD COptions::FindOption(PCSTR pszOption, WORD wDefault) const
{
BOOL bFound = FALSE;
WORD wMatch = wDefault;
// Validate parameters.
if( (NULL == pszOption)
||
(NULL == m_ppszOptions)
)
{
return wDefault;
}
// Walk the option keyword looking for a match.
for(WORD wIndex = 0; !bFound && (wIndex < m_wOptions); ++wIndex)
{
bFound = !lstrcmpA(pszOption, m_ppszOptions[wIndex]);
if(bFound)
{
wMatch = wIndex;
}
}
return wMatch;
}
// Initializes OptItem with options for a feature.
HRESULT COptions::InitOptItem(HANDLE hHeap, POPTITEM pOptItem)
{
WORD wParams = 0;
WORD wOptions = 0;
HRESULT hrResult = S_OK;
// Set option selection.
pOptItem->pSel = m_pSel;
// Get count of number of options.
// NOTE: Some feature options have no counts.
wOptions = GetCount();
// Different OPTTYPE types require different number of OPTPARAMs.
switch(m_cType)
{
// For up down arrow control, the OPTPARAMs need is 2.
case TVOT_UDARROW:
wParams = 2;
break;
// For combobox, the OPTPARAMs needed is on per options.
case TVOT_COMBOBOX:
wParams = wOptions;
break;
// The default is the option count.
default:
WARNING(DLLTEXT("COptions::InitOptItem() OPTTYPE type %d num of OPTPARAMs not handled. Default to option count of %d.\r\n"),
m_cType,
wOptions);
wParams = wOptions;
break;
}
// Only do OPTTYPEs if we have non-Zero number of OPTPARAMs.
// Every OPTTYPE has at leas one OPTPARAM.
if(0 < wParams)
{
// Allocate memory for feature options.
pOptItem->pOptType = CreateOptType(hHeap, wParams);
if(NULL == pOptItem->pOptType)
{
ERR(ERRORTEXT("COptions::InitOptItem() failed to allocate OPTTYPEs for OPTITEM %hs.\r\n"),
m_pszFeature);
hrResult = E_OUTOFMEMORY;
goto Exit;
}
// Set OPTTYPE.
pOptItem->pOptType->Type = m_cType;
// Different OPTTYPE types require different initialization.
switch(m_cType)
{
// For up down arrow control, OPTPARAM[0] is used by the contrl.
// pOptParam[0]->pData is the Units description string.
// pOptParam[1].IconID is the min limit.
// pOptParam[1].lParam is the max limit.
case TVOT_UDARROW:
assert(2 == wParams);
pOptItem->pOptType->pOptParam[0].pData = m_pszUnits;
pOptItem->pOptType->pOptParam[1].IconID = m_ptRange.x;
pOptItem->pOptType->pOptParam[1].lParam = m_ptRange.y;
break;
// For combobox, the pOptParam[n].pData is the display name of the option.
case TVOT_COMBOBOX:
for(WORD wIndex = 0; wIndex < wParams; ++wIndex)
{
pOptItem->pOptType->pOptParam[wIndex].pData = const_cast<PWSTR>(GetName(wIndex));
}
break;
default:
ERR(ERRORTEXT("COptions::InitOptItem() OPTTYPE type %d OPTTYPE init not handled.\r\n"),
m_cType);
break;
}
}
Exit:
return hrResult;
}
// Refresh option selection.
HRESULT COptions::RefreshSelection(CUIHelper &Helper, POEMUIOBJ poemuiobj)
{
HRESULT hrResult = S_OK;
// Method for getting option selection is based
// on OPTTYPE type.
switch(m_cType)
{
case TVOT_UDARROW:
hrResult = GetOptionSelectionShort(Helper, poemuiobj);
break;
case TVOT_COMBOBOX:
hrResult = GetOptionSelectionIndex(Helper, poemuiobj);
break;
default:
ERR(ERRORTEXT("COptions::RefreshSelection() not handled for type %d OPTTYPE.\r\n"),
m_cType);
break;
}
return hrResult;
}
////////////////////////////////////////////
//
// CFeatures Methods
//
//
// Private Methods
//
// Initializes class
void CFeatures::Init()
{
// Initialize data members.
m_wFeatures = 0;
m_wDocFeatures = 0;
m_wPrintFeatures = 0;
m_pmszRaw = NULL;
m_ppszKeywords = NULL;
m_dwSize = 0;
m_hHeap = NULL;
m_pInfo = NULL;
}
// Cleans up class and re-initialize it.
void CFeatures::Clear()
{
// Free memory associated with data members.
if(NULL != m_pmszRaw) HeapFree(m_hHeap, 0, m_pmszRaw);
if(NULL != m_ppszKeywords) HeapFree(m_hHeap, 0, m_ppszKeywords);
// Free feature info
FreeFeatureInfo();
// Re-initialize
Init();
}
// Free feature info
void CFeatures::FreeFeatureInfo()
{
// Validate parameters.
if( (NULL == m_hHeap)
||
(NULL == m_pInfo)
)
{
return;
}
// Free memory associated with feature info.
for(WORD wIndex = 0; wIndex < m_wFeatures; ++wIndex)
{
PWSTR pszDisplay = m_pInfo[wIndex].pszDisplayName;
// Free display name.
if( (NULL != pszDisplay)
&&
!IS_INTRESOURCE(pszDisplay)
)
{
HeapFree(m_hHeap, 0, pszDisplay);
}
}
// Free feature info array.
// Feature Info array allocated with new so
// that each of the constructors for COptions
// in fhte Feature Info array will be called.
delete[] m_pInfo;
}
// Turns index for mode to modeless index, which
// is the real index to the feature.
WORD CFeatures::GetModelessIndex(WORD wIndex, DWORD dwMode) const
{
WORD wCount = 0;
switch(dwMode)
{
// Number of features, all modes
case 0:
wCount = wIndex;
break;
// Find the nth feature that matches the mode
case OEMCUIP_DOCPROP:
case OEMCUIP_PRNPROP:
// Walk the feature list looking for nth feature
// with matching mode.
for(wCount = 0; wCount < m_wFeatures; ++wCount)
{
// Count down to the feature we want.
// Only count down for matching modes.
if(dwMode == m_pInfo[wCount].dwMode)
{
if(0 == wIndex)
{
break;
}
else
{
--wIndex;
}
}
}
break;
}
return wCount;
}
//
// Public Methods
//
// Default constructor
CFeatures::CFeatures()
{
Init();
}
// Destructor
CFeatures::~CFeatures()
{
// Clean up class.
Clear();
}
// Gets Core Driver Features, if not already retrieved.
HRESULT CFeatures::Acquire(HANDLE hHeap,
CUIHelper &Helper,
POEMUIOBJ poemuiobj
)
{
WORD wIndex = 0;
DWORD dwNeeded = 0;
HRESULT hrResult = S_OK;
VERBOSE(DLLTEXT("CFeatures::Acquire(0x%p, Helper, 0x%p) entered.\r\n"),
hHeap,
poemuiobj);
// Don't retreive the Features again if we already got them.
if(0 < m_wFeatures)
{
VERBOSE(DLLTEXT("CFeatures::Acquire() features already enumerated.\r\n"));
VERBOSE(DLLTEXT("CFeatures::Acquire() returning S_OK.\r\n"));
return S_OK;
}
// Save the heap handle for use later, such as freeing memory at destruction.
m_hHeap = hHeap;
//
// Enumerate features.
//
// To try to cut down on having to call EnumFeatures more than once,
// pre-allocate a buffer of reasonable size.
m_dwSize = INITIAL_ENUM_FEATURES_SIZE;
m_pmszRaw = (PSTR) HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY, m_dwSize);
if(NULL == m_pmszRaw)
{
ERR(ERRORTEXT("CFeatures::Acquire() alloc for feature list failed.\r\n"));
hrResult = E_OUTOFMEMORY;
goto Exit;
}
// Try to get feature list with initial buffer.
hrResult = Helper.EnumFeatures(poemuiobj, 0, m_pmszRaw, m_dwSize, &dwNeeded);
if( (E_OUTOFMEMORY == hrResult) && (m_dwSize < dwNeeded))
{
PSTR pTemp;
// INVARIANT: feature list multi-sz wasn't large enough.
// Re-allocate the buffer and try again.
pTemp = (PSTR) HeapReAlloc(m_hHeap, HEAP_ZERO_MEMORY, m_pmszRaw, dwNeeded);
if(NULL == pTemp)
{
ERR(ERRORTEXT("CFeatures::Acquire() re-alloc for feature list failed.\r\n"));
hrResult = E_OUTOFMEMORY;
goto Exit;
}
m_pmszRaw = pTemp;
m_dwSize = dwNeeded;
// Try again to get the feature list.
hrResult = Helper.EnumFeatures(poemuiobj, 0, m_pmszRaw, m_dwSize, &dwNeeded);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("CFeatures::Acquire() failed to EnumFeatures() after re-allocating buffer.\r\n"));
goto Exit;
}
}
// Make sure we got the feature list.
// Can't do anything more with out it.
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("CFeatures::Acquire() failed to enumerate features. (hrResult = 0x%x)\r\n"), hrResult);
goto Exit;
}
// INVARIANT: successfully got feature keyword list.
// Create array of string pointers to the feature keywords
// in the multi-sz we got from EnumFreatures().
hrResult = MakeStrPtrList(m_hHeap, m_pmszRaw, &m_ppszKeywords, &m_wFeatures);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("CFeatures::Acquire() failed to create pointer list to keywords. (hrResult = 0x%x)\r\n"), hrResult);
goto Exit;
}
//
// Build Feature Information
//
// Allocate array to hold feature info
// Use new for allocation so class
// constructors/destructors get called.
m_pInfo = new FEATURE_INFO[m_wFeatures];
if(NULL == m_pInfo)
{
ERR(ERRORTEXT("CFeatures::Acquire() failed to alloc feature info array.\r\n"));
hrResult = E_OUTOFMEMORY;
goto Exit;
}
// For each feature, build/get feature info....
for(wIndex = 0; wIndex < m_wFeatures; ++wIndex)
{
PFEATURE_INFO pCurrent = m_pInfo + wIndex;
// Get or build a keyword mapping entry
// that maps from keyword to usefully where to get info, such as
// display name, icon, option type, for keywords that may not be
// able to get info for from Helper.
pCurrent->pMapping = FindKeywordMapping(gkmFeatureMap, NUM_FEATURE_MAP, m_ppszKeywords[wIndex]);
// Get display names for each of the featurs.
// The function implements a heuristic for detemining the display name,
// since can't get the display name from the UI Helper for all features.
hrResult = DetermineFeatureDisplayName(m_hHeap,
Helper,
poemuiobj,
m_ppszKeywords[wIndex],
pCurrent->pMapping,
&pCurrent->pszDisplayName);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("CFeatures::Acquire() failed to get display name for feature %hs. (hrResult = 0x%x)\r\n"),
m_ppszKeywords[wIndex],
hrResult);
goto Exit;
}
// Get options for each feature.
// NOTE: some features don't have options; the HRESULT will be E_NOTIMPL for these.
hrResult = pCurrent->Options.Acquire(hHeap,
Helper,
poemuiobj,
m_ppszKeywords[wIndex]);
if(!SUCCEEDED(hrResult) && (E_NOTIMPL != hrResult))
{
ERR(ERRORTEXT("CFeatures::Acquire() failed to get options for feature %hs. (hrResult = 0x%x)\r\n"),
m_ppszKeywords[wIndex],
hrResult);
goto Exit;
}
// Determine if feature is Printer or Document sticky.
hrResult = DetermineStickiness(Helper,
poemuiobj,
m_ppszKeywords[wIndex],
pCurrent->pMapping,
&pCurrent->dwMode);
// Don't propagate error if failure from unhandled driver feature.
if( !SUCCEEDED(hrResult)
&&
!IS_DRIVER_FEATURE(m_ppszKeywords[wIndex])
)
{
ERR(ERRORTEXT("CFeatures::Acquire() failed to determine stickiness for feature %hs. (hrResult = 0x%x)\r\n"),
m_ppszKeywords[wIndex],
hrResult);
goto Exit;
}
// Keep track of mode counts.
switch(pCurrent->dwMode)
{
case OEMCUIP_DOCPROP:
++m_wDocFeatures;
break;
case OEMCUIP_PRNPROP:
++m_wPrintFeatures;
break;
default:
ERR(ERRORTEXT("CFeatures::Acquire() unknown stickiness for feature %hs.\r\n"),
m_ppszKeywords[wIndex]);
break;
}
}
// INVARIANT: successfully build feature list.
// Make sure that we always return success if we reach this point.
hrResult = S_OK;
Exit:
// Clean up if weren't successful.
if(!SUCCEEDED(hrResult))
{
Clear();
}
VERBOSE(DLLTEXT("CFeatures::Acquire() returning HRESULT of 0x%x.\r\n"), hrResult);
return hrResult;
}
// Returns number of features contained in class instance.
WORD CFeatures::GetCount(DWORD dwMode) const
{
WORD wCount = 0;
switch(dwMode)
{
// Number of features, all modes
case 0:
wCount = m_wFeatures;
break;
case OEMCUIP_DOCPROP:
wCount = m_wDocFeatures;
break;
case OEMCUIP_PRNPROP:
wCount = m_wPrintFeatures;
break;
}
VERBOSE(DLLTEXT("CFeatures::GetCount() returning %d\r\n"), wCount);
return wCount;
}
// Returns nth feature's keyword
PCSTR CFeatures::GetKeyword(WORD wIndex, DWORD dwMode) const
{
// Validate parameters.
if( (wIndex >= GetCount(dwMode))
||
(NULL == m_ppszKeywords)
)
{
return NULL;
}
// Get internal index.
wIndex = GetModelessIndex(wIndex, dwMode);
// Return keyword
return m_ppszKeywords[wIndex];
}
// Return nth feature's Display Name.
PCWSTR CFeatures::GetName(WORD wIndex, DWORD dwMode) const
{
// Validate parameters.
if( (wIndex >= GetCount(dwMode))
||
(NULL == m_pInfo)
)
{
return NULL;
}
// Get internal index.
wIndex = GetModelessIndex(wIndex, dwMode);
// Return display name.
return m_pInfo[wIndex].pszDisplayName;
}
// Returns pointer to option class for nth feature.
COptions* CFeatures::GetOptions(WORD wIndex, DWORD dwMode) const
{
// Validate parameters.
if( (wIndex >= GetCount(dwMode))
||
(NULL == m_pInfo)
)
{
return NULL;
}
// Get internal index.
wIndex = GetModelessIndex(wIndex, dwMode);
// Return options pointer.
return &m_pInfo[wIndex].Options;
}
// Formats OPTITEM for specied feature.
HRESULT CFeatures::InitOptItem(HANDLE hHeap, POPTITEM pOptItem, WORD wIndex, DWORD dwMode)
{
COptions *pOptions = NULL;
HRESULT hrResult = S_OK;
PFEATUREOPTITEMDATA pData = NULL;
// Validate parameters.
if( (wIndex >= GetCount(dwMode))
||
(NULL == m_pInfo)
||
(NULL == pOptItem)
)
{
return E_INVALIDARG;
}
// Map mode index to internal index.
wIndex = GetModelessIndex(wIndex, dwMode);
// Get name of feature.
pOptItem->pName = const_cast<PWSTR>(GetName(wIndex));
// Add feature OPTITEM data to OPTITEM to facilitate saving
// selection changes.
pData = (PFEATUREOPTITEMDATA) HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(FEATUREOPTITEMDATA));
if(NULL == pData)
{
ERR(ERRORTEXT("CFeatures::InitOptItem() failed to allocated memory for feature OPTITEM data."));
hrResult = E_OUTOFMEMORY;
goto Exit;
}
pData->dwSize = sizeof(FEATUREOPTITEMDATA);
pData->dwTag = FEATURE_OPTITEM_TAG;
pData->pszFeatureKeyword = GetKeyword(wIndex);
pData->pOptions = GetOptions(wIndex);
pOptItem->UserData = (ULONG_PTR) pData;
// Get pointer to options for this feature.
// NOTE: some features do not have option list for various reasons.
pOptions = GetOptions(wIndex);
if(NULL != pOptions)
{
// Initialize COption parts of the OPTITEM
hrResult = pOptions->InitOptItem(hHeap, pOptItem);
}
Exit:
Dump(pOptItem);
return hrResult;
}
//////////////////////////////////////////////////
//
// Regular functions not part of class
//
//////////////////////////////////////////////////
// Maps feature keywords to display names for the features.
HRESULT DetermineFeatureDisplayName(HANDLE hHeap,
CUIHelper &Helper,
POEMUIOBJ poemuiobj,
PCSTR pszKeyword,
const PKEYWORDMAP pMapping,
PWSTR *ppszDisplayName)
{
DWORD dwDataType = -1;
DWORD dwSize = INITIAL_FEATURE_DISPLAY_NAME_SIZE;
DWORD dwNeeded = 0;
HRESULT hrResult = S_OK;
// Validate parameters.
if( (NULL == hHeap)
||
(NULL == pszKeyword)
||
(NULL == ppszDisplayName)
)
{
ERR(ERRORTEXT("DetermineFeatureDisplayName() invalid arguement.\r\n"));
hrResult = E_INVALIDARG;
goto Exit;
}
//
// Call the Helper function.
//
// Helper will return Display Names for PPD Features, but
// not for Driver Synthisized features (i.e. features prefixed with %).
// Do it for Driver Synthisized features, just in case the helper
// interface changes to support it.
// Pre-allocate a buffer of reasonable size to try
// to one have to call GetFeatureAttribute() once.
*ppszDisplayName = (PWSTR) HeapAlloc(hHeap, HEAP_ZERO_MEMORY, dwSize);
if(NULL == *ppszDisplayName)
{
ERR(ERRORTEXT("DetermineFeatureDisplayName() alloc for feature display name failed.\r\n"));
hrResult = E_OUTOFMEMORY;
goto Exit;
}
// Try to get diplay name for feature from Helper.
hrResult = Helper.GetFeatureAttribute(poemuiobj,
0,
pszKeyword,
"DisplayName",
&dwDataType,
(PBYTE) *ppszDisplayName,
dwSize,
&dwNeeded);
if( (E_OUTOFMEMORY == hrResult) && (dwSize < dwNeeded))
{
PWSTR pTemp;
// INVARIANT: initial buffer wasn't large enough.
// Re-alloc buffer and try again.
pTemp = (PWSTR) HeapReAlloc(hHeap, HEAP_ZERO_MEMORY, *ppszDisplayName, dwNeeded);
if(NULL == pTemp)
{
ERR(ERRORTEXT("DetermineFeatureDisplayName() re-alloc for feature display name failed.\r\n"));
hrResult = E_OUTOFMEMORY;
goto Exit;
}
*ppszDisplayName = pTemp;
// Try to get the display name from Helper, again.
hrResult = Helper.GetFeatureAttribute(poemuiobj,
0,
pszKeyword,
"DisplayName",
&dwDataType,
(PBYTE) *ppszDisplayName,
dwNeeded,
&dwNeeded);
}
if(SUCCEEDED(hrResult))
{
// INVARIANT: Successfully got display name from Helper for feature.
// Don't need to do anything more.
// Check the data type, it should be kADT_UNICODE.
if(kADT_UNICODE != dwDataType) WARNING(DLLTEXT("DetermineFeatureDisplayName() feature attribute type not kADT_UNICODE. (dwDataType = %d)\r\n"), dwDataType);
goto Exit;
}
// INVARIANT: Did not get the display name from the Helper.
// Free memory allocated for call to Helper.
if(NULL != *ppszDisplayName)
{
HeapFree(hHeap, 0, *ppszDisplayName);
*ppszDisplayName = NULL;
}
// Try alternative methods for getting the display name other
// than from the Helper function.
// If we have a mapping entry, then try to get resource string
// for the display name.
// Otherwise, covert the keyword to UNICODE and use that.
if(NULL != pMapping)
{
//
// Try mapping the keyword to resource string.
//
hrResult = GetDisplayNameFromMapping(hHeap, pMapping, ppszDisplayName);
}
else
{
//
// Convert the keyword to UNICODE and use that.
//
// Convert ANSI keyword to Unicode string for display name.
// Need to remove the % for Driver Synthisized features.
// For debug version, add marker that shows that the display name was faked.
PCSTR pConvert = IS_DRIVER_FEATURE(pszKeyword) ? pszKeyword + 1 : pszKeyword;
#if DBG
CHAR szTemp[256];
if(FAILED(StringCbPrintfA(szTemp, sizeof(szTemp), "%s (Keyword)", pConvert)))
{
ERR(ERRORTEXT("DetermineFeatureDisplayName() StringCbPrintfA() called failed.\r\n"));
}
pConvert = szTemp;
#endif
*ppszDisplayName = MakeUnicodeString(hHeap, pConvert);
if(NULL == *ppszDisplayName)
{
ERR(ERRORTEXT("DetermineFeatureDisplayName() alloc for feature display name failed.\r\n"));
hrResult = E_OUTOFMEMORY;
goto Exit;
}
// Return success even though we faked a display name.
hrResult = S_OK;
}
Exit:
// If failed, then return no string.
if(!SUCCEEDED(hrResult))
{
if(NULL != *ppszDisplayName)
{
HeapFree(hHeap, 0, *ppszDisplayName);
*ppszDisplayName = NULL;
}
}
return hrResult;
}
// Maps option keywords to display names for the option.
HRESULT DetermineOptionDisplayName(HANDLE hHeap,
CUIHelper &Helper,
POEMUIOBJ poemuiobj,
PCSTR pszFeature,
PCSTR pszOption,
const PKEYWORDMAP pMapping,
PWSTR *ppszDisplayName)
{
DWORD dwDataType = -1;
DWORD dwSize = INITIAL_OPTION_DISPLAY_NAME_SIZE;
DWORD dwNeeded = 0;
HRESULT hrResult = S_OK;
// Validate parameters.
if( (NULL == hHeap)
||
(NULL == pszFeature)
||
(NULL == pszOption)
||
(NULL == ppszDisplayName)
)
{
ERR(ERRORTEXT("DetermineOptionDisplayName() invalid arguement.\r\n"));
hrResult = E_INVALIDARG;
goto Exit;
}
//
// Call the Helper function.
//
// Helper will return Display Names for PPD Feature Options, but
// not for Driver Synthisized features options (i.e. features prefixed with %).
// Do it for all options, just in case the helper interface changes to support it.
// Pre-allocate a buffer of reasonable size to try
// to one have to call GetOptionAttribute() once.
*ppszDisplayName = (PWSTR) HeapAlloc(hHeap, HEAP_ZERO_MEMORY, dwSize);
if(NULL == *ppszDisplayName)
{
ERR(ERRORTEXT("DetermineOptionDisplayName() alloc for feature display name failed.\r\n"));
hrResult = E_OUTOFMEMORY;
goto Exit;
}
// Try to get diplay name for feature from Helper.
hrResult = Helper.GetOptionAttribute(poemuiobj,
0,
pszFeature,
pszOption,
"DisplayName",
&dwDataType,
(PBYTE) *ppszDisplayName,
dwSize,
&dwNeeded);
if( (E_OUTOFMEMORY == hrResult) && (dwSize < dwNeeded))
{
PWSTR pTemp;
// INVARIANT: initial buffer wasn't large enough.
// Re-alloc buffer and try again.
pTemp = (PWSTR) HeapReAlloc(hHeap, HEAP_ZERO_MEMORY, *ppszDisplayName, dwNeeded);
if(NULL == pTemp)
{
ERR(ERRORTEXT("GetOptionAttribute() re-alloc for feature display name failed.\r\n"));
hrResult = E_OUTOFMEMORY;
goto Exit;
}
*ppszDisplayName = pTemp;
// Try to get the display name from Helper, again.
hrResult = Helper.GetOptionAttribute(poemuiobj,
0,
pszFeature,
pszOption,
"DisplayName",
&dwDataType,
(PBYTE) *ppszDisplayName,
dwNeeded,
&dwNeeded);
}
if(SUCCEEDED(hrResult))
{
// INVARIANT: Successfully got display name from Helper for feature.
// Don't need to do anything more.
// Check the data type, it should be kADT_UNICODE.
if(kADT_UNICODE != dwDataType) WARNING(DLLTEXT("DetermineOptionDisplayName() feature attribute type not kADT_UNICODE. (dwDataType = %d)\r\n"), dwDataType);
goto Exit;
}
// INVARIANT: Did not get the display name from the Helper.
// Free memory allocated for call to Helper.
if(NULL != *ppszDisplayName)
{
HeapFree(hHeap, 0, *ppszDisplayName);
*ppszDisplayName = NULL;
}
// Try alternative methods for getting the display name other
// than from the Helper function.
// If we have a mapping entry, then try to get resource string
// for the display name.
// Otherwise, covert the keyword to UNICODE and use that.
if(NULL != pMapping)
{
//
// Try mapping the keyword to resource string.
//
hrResult = GetDisplayNameFromMapping(hHeap, pMapping, ppszDisplayName);
}
else
{
//
// Convert the keyword to UNICODE and use that.
//
// Convert ANSI keyword to Unicode string for display name.
// For debug version, add marker that shows that the display name was faked.
PCSTR pConvert = pszOption;
#if DBG
CHAR szTemp[256];
if(FAILED(StringCbPrintfA(szTemp, sizeof(szTemp), "%s (Keyword)", pConvert)))
{
ERR(ERRORTEXT("DetermineOptionDisplayName() StringCbPrintfA() called failed.\r\n"));
}
pConvert = szTemp;
#endif
*ppszDisplayName = MakeUnicodeString(hHeap, pConvert);
if(NULL == *ppszDisplayName)
{
ERR(ERRORTEXT("DetermineOptionDisplayName() alloc for feature display name failed.\r\n"));
hrResult = E_OUTOFMEMORY;
goto Exit;
}
// Return success even though we faked a display name.
hrResult = S_OK;
}
Exit:
// If failed, then return no string.
if(!SUCCEEDED(hrResult))
{
if(NULL != *ppszDisplayName)
{
HeapFree(hHeap, 0, *ppszDisplayName);
*ppszDisplayName = NULL;
}
}
return hrResult;
}
// Determines sticky mode for the feature.
HRESULT DetermineStickiness(CUIHelper &Helper,
POEMUIOBJ poemuiobj,
PCSTR pszKeyword,
const PKEYWORDMAP pMapping,
PDWORD pdwMode)
{
CHAR szGroupType[32] = {0};
DWORD dwType = 0;
DWORD dwNeeded = 0;
HRESULT hrResult = S_OK;
// Use mapping to see what stickiness of the feature is.
if(NULL != pMapping)
{
*pdwMode = pMapping->dwMode;
goto Exit;
}
// By default make feature Document sticky, if we don't have mapping.
*pdwMode = OEMCUIP_DOCPROP;
// Try to use Helper to determine stickiness.
hrResult = Helper.GetFeatureAttribute(poemuiobj,
0,
pszKeyword,
"OpenGroupType",
&dwType,
(PBYTE) szGroupType,
sizeof(szGroupType),
&dwNeeded);
if(SUCCEEDED(hrResult))
{
// INVARIANT: found out if feature is an installable option.
// Installable options are the only PPD features
// that are Printer sticky.
if(!lstrcmpA(szGroupType, "InstallableOptions"))
{
*pdwMode = OEMCUIP_PRNPROP;
}
goto Exit;
}
Exit:
return hrResult;
}
// Find the mapping entry from the keyword.
PKEYWORDMAP FindKeywordMapping(PKEYWORDMAP pKeywordMap, WORD wMapSize, PCSTR pszKeyword)
{
BOOL bFound = FALSE;
PKEYWORDMAP pMapping = NULL;
// Walk mapping array for matching keyword.
for(WORD wIndex = 0; !bFound && (wIndex < wMapSize); ++wIndex)
{
bFound = !lstrcmpA(pszKeyword, pKeywordMap[wIndex].pszKeyword);
if(bFound)
{
pMapping = pKeywordMap + wIndex;
}
}
return pMapping;
}
// Get display name from mapping entry.
HRESULT GetDisplayNameFromMapping(HANDLE hHeap, PKEYWORDMAP pMapping, PWSTR *ppszDisplayName)
{
HMODULE hModule = NULL;
HRESULT hrResult = S_OK;
// Validate parameters.
if( (NULL == hHeap)
||
(NULL == pMapping)
||
(NULL == ppszDisplayName)
)
{
hrResult = E_INVALIDARG;
goto Exit;
}
// Check for simple case of returning INT resource.
if( (NULL == pMapping->pwszModule)
||
IS_MAPPING_INT_RESOURCE(pMapping)
)
{
// Just need to do MAKEINTRESOURCE on the resource ID and return.
*ppszDisplayName = MAKEINTRESOURCE(pMapping->uDisplayNameID);
goto Exit;
}
// We only need to get the module if we aren't loading the resource from
// this module (i.e. if module name isn't NULL).
// Also, as an optimization, we assume that the module has already been loaded,
// since the only cases currently are this module, driver ui, and Compstui.dll.
hModule = GetModuleHandle(pMapping->pwszModule);
if(NULL == hModule)
{
DWORD dwError = GetLastError();
ERR(ERRORTEXT("GetDisplayNameFromMapping() for failed to load module %s. (Error %d)\r\n"),
pMapping->pwszModule,
dwError);
hrResult = HRESULT_FROM_WIN32(dwError);
goto Exit;
}
// INVARIANT: have handle to module to load resource from or the
// resource is being loaded from this module.
// Get the string resouce.
hrResult = GetStringResource(hHeap, hModule, pMapping->uDisplayNameID, ppszDisplayName);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("GetDisplayNameFromMapping() failed to load string. (hrResult = 0x%x)\r\n"),
hrResult);
goto Exit;
}
Exit:
return hrResult;
}
// Test if an OPTITEM is an OPTITEM for a feature.
// Macro for testing if OPTITEM is feature OPTITEM
BOOL IsFeatureOptitem(POPTITEM pOptItem)
{
BOOL bRet = FALSE;
PFEATUREOPTITEMDATA pData = NULL;
// Make sure pointers are NULL.
if( (NULL == pOptItem)
||
(NULL == pOptItem->UserData)
)
{
// INVARIANT: can't be feature OPTITEM, since one of
// the necessary pointer are NULL.
return FALSE;
}
// For convienience, assign to pointer to feature OPTITEM data.
pData = (PFEATUREOPTITEMDATA)(pOptItem->UserData);
// Check size and tag.
bRet = (sizeof(FEATUREOPTITEMDATA) == pData->dwSize)
&&
(FEATURE_OPTITEM_TAG == pData->dwTag);
return bRet;
}
// Walks array of OPTITEMs saving each feature OPTITEM
// that has changed.
HRESULT SaveFeatureOptItems(HANDLE hHeap,
CUIHelper *pHelper,
POEMUIOBJ poemuiobj,
HWND hWnd,
POPTITEM pOptItem,
WORD wItems)
{
PSTR pmszPairs = NULL;
WORD wPairs = 0;
WORD wConflicts = 0;
WORD wReasons = 0;
DWORD dwSize = 0;
DWORD dwResult = 0;
PCSTR *ppszReasons = NULL;
PWSTR pszConfilictFeature = NULL;
PWSTR pszConfilictOption = NULL;
PWSTR pszCaption = NULL;
PWSTR pszFormat = NULL;
PWSTR pszMessage = NULL;
HRESULT hrResult = S_OK;
// Validate parameters
if( (NULL == hHeap)
||
(NULL == pHelper)
||
(NULL == pOptItem)
)
{
ERR(ERRORTEXT("SaveFeatureOptItems() called with invalid parameters.\r\n"));
hrResult = E_INVALIDARG;
goto Exit;
}
// Get feature option pairs to save.
hrResult = GetChangedFeatureOptions(hHeap, pOptItem, wItems, &pmszPairs, &wPairs, &dwSize);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("SaveFeatureOptItems() failed to get changed feature option pairs. (hrResult = 0x%x)\r\n"),
hrResult);
goto Exit;
}
// Don't need to do anything more if no feature options changed.
if(0 == wPairs)
{
VERBOSE(DLLTEXT("SaveFeatureOptItems() no feature options that need to be set.\r\n"));
goto Exit;
}
// Set the change feature options.
// For the first SetOptions() call, don't have the
// core driver UI resolve conflicts, so we can
// prompt user for automatic resolution or let
// them do the conflict resolving.
hrResult = pHelper->SetOptions(poemuiobj,
SETOPTIONS_FLAG_KEEP_CONFLICT,
pmszPairs,
dwSize,
&dwResult);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("SaveFeatureOptItems() call to SetOptions() failed. (hrResult = 0x%x, dwResult = %d\r\n"),
hrResult,
dwResult);
goto Exit;
}
// Check to see if we were able to save changed feature options,
// or if there is a conflict that needs resolution.
if(SETOPTIONS_RESULT_CONFLICT_REMAINED == dwResult)
{
int nRet;
DWORD dwRet;
CONFLICT Conflict;
PKEYWORDMAP pMapping = NULL;
// INVARIANT: constraint conflict, options weren't saved.
// Get list of all features that have conflict.
hrResult = GetFirstConflictingFeature(hHeap,
pHelper,
poemuiobj,
pOptItem,
wItems,
&Conflict);
if(!SUCCEEDED(hrResult))
{
goto Exit;
}
// Create string pointer list in to multi-sz of first conflict.
hrResult = MakeStrPtrList(hHeap, Conflict.pmszReasons, &ppszReasons, &wReasons);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("SaveFeatureOptItems() failed to make string list for conflict reasons. (hrResult = 0x%x)\r\n"),
hrResult);
goto Exit;
}
//
// Get display versions of feature/option conflict reason.
//
// Get or build a keyword mapping entry
// that maps from keyword to usefully where to get info, such as
// display name, icon, option type, for keywords that may not be
// able to get info for from Helper.
pMapping = FindKeywordMapping(gkmFeatureMap, NUM_FEATURE_MAP, ppszReasons[0]);
// Get display names for each of the featurs.
// The function implements a heuristic for detemining the display name,
// since can't get the display name from the UI Helper for all features.
hrResult = DetermineFeatureDisplayName(hHeap,
*pHelper,
poemuiobj,
ppszReasons[0],
pMapping,
&pszConfilictFeature);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("SaveFeatureOptItems failed to get display name for feature %hs. (hrResult = 0x%x)\r\n"),
ppszReasons[0],
hrResult);
goto Exit;
}
// Get or build a keyword mapping entry
// that maps from keyword to usefully where to get info, such as
// display name, icon, option type, for keywords that may not be
// able to get info for from Helper.
pMapping = FindKeywordMapping(gkmOptionMap, NUM_OPTION_MAP, ppszReasons[1]);
// Get option display name.
hrResult = DetermineOptionDisplayName(hHeap,
*pHelper,
poemuiobj,
ppszReasons[0],
ppszReasons[1],
pMapping,
&pszConfilictOption);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("SaveFeatureOptItems() failed to get display name for %hs of feature %hs. (hrResult = 0x%x)\r\n"),
ppszReasons[1],
ppszReasons[0],
hrResult);
goto Exit;
}
//
// Prompt user about how to resolve conflict.
//
// Get caption name.
hrResult = GetStringResource(hHeap, ghInstance, IDS_NAME, &pszCaption);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("SaveFeatureOptItems() failed to get caption name. (hrResult = 0x%x)\r\n"),
hrResult);
goto Exit;
}
// Get message body format string.
hrResult = GetStringResource(hHeap, ghInstance, IDS_CONSTRAINT_CONFLICT, &pszFormat);
if(!SUCCEEDED(hrResult))
{
ERR(ERRORTEXT("SaveFeatureOptItems() failed to get constraint conflict format. (hrResult = 0x%x)\r\n"),
hrResult);
goto Exit;
}
// Get messsage body.
PVOID paArgs[4] = {pszConfilictFeature,
pszConfilictOption,
const_cast<PWSTR>(Conflict.pszFeature),
Conflict.pszOption
};
dwRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
pszFormat,
0,
0,
(PWSTR) &pszMessage,
0,
(va_list *) paArgs);
if(0 == dwRet)
{
DWORD dwError = GetLastError();
ERR(ERRORTEXT("SaveFeatureOptItems() failed to FormatMessage() for constraint conflict of feature %hs option %hs. (Last Error %d)\r\n"),
Conflict.pszFeatureKeyword,
Conflict.pszOptionKeyword,
dwError);
hrResult = HRESULT_FROM_WIN32(dwError);
goto Exit;
}
// Display simple message box with prompt.
nRet = MessageBox(hWnd, pszMessage, pszCaption, MB_YESNO | MB_ICONWARNING);
// Check to see how user wants to resolve conflict.
if(IDYES == nRet)
{
// Let core driver resolve conflict resolution.
hrResult = pHelper->SetOptions(poemuiobj,
SETOPTIONS_FLAG_RESOLVE_CONFLICT,
pmszPairs,
dwSize,
&dwResult);
// Conflict resolution requires refreshing current option
// selection for each feature, since selection may have
// changed because of conflict resolution.
RefreshOptItemSelection(pHelper, poemuiobj, pOptItem, wItems);
}
// Return failure if there are still conflictts.
if(SETOPTIONS_RESULT_CONFLICT_REMAINED == dwResult)
{
hrResult = E_FAIL;
}
}
Exit:
// Clean up...
// cleanup heap allocs.
if(NULL != pmszPairs) HeapFree(hHeap, 0, pmszPairs);
if(NULL != ppszReasons) HeapFree(hHeap, 0, ppszReasons);
if(NULL != pszConfilictFeature) HeapFree(hHeap, 0, pszConfilictFeature);
if(NULL != pszConfilictOption) HeapFree(hHeap, 0, pszConfilictOption);
if(NULL != pszCaption) HeapFree(hHeap, 0, pszCaption);
if(NULL != pszFormat) HeapFree(hHeap, 0, pszFormat);
if(NULL != pszMessage) LocalFree(pszMessage);
return hrResult;
}
// Allocates buffer, if needed, and calls IPrintCoreUI2::WhyConsrained
// to get reason for constraint.
HRESULT GetWhyConstrained(HANDLE hHeap,
CUIHelper *pHelper,
POEMUIOBJ poemuiobj,
PCSTR pszFeature,
PCSTR pszOption,
PSTR *ppmszReason,
PDWORD pdwSize)
{
PSTR pmszReasonList = *ppmszReason;
DWORD dwNeeded = 0;
HRESULT hrResult = S_OK;
// If buffer wasn't passed in, then allocate one.
if( (NULL == pmszReasonList) || (0 == *pdwSize) )
{
// If no size or size is smaller than default, then change to default
// size. We want to try to only allocate and call once.
if(*pdwSize < INITIAL_GET_REASON_SIZE)
{
*pdwSize = INITIAL_GET_REASON_SIZE;
}
// Alloc initial buffer for reason constrained.
pmszReasonList = (PSTR) HeapAlloc(hHeap, HEAP_ZERO_MEMORY, *pdwSize);
if(NULL == pmszReasonList)
{
ERR(ERRORTEXT("GetWhyConstrained() failed to alloc buffer for constraint reasons for feature %hs and option %hs.\r\n"),
pszFeature,
pszOption);
hrResult = E_OUTOFMEMORY;
goto Exit;
}
}
// Get reason for constraint.
hrResult = pHelper->WhyConstrained(poemuiobj,
0,
pszFeature,
pszOption,
pmszReasonList,
*pdwSize,
&dwNeeded);
if( (E_OUTOFMEMORY == hrResult)
&&
(*pdwSize < dwNeeded)
)
{
PSTR pTemp;
// INVARIANT: initial buffer not large enough.
// Re-alloc buffer to needed size.
pTemp = (PSTR) HeapReAlloc(hHeap, HEAP_ZERO_MEMORY, pmszReasonList, dwNeeded);
if(NULL == pTemp)
{
ERR(ERRORTEXT("GetWhyConstrained() failed to re-allocate buffer for constraint reason for feature %hs and option %hs.\r\n"),
pszFeature,
pszOption);
hrResult = E_OUTOFMEMORY;
goto Exit;
}
pmszReasonList = pTemp;
*pdwSize = dwNeeded;
// Retry getting constaint reason.
hrResult = pHelper->WhyConstrained(poemuiobj,
0,
pszFeature,
pszOption,
pmszReasonList,
*pdwSize,
&dwNeeded);
}
Exit:
// On error, do clean up.
if(!SUCCEEDED(hrResult))
{
// Free reason buffer.
if(NULL != pmszReasonList) HeapFree(hHeap, 0, pmszReasonList);
// Return NULL and zero size.
*ppmszReason = NULL;
*pdwSize = 0;
}
else
{
*ppmszReason = pmszReasonList;
}
return hrResult;
}
// Creates multi-sz list of feature option pairs that have changed.
HRESULT GetChangedFeatureOptions(HANDLE hHeap,
POPTITEM pOptItem,
WORD wItems,
PSTR *ppmszPairs,
PWORD pwPairs,
PDWORD pdwSize)
{
WORD wCount = 0;
WORD wChanged = 0;
WORD wPairs = 0;
PSTR pmszPairs = NULL;
DWORD dwNeeded = 2;
DWORD dwOffset = 0;
HRESULT hrResult = S_OK;
PFEATUREOPTITEMDATA pData = NULL;
// Walk OPTITEM array looking or changed options,
// and calculating size needed for multi-sz buffer.
for(wCount = 0; wCount < wItems; ++wCount)
{
PSTR pszOption = NULL;
// Just go to next item if this OPTITEM hasn't
// changed or isn't a feature OPTITEM.
if( !(OPTIF_CHANGEONCE & pOptItem[wCount].Flags)
||
!IsFeatureOptitem(pOptItem + wCount)
)
{
continue;
}
// For convienience, assign to pointer to feature OPTITEM data.
pData = (PFEATUREOPTITEMDATA)(pOptItem[wCount].UserData);
// Increment options changed and size needed.
pszOption = GetOptionKeywordFromOptItem(hHeap, pOptItem + wCount);
if(NULL != pszOption)
{
++wChanged;
dwNeeded += lstrlenA(pData->pszFeatureKeyword) + lstrlenA(pszOption) + 2;
// Need to free option keyword string copy
// allocated in GetOptionKeywordFromOptItem().
HeapFree(hHeap, 0, pszOption);
}
}
// Don't need to do anything more if no feature options changed.
if(0 == wChanged)
{
goto Exit;
}
// Allocate multi-sz buffer.
pmszPairs = (PSTR) HeapAlloc(hHeap, HEAP_ZERO_MEMORY, dwNeeded);
if(NULL == pmszPairs)
{
ERR(ERRORTEXT("GetChangedFeatureOptions() failed to allocate multi-sz.\r\n"));
hrResult = E_OUTOFMEMORY;
goto Exit;
}
// Build mult-sz list of feature option pairs
// that changed.
for(wCount = 0, wPairs = 0; (wCount < wItems) && (wPairs < wChanged); ++wCount)
{
PSTR pszOption = NULL;
// Just go to next item if this OPTITEM hasn't
// changed or isn't a feature OPTITEM.
if( !(OPTIF_CHANGEONCE & pOptItem[wCount].Flags)
||
!IsFeatureOptitem(pOptItem + wCount)
)
{
continue;
}
// For convienience, assign to pointer to feature OPTITEM data.
pData = (PFEATUREOPTITEMDATA)(pOptItem[wCount].UserData);
// Add feature option pair.
pszOption = GetOptionKeywordFromOptItem(hHeap, pOptItem + wCount);
if(NULL != pszOption)
{
if(dwOffset * sizeof(CHAR) < dwNeeded)
{
break;
}
hrResult = StringCbCopyA(pmszPairs + dwOffset, dwNeeded - (dwOffset * sizeof(CHAR)), pData->pszFeatureKeyword);
dwOffset += lstrlenA(pData->pszFeatureKeyword) + 1;
if(dwOffset * sizeof(CHAR) < dwNeeded)
{
break;
}
hrResult = StringCbCopyA(pmszPairs + dwOffset, dwNeeded - (dwOffset * sizeof(CHAR)), pszOption);
dwOffset += lstrlenA(pszOption) + 1;
// Keep track of number of pairs added, so
// we are able to exit loop as soon as
// we added all changed feature options.
++wPairs;
// Need to free option keyword string copy
// allocated in GetOptionKeywordFromOptItem().
HeapFree(hHeap, 0, pszOption);
}
}
Exit:
if(SUCCEEDED(hrResult))
{
// INVARIANT: either successfully build mutli-sz of changed
// feature option pairs, or there are no feature
// that options changed.
// Return pairs and number of pairs.
*pwPairs = wPairs;
*pdwSize = dwNeeded;
*ppmszPairs = pmszPairs;
}
else
{
// INVARINAT: error.
// Clean up.
if(NULL == pmszPairs) HeapFree(hHeap, 0, pmszPairs);
// Return NULL and zero count.
*pwPairs = 0;
*pdwSize = 0;
*ppmszPairs = NULL;
}
return hrResult;
}
// Returns pointer to option keyword for a feature OPTITEM.
PSTR GetOptionKeywordFromOptItem(HANDLE hHeap, POPTITEM pOptItem)
{
char szNumber[16] = {0};
PSTR pszOption = NULL;
PFEATUREOPTITEMDATA pData = NULL;
// Validate parameter.
if(!IsFeatureOptitem(pOptItem))
{
ERR(ERRORTEXT("GetOptionKeywordFromOptItem() invalid parameter.\r\n"));
goto Exit;
}
// For convienience, assign to pointer to feature OPTITEM data.
pData = (PFEATUREOPTITEMDATA)(pOptItem->UserData);
// Option selection is based on type of OPTTYPE.
switch(pOptItem->pOptType->Type)
{
// For up down arrow control, selection is just pOptItem->Sel
// converted to string.
case TVOT_UDARROW:
if(FAILED(StringCbPrintfA(szNumber, sizeof(szNumber)/sizeof(szNumber[0]), "%u", pOptItem->Sel)))
{
ERR(ERRORTEXT("GetOptionKeywordFromOptItem() failed to convert number to string.\r\n"));
}
szNumber[sizeof(szNumber)/sizeof(szNumber[0]) - 1] = '\0';
pszOption = MakeStringCopy(hHeap, szNumber);
break;
// For combobox, pOptItem->Sel is the index in to the
// option array.
case TVOT_COMBOBOX:
pszOption = MakeStringCopy(hHeap, pData->pOptions->GetKeyword((WORD)pOptItem->Sel));
break;
// The default is the option count.
default:
ERR(ERRORTEXT("GetOptionKeywordFromOptItem() OPTTYPE type %d num of OPTPARAMs not handled.\r\n"),
pOptItem->pOptType->Type);
goto Exit;
break;
}
Exit:
return pszOption;
}
// Returns pointer to option display name for a feature OPTITEM.
PWSTR GetOptionDisplayNameFromOptItem(HANDLE hHeap, POPTITEM pOptItem)
{
WCHAR szNumber[16] = {0};
PWSTR pszOption = NULL;
PFEATUREOPTITEMDATA pData = NULL;
// Validate parameter.
if(!IsFeatureOptitem(pOptItem))
{
ERR(ERRORTEXT("GetOptionDisplayNameFromOptItem() invalid parameter.\r\n"));
goto Exit;
}
// For convienience, assign to pointer to feature OPTITEM data.
pData = (PFEATUREOPTITEMDATA)(pOptItem->UserData);
// Option selection is based on type of OPTTYPE.
switch(pOptItem->pOptType->Type)
{
// For up down arrow control, selection is just pOptItem->Sel
// converted to string.
case TVOT_UDARROW:
if(FAILED(StringCbPrintfW(szNumber, sizeof(szNumber)/sizeof(szNumber[0]), L"%u", pOptItem->Sel)))
{
ERR(ERRORTEXT("GetOptionDisplayNameFromOptItem() failed to convert number to string.\r\n"));
}
szNumber[sizeof(szNumber)/sizeof(szNumber[0]) - 1] = L'\0';
pszOption = MakeStringCopy(hHeap, szNumber);
break;
// For combobox, pOptItem->Sel is the index in to the
// option array.
case TVOT_COMBOBOX:
pszOption = MakeStringCopy(hHeap, pOptItem->pOptType->pOptParam[pOptItem->Sel].pData);
break;
// The default is the option count.
default:
ERR(ERRORTEXT("GetOptionDisplayNameFromOptItem() OPTTYPE type %d num of OPTPARAMs not handled.\r\n"),
pOptItem->pOptType->Type);
goto Exit;
break;
}
Exit:
return pszOption;
}
// Refreshes option selection for each feature OPTITEM
HRESULT RefreshOptItemSelection(CUIHelper *pHelper,
POEMUIOBJ poemuiobj,
POPTITEM pOptItems,
WORD wItems)
{
HRESULT hrResult = S_OK;
PFEATUREOPTITEMDATA pData = NULL;
// Walk OPTITEM array refreshing feature OPTITEMs.
for(WORD wCount = 0; wCount < wItems; ++wCount)
{
// Just go to next item if this OPTITEM isn't a feature OPTITEM.
if(!IsFeatureOptitem(pOptItems + wCount))
{
continue;
}
// For convienience, assign to pointer to feature OPTITEM data.
pData = (PFEATUREOPTITEMDATA)(pOptItems[wCount].UserData);
// Refresh COption selection.
pData->pOptions->RefreshSelection(*pHelper, poemuiobj);
// Assign COption selection to OPTITEM selection.
pOptItems[wCount].pSel = pData->pOptions->GetSelection();
}
return hrResult;
}
// Creates array of conflict features.
HRESULT GetFirstConflictingFeature(HANDLE hHeap,
CUIHelper *pHelper,
POEMUIOBJ poemuiobj,
POPTITEM pOptItem,
WORD wItems,
PCONFLICT pConflict)
{
WORD wCount = 0;
HRESULT hrResult = S_OK;
PFEATUREOPTITEMDATA pData = NULL;
// Walk OPTITEM array looking or changed options that are in conflict.
for(wCount = 0; wCount < wItems; ++wCount)
{
// Just go to next item if this OPTITEM hasn't
// changed or isn't a feature OPTITEM.
if( !(OPTIF_CHANGEONCE & pOptItem[wCount].Flags)
||
!IsFeatureOptitem(pOptItem + wCount)
)
{
continue;
}
// For convienience, assign to pointer to feature OPTITEM data.
pData = (PFEATUREOPTITEMDATA)(pOptItem[wCount].UserData);
// Init conflict record if this feature is in conflict.
pConflict->pszOptionKeyword = GetOptionKeywordFromOptItem(hHeap, pOptItem + wCount);
if(NULL != pConflict->pszOptionKeyword)
{
// Get reason for conflict
// If the feature isn't in conflict,
// then the pmszReasonList will start
// with NULL terminator.
hrResult = GetWhyConstrained(hHeap,
pHelper,
poemuiobj,
pData->pszFeatureKeyword,
pConflict->pszOptionKeyword,
&pConflict->pmszReasons,
&pConflict->dwReasonsSize);
if(!SUCCEEDED(hrResult))
{
// NOTE: driver features aren't supported by WhyConstrained.
if((E_INVALIDARG == hrResult) && IS_DRIVER_FEATURE(pData->pszFeatureKeyword))
{
// Need to reset the result in case it is the last
// feature OPTITEM.
hrResult = S_OK;
continue;
}
ERR(ERRORTEXT("GetConflictingFeatures() failed to get reason for feature %hs option %hs constraint. (hrResult = 0x%x)\r\n"),
pData->pszFeatureKeyword,
pConflict->pszOptionKeyword,
hrResult);
goto Exit;
}
// Record conflict if feature is in conflict.
if( (NULL != pConflict->pmszReasons)
&&
(pConflict->pmszReasons[0] != '\0')
)
{
// Save pointer to feature keyword.
pConflict->pszFeatureKeyword = pData->pszFeatureKeyword;
pConflict->pszFeature = pOptItem[wCount].pName;
pConflict->pszOption = GetOptionDisplayNameFromOptItem(hHeap, pOptItem + wCount);
// Found first conflict.
break;
}
}
}
Exit:
return hrResult;
}