Pass information to .NET control from Native Code with Visual C++

As a follow up to this post on interoperability between .NET and native code, this post describes how we can host a .NET user control from an ActiveX control, but more importantly set properties that drive or change the behaviour of the .NET user control.

First of all, we need to define an interface with the properties that are required by the user control, but more importantly accessible to native code, and we declare it similarly we do with any other interface but we need to decorate it with a Guid attribute. In this case the control stores the path for a given file to do something, at the same time it’s important to note that if some operations are required to be done to the control then the control should return its HWND (as depicted below)

[Guid("EBD2C511-4EEB-488A-9BF1-EE14FB631C92")] public interface IMetaFsProps { /// <summary> /// Gets the control HWND. /// </summary> /// <returns>IntPtr.</returns> IntPtr GetControlHwnd(); /// <summary> /// Gets or sets the selected file. /// </summary> /// <value>The selected file.</value> string SelectedFile { get; set; } }

Then our .NET user control must implement the interface previously mentioned, but the user control class must also be decorated with a few attributes which are

[ClassInterface(ClassInterfaceType.None)] [ComSourceInterfaces(typeof(IMetaFsProps))] [Guid("917937B1-1AD8-4951-A417-3BED065089A0")] public partial class MyFileControl : UserControl, IMetaFsProps { /// <summary> /// Gets the control HWND. /// </summary> /// <returns></returns> public IntPtr GetControlHwnd() { return Handle; } /// <summary> /// Gets or sets the selected file. /// </summary> /// <value>The selected file.</value> public string SelectedFile { get; set; } }


In our Visual C++ COM object we instantiate and host the .NET user control as shown next

HRESULT MyNativeComponent::AddPages(IN LPFNADDPROPSHEETPAGE lpfnAddPage, IN LPARAM lParam) { HPROPSHEETPAGE hPage; PROPSHEETPAGE psp = {0}; auto retval = E_INVALIDARG; psp.dwSize = sizeof(PROPSHEETPAGE); psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_DEFAULT; psp.hInstance = _AtlBaseModule.GetResourceInstance(); psp.pszTemplate = MAKEINTRESOURCE(IDD_EMPTYPAGE); psp.pszTitle = _T("My dialog's name goes here"); psp.pcRefParent = NULL; psp.lParam = reinterpret_cast<LPARAM>(new wstring(; psp.pfnDlgProc = [](HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)->LRESULT { switch (uiMsg) { case WM_INITDIALOG: CoInitialize(NULL); CComPtr<IDispatch> pUnk; CAxWindow container; auto psp = reinterpret_cast<PROPSHEETPAGE*>(lParam); shared_ptr<wstring> selectedFile(reinterpret_cast<wstring*>(psp->lParam)); container.Attach(hwnd); pUnk.CoCreateInstance(L"MyAssembly.MyFileControl", NULL); // We set the propery here!!! pUnk.PutPropertyByName(_bstr_t("SelectedFile"), &_variant_t(selectedFile->data())); container.AttachControl(pUnk, NULL); CoUninitialize(); pWndProc = (WNDPROC)SetWindowLongPtr(GetParent(hwnd), GWLP_WNDPROC, (LONG_PTR)&CustomWndProc); break; } return FALSE; }; if ((hPage = CreatePropertySheetPage(&psp)) != NULL) { if (!lpfnAddPage(hPage, lParam)) DestroyPropertySheetPage(hPage); retval = NOERROR; } return retval; }

Leave a Reply

Your email address will not be published. Required fields are marked *