Thursday, September 13, 2012

IMediaExtension::SetProperties(IPropertySet*) in WinRT/C++

Media foundation transform (MFT) is initialized via
// IMediaExtension
SetProperties(STDMETHODIMP SetProperties(ABI::Windows::Foundation::Collections::IPropertySet *pConfiguration);
To get values from pConfiguration:
1. Convert it to IMap<HSTRING, IInspectable*> interface.
2. The value of each KeyValuePair is also IInspectable. To get the actual value of it - unbox it by converting value to IReference<T>. For strings this approach doesn't work: the documentation states "T must not be a delegate type or an HSTRING". So the IReference<HSTRING> is not possible. The workaround is to convert the value to IPropertyValue<T> and then chat with it to get actual value. Below is a helper method which implements it.

HRESULT SetProperties(ABI::Windows::Foundation::Collections::IPropertySet *pConfiguration)
{
 if (pConfiguration == nullptr)
  return E_POINTER;
 INT32 skipSampleCount;
 if (tryGetValue(*pConfiguration, L"SkipSampleCount", skipSampleCount))
  m_skipSampleCount = skipSampleCount;
 wstring formatStr;
 if (tryGetValue(*pConfiguration, L"Format", formatStr))
  m_format = formatStr;
 return S_OK;
}
template <typename ValueType>
bool tryGetValue(ABI::Windows::Foundation::Collections::IPropertySet& propertySet, const std::wstring& key, ValueType& outValue)
{
 // query for IMap

 Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IMap<HSTRING, IInspectable *>> pMap;
 HRESULT hr = propertySet.QueryInterface(IID_PPV_ARGS(&pMap));
 if (FAILED(hr))
  return false;

 // construct key

 HSTRING_HEADER keyHeader;
 HSTRING keyHstr;
 if (FAILED(WindowsCreateStringReference(key.c_str(), key.size(), &keyHeader, &keyHstr))) 
  return false;
 
 boolean found;
 if (FAILED(pMap->HasKey(keyHstr,&found)) || !found) 
  return false;

 // lookup

 IInspectable *valueAsInsp;
 hr = pMap->Lookup(keyHstr, &valueAsInsp);
 assert(SUCCEEDED(hr));

 //HSTRING inspName;//
 //hr = valueAsInsp->GetRuntimeClassName(&inspName);//
 //UINT32 len;//
 //auto strOut = WindowsGetStringRawBuffer(inspName, &len);//
 //hr = WindowsDeleteString(inspName); //

 return tryGetValue_ExtractValue(valueAsInsp, outValue);
}

template <typename ValueType>
bool tryGetValue_ExtractValue(IInspectable *valueAsInsp, ValueType& outValue)
{
 // cast IInspectable value to IReference

 Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IReference<ValueType>> valueRef;
 HRESULT hr = valueAsInsp->QueryInterface(IID_PPV_ARGS(&valueRef)); // <REFIID, CComPtr>
 if (FAILED(hr))
  return false;

 hr = valueRef->get_Value(&outValue);
 return SUCCEEDED(hr);
}

template <>
bool tryGetValue_ExtractValue(IInspectable *valueAsInsp, IInspectable*& outValue)
{
 outValue = valueAsInsp;
 return true;
}
template <>
bool tryGetValue_ExtractValue(IInspectable *valueAsInsp, wstring& outValue)
{
 // HSTRING case is handled here because IReference<HSTRING> is not possible

 Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IPropertyValue> pPropertyValue;
 HRESULT hr = valueAsInsp->QueryInterface(IID_PPV_ARGS(&pPropertyValue)); // <REFIID, CComPtr>
 if (FAILED(hr))
  return false;

 ABI::Windows::Foundation::PropertyType propType;
 hr = pPropertyValue->get_Type(&propType);
 if (FAILED(hr))
  return false;

 if (propType == ABI::Windows::Foundation::PropertyType_String)
 {
  HSTRING valueHstr;
  hr = pPropertyValue->GetString(&valueHstr);
  if (FAILED(hr))
   return false;

  UINT32 valueLen;
  PCWSTR valueStr = WindowsGetStringRawBuffer(valueHstr, &valueLen);
  outValue = wstring(valueStr);
  return true;
 }
 return false;
}
Note: Bool values are actually expanded to unsigned char in C++, so it makes sense to transfer it via byte or int types.

4 comments:

  1. I haven't tried this out but this is a gem; just what i was looking for.

    ReplyDelete
  2. Thank you so much!
    It would have taken me days to solve this by myself...

    And to think the whole point is just to pass a string argument... very frustrating.

    ReplyDelete
  3. This comment has been removed by a blog administrator.

    ReplyDelete