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