locked
C++/CX Reflection

    Question

  • Hello, how can I introspect an object? I'm able to get its class name (using IInspectable) but I'd like to get a list of its properties. Also I'd like to know how to access an object's properties and how to invoke a method when I have its name (string).

    I'm aware of the System.Reflection namespace in the .NET layer but it doesn't seem to be available in C++/CX.

    Monday, September 26, 2011 9:12 AM

Answers

  • Nishant is correct. While C++ doesn't provide any specific APIs to "reflect" on WinRT types, these types are fully defined in CLI compliant metadata files and you can use the CLR native metadata APIs to read their definition. One thing you need to figure out is how to get to the actual winmd for a given type. For that, Win8 provides new metadata discovery APIs (RoGetMetaDataFile).
    This won't allow you to do any instantiation or method calls though. So this should rarely be useful in a C++ app.
    However, since Marius was asking for a sample, here's a snippet of code that I was playing with. Disclaimer: while I tried to make sure that the snippet works and correctly handles most errors, I might've missed a few things so treat this code with a grain of salt. :)
    #include <windows.h>
    
    #include <hstring.h>
    #include <cor.h>
    #include <rometadata.h>
    #include <rometadataresolution.h>
    #include <collection.h>
    
    namespace ABI_Isolation_Workaround {
    	#include <inspectable.h>
    	#include <WeakReference.h>
    }
    
    using namespace ABI_Isolation_Workaround;
    #include <wrl/client.h>
    
    using namespace Microsoft::WRL;
    using namespace Windows::Foundation::Collections;
    
    IVector<String^>^ GetTypeMethods(Object^);
    
    MainPage::MainPage()
    {
        InitializeComponent();
    
    	Windows::Foundation::Uri^ uri = ref new Windows::Foundation::Uri("http://buildwindows.com/");
    	auto methods = GetTypeMethods(uri);
    
    	std::wstring strMethods;
    	std::for_each(begin(methods), end(methods), [&strMethods](String^ methodName) {
    		strMethods += methodName->Data();
    		strMethods += L"\n";
    	});
    
    	wprintf(L"%s\n", strMethods.c_str());
    }
    
    IVector<String^>^ GetTypeMethods(Object^ instance)
    {
    	HRESULT hr;
    	HSTRING hStringClassName;
    	hr = instance->__cli_GetRuntimeClassName(reinterpret_cast<__cli_HSTRING__**>(&hStringClassName)); // internal method name subject to change post BUILD
    	if (FAILED(hr))
    		__cli_WinRTThrowError(hr); // internal method name subject to change post BUILD
    	String^ className = reinterpret_cast<String^>(hStringClassName); 
    
    	ComPtr<IMetaDataDispenserEx> metadataDispenser;
    	ComPtr<IMetaDataImport2> metadataImport;
    
    	hr = MetaDataGetDispenser(CLSID_CorMetaDataDispenser, IID_IMetaDataDispenser, (LPVOID*)metadataDispenser.GetAddressOf());
    	if (FAILED(hr))
    		__cli_WinRTThrowError(hr); // internal method name subject to change post BUILD
    
    	HSTRING hStringFileName;
    	mdTypeDef typeDefToken; 
    	hr = RoGetMetaDataFile(hStringClassName, metadataDispenser.Get(), &hStringFileName, &metadataImport, &typeDefToken);
    	if (FAILED(hr))
    		__cli_WinRTThrowError(hr); // internal method name subject to change post BUILD
    	String^ fileName = reinterpret_cast<String^>(hStringFileName);
    	
    	HCORENUM hCorEnum = 0;
    	mdMethodDef methodDefs[2048];
    	ULONG countMethodDefs = sizeof(methodDefs);
    	hr = metadataImport->EnumMethods(&hCorEnum, typeDefToken, methodDefs, countMethodDefs,  &countMethodDefs);
    	if (FAILED(hr))
    		__cli_WinRTThrowError(hr); // internal method name subject to change post BUILD
    
    	wchar_t methodName[1024];
    	ULONG countMethodName;
    	std::wstring strMethods;
    	Vector<String^>^ retVal = ref new Vector<String^>();
    
    	for(int i = 0; i < countMethodDefs; ++i)
    	{
    		countMethodName = sizeof(methodName);
    		hr = metadataImport->GetMethodProps(methodDefs[i], nullptr, methodName, countMethodName, &countMethodName, nullptr, nullptr, nullptr, nullptr, nullptr);
    		if (SUCCEEDED(hr))
    		{
    			methodName[ countMethodName ] = 0;
    			retVal->Append(ref new String(methodName));
    		}
    	}
    	return retVal;
    } 
    
    
    
    You'll need to pass Runtimeobject.lib and Rometadata.lib to Linker.
    Hope this helps,
    Marian Luparu
    Visual C++
    Tuesday, September 27, 2011 12:49 AM

All replies

  • I would love to see a sample about something like that. Is it possible to reflect on a DLL and figure out what types are exported, their methods, properties, etc. I'm actually building a demo Metro app and this would be of great help for extensibility and auto discovery.
    Microsoft MVP VC++ | www.mariusbancila.ro/blog | www.codexpert.ro
    Monday, September 26, 2011 2:02 PM
  • Hello, how can I introspect an object? I'm able to get its class name (using IInspectable) but I'd like to get a list of its properties. Also I'd like to know how to access an object's properties and how to invoke a method when I have its name (string).

    I'm aware of the System.Reflection namespace in the .NET layer but it doesn't seem to be available in C++/CX.


    You could just examine the winmd file using .NET reflection. Apparently that seems to what the compilers and the intellisense does.
    http://blog.voidnish.com
    • Proposed as answer by Bob_Bao Tuesday, September 27, 2011 5:22 AM
    Monday, September 26, 2011 10:21 PM
  • Hello, how can I introspect an object? I'm able to get its class name (using IInspectable) but I'd like to get a list of its properties. Also I'd like to know how to access an object's properties and how to invoke a method when I have its name (string).

    I'm aware of the System.Reflection namespace in the .NET layer but it doesn't seem to be available in C++/CX.


    You could just examine the winmd file using .NET reflection. Apparently that seems to what the compilers and the intellisense does.
    http://blog.voidnish.com

    Note : I don't think they actually use reflection to do that. My guess would be that they'd have code that directly parses the file format rather than go through .NET.  But for us end-users, it may be far more simpler to use reflection.
    http://blog.voidnish.com
    Monday, September 26, 2011 10:22 PM
  • Nishant is correct. While C++ doesn't provide any specific APIs to "reflect" on WinRT types, these types are fully defined in CLI compliant metadata files and you can use the CLR native metadata APIs to read their definition. One thing you need to figure out is how to get to the actual winmd for a given type. For that, Win8 provides new metadata discovery APIs (RoGetMetaDataFile).
    This won't allow you to do any instantiation or method calls though. So this should rarely be useful in a C++ app.
    However, since Marius was asking for a sample, here's a snippet of code that I was playing with. Disclaimer: while I tried to make sure that the snippet works and correctly handles most errors, I might've missed a few things so treat this code with a grain of salt. :)
    #include <windows.h>
    
    #include <hstring.h>
    #include <cor.h>
    #include <rometadata.h>
    #include <rometadataresolution.h>
    #include <collection.h>
    
    namespace ABI_Isolation_Workaround {
    	#include <inspectable.h>
    	#include <WeakReference.h>
    }
    
    using namespace ABI_Isolation_Workaround;
    #include <wrl/client.h>
    
    using namespace Microsoft::WRL;
    using namespace Windows::Foundation::Collections;
    
    IVector<String^>^ GetTypeMethods(Object^);
    
    MainPage::MainPage()
    {
        InitializeComponent();
    
    	Windows::Foundation::Uri^ uri = ref new Windows::Foundation::Uri("http://buildwindows.com/");
    	auto methods = GetTypeMethods(uri);
    
    	std::wstring strMethods;
    	std::for_each(begin(methods), end(methods), [&strMethods](String^ methodName) {
    		strMethods += methodName->Data();
    		strMethods += L"\n";
    	});
    
    	wprintf(L"%s\n", strMethods.c_str());
    }
    
    IVector<String^>^ GetTypeMethods(Object^ instance)
    {
    	HRESULT hr;
    	HSTRING hStringClassName;
    	hr = instance->__cli_GetRuntimeClassName(reinterpret_cast<__cli_HSTRING__**>(&hStringClassName)); // internal method name subject to change post BUILD
    	if (FAILED(hr))
    		__cli_WinRTThrowError(hr); // internal method name subject to change post BUILD
    	String^ className = reinterpret_cast<String^>(hStringClassName); 
    
    	ComPtr<IMetaDataDispenserEx> metadataDispenser;
    	ComPtr<IMetaDataImport2> metadataImport;
    
    	hr = MetaDataGetDispenser(CLSID_CorMetaDataDispenser, IID_IMetaDataDispenser, (LPVOID*)metadataDispenser.GetAddressOf());
    	if (FAILED(hr))
    		__cli_WinRTThrowError(hr); // internal method name subject to change post BUILD
    
    	HSTRING hStringFileName;
    	mdTypeDef typeDefToken; 
    	hr = RoGetMetaDataFile(hStringClassName, metadataDispenser.Get(), &hStringFileName, &metadataImport, &typeDefToken);
    	if (FAILED(hr))
    		__cli_WinRTThrowError(hr); // internal method name subject to change post BUILD
    	String^ fileName = reinterpret_cast<String^>(hStringFileName);
    	
    	HCORENUM hCorEnum = 0;
    	mdMethodDef methodDefs[2048];
    	ULONG countMethodDefs = sizeof(methodDefs);
    	hr = metadataImport->EnumMethods(&hCorEnum, typeDefToken, methodDefs, countMethodDefs,  &countMethodDefs);
    	if (FAILED(hr))
    		__cli_WinRTThrowError(hr); // internal method name subject to change post BUILD
    
    	wchar_t methodName[1024];
    	ULONG countMethodName;
    	std::wstring strMethods;
    	Vector<String^>^ retVal = ref new Vector<String^>();
    
    	for(int i = 0; i < countMethodDefs; ++i)
    	{
    		countMethodName = sizeof(methodName);
    		hr = metadataImport->GetMethodProps(methodDefs[i], nullptr, methodName, countMethodName, &countMethodName, nullptr, nullptr, nullptr, nullptr, nullptr);
    		if (SUCCEEDED(hr))
    		{
    			methodName[ countMethodName ] = 0;
    			retVal->Append(ref new String(methodName));
    		}
    	}
    	return retVal;
    } 
    
    
    
    You'll need to pass Runtimeobject.lib and Rometadata.lib to Linker.
    Hope this helps,
    Marian Luparu
    Visual C++
    Tuesday, September 27, 2011 12:49 AM
  • I'd love to point out that James McNellis just released a full C++ library for CX reflection...Very well designed.

    http://seaplusplus.com/2012/04/26/cxxreflect-native-reflection-for-the-windows-runtime/

    • Proposed as answer by jmorrill Thursday, April 26, 2012 9:15 PM
    Thursday, April 26, 2012 9:15 PM