88using namespace std ;
99using namespace std ::placeholders;
1010
11+ const std::wstring Formatter::empty_wstring;
12+
1113Formatter::Formatter ()
1214{
1315 m_properties =
@@ -17,6 +19,8 @@ Formatter::Formatter()
1719 { L" installationName" , bind (&Formatter::GetInstallationName, this , _1, _2) },
1820 { L" installationPath" , bind (&Formatter::GetInstallationPath, this , _1, _2) },
1921 { L" installationVersion" , bind (&Formatter::GetInstallationVersion, this , _1, _2) },
22+ { L" productId" , bind (&Formatter::GetProductId, this , _1, _2) },
23+ { L" productPath" , bind (&Formatter::GetProductPath, this , _1, _2) },
2024 { L" isPrerelease" , bind (&Formatter::GetIsPrerelease, this , _1, _2) },
2125 { L" displayName" , bind (&Formatter::GetDisplayName, this , _1, _2) },
2226 { L" description" , bind (&Formatter::GetDescription, this , _1, _2) },
@@ -31,6 +35,7 @@ Formatter::FormatterMap Formatter::Formatters =
3135 { L" xml" , make_tuple (IDS_FORMAT_XML, XmlFormatter::Create) },
3236};
3337
38+ const wstring Formatter::s_delims (L" ./_" );
3439ci_equal Formatter::s_comparer;
3540
3641std::unique_ptr<Formatter> Formatter::Create (const std::wstring& type)
@@ -111,6 +116,7 @@ wstring Formatter::FormatDate(_In_ const FILETIME& value)
111116 throw win32_error ();
112117 }
113118
119+ date.reserve (cch);
114120 date.resize (cch - 1 );
115121 cch = ::GetDateFormatW (LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL , const_cast <LPWSTR>(date.c_str ()), cch);
116122 if (!cch)
@@ -125,7 +131,8 @@ wstring Formatter::FormatDate(_In_ const FILETIME& value)
125131 throw win32_error ();
126132 }
127133
128- time.reserve (cch - 1 );
134+ time.reserve (cch);
135+ time.resize (cch - 1 );
129136 cch = ::GetTimeFormatW (LOCALE_USER_DEFAULT, 0 , &st, NULL , const_cast <LPWSTR>(time.c_str ()), cch);
130137 if (!cch)
131138 {
@@ -161,7 +168,28 @@ void Formatter::WriteInternal(_In_ const CommandArgs& args, _In_ Console& consol
161168
162169 if (specified.empty () || !found)
163170 {
164- WriteProperties (args, console, pInstance);
171+ found = WriteProperties (args, console, pInstance);
172+ if (specified.empty () || !found)
173+ {
174+ ISetupInstance2Ptr instance2;
175+
176+ auto hr = pInstance->QueryInterface (&instance2);
177+ if (SUCCEEDED (hr))
178+ {
179+ ISetupPropertyStorePtr store;
180+
181+ hr = instance2->GetProperties (&store);
182+ if (SUCCEEDED (hr) && !!store)
183+ {
184+ wstring name (L" properties" );
185+ StartObject (console, name);
186+
187+ WriteProperties (args, console, store, name);
188+
189+ EndObject (console);
190+ }
191+ }
192+ }
165193 }
166194
167195 EndObject (console);
@@ -191,12 +219,11 @@ void Formatter::WriteProperty(_In_ Console& console, _In_ const wstring& name, _
191219 }
192220}
193221
194- void Formatter::WriteProperties (_In_ const CommandArgs& args, _In_ Console& console, _In_ ISetupInstance* pInstance)
222+ bool Formatter::WriteProperties (_In_ const CommandArgs& args, _In_ Console& console, _In_ ISetupInstance* pInstance)
195223{
196224 _ASSERTE (pInstance);
197225
198226 ISetupPropertyStorePtr store;
199- LPSAFEARRAY psaNames = NULL ;
200227
201228 auto hr = pInstance->QueryInterface (&store);
202229 if (FAILED (hr))
@@ -206,36 +233,67 @@ void Formatter::WriteProperties(_In_ const CommandArgs& args, _In_ Console& cons
206233 throw win32_error (hr);
207234 }
208235
209- return ;
236+ return false ;
210237 }
211238
212- hr = store->GetNames (&psaNames);
239+ return WriteProperties (args, console, store);
240+ }
241+
242+ bool Formatter::WriteProperties (_In_ const CommandArgs& args, _In_ Console& console, _In_ ISetupPropertyStore* pProperties, _In_opt_ const wstring& prefix)
243+ {
244+ _ASSERTE (pProperties);
245+
246+ LPSAFEARRAY psaNames = NULL ;
247+ auto found = false ;
248+
249+ auto hr = pProperties->GetNames (&psaNames);
213250 if (FAILED (hr))
214251 {
215- return ;
252+ return false ;
216253 }
217254
218- const auto & specified = args.get_Property ();
219- SafeArray<BSTR> saNames (psaNames);
255+ // Trim optional nested object name from specified property if matching current scope.
256+ wstring specified = args.get_Property ();
257+ if (prefix.size () > 0 )
258+ {
259+ auto pos = specified.find_first_of (s_delims);
260+ if (pos != wstring::npos && (pos + 1 ) < specified.size () && s_comparer (prefix, specified.substr (0 , pos)))
261+ {
262+ specified = specified.substr (pos + 1 );
263+ }
264+ else if (s_comparer (prefix, specified))
265+ {
266+ // If the current nested object name is specified, clear the prefix to show the whole nested object.
267+ specified.clear ();
268+ }
269+ }
220270
271+ SafeArray<BSTR> saNames (psaNames);
221272 for (const auto & bstrName : saNames.Elements ())
222273 {
223274 wstring name (bstrName);
224- if (specified.empty () || s_comparer (name, specified))
275+ if (specified.empty () || (found = s_comparer (name, specified) ))
225276 {
226277 variant_t vtValue;
227278
228279 auto it = find_if (m_properties.begin (), m_properties.end (), bind (Formatter::PropertyEqual, name, _1));
229280 if (it == m_properties.end ())
230281 {
231- hr = store ->GetValue (bstrName, vtValue.GetAddress ());
282+ hr = pProperties ->GetValue (bstrName, vtValue.GetAddress ());
232283 if (SUCCEEDED (hr))
233284 {
234285 WriteProperty (console, name, vtValue);
235286 }
236287 }
288+
289+ if (found)
290+ {
291+ return true ;
292+ }
237293 }
238294 }
295+
296+ return false ;
239297}
240298
241299bool Formatter::PropertyEqual (_In_ const std::wstring& name, _In_ PropertyArray::const_reference property)
@@ -305,6 +363,58 @@ HRESULT Formatter::GetInstallationVersion(_In_ ISetupInstance* pInstance, _Out_
305363 return GetStringProperty (bind (&ISetupInstance::GetInstallationVersion, pInstance, _1), pvtInstallationVersion);
306364}
307365
366+ HRESULT Formatter::GetProductId (_In_ ISetupInstance* pInstance, _Out_ VARIANT* pvtProductId)
367+ {
368+ ISetupInstance2Ptr instance;
369+
370+ auto hr = pInstance->QueryInterface (&instance);
371+ if (SUCCEEDED (hr))
372+ {
373+ ISetupPackageReferencePtr reference;
374+
375+ hr = instance->GetProduct (&reference);
376+ if (SUCCEEDED (hr))
377+ {
378+ variant_t vt;
379+
380+ hr = reference->GetId (&vt.bstrVal );
381+ if (SUCCEEDED (hr))
382+ {
383+ vt.vt = VT_BSTR;
384+ *pvtProductId = vt.Detach ();
385+ }
386+ }
387+ }
388+
389+ return hr;
390+ }
391+
392+ HRESULT Formatter::GetProductPath (_In_ ISetupInstance* pInstance, _Out_ VARIANT* pvtProductPath)
393+ {
394+ ISetupInstance2Ptr instance;
395+
396+ auto hr = pInstance->QueryInterface (&instance);
397+ if (SUCCEEDED (hr))
398+ {
399+ bstr_t bstrProductPath;
400+
401+ hr = instance->GetProductPath (bstrProductPath.GetAddress ());
402+ if (SUCCEEDED (hr))
403+ {
404+ variant_t vt;
405+
406+ hr = instance->ResolvePath (bstrProductPath, &vt.bstrVal );
407+ if (SUCCEEDED (hr))
408+ {
409+ vt.vt = VT_BSTR;
410+ *pvtProductPath = vt.Detach ();
411+ }
412+ }
413+ }
414+
415+ return hr;
416+ }
417+
308418HRESULT Formatter::GetIsPrerelease (_In_ ISetupInstance* pInstance, _Out_ VARIANT* pvtIsPrerelease)
309419{
310420 ISetupInstanceCatalogPtr catalog;
0 commit comments