Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

Communication between C++ Silverlight Host and Silverlight Application

, 20 May 2010
Rate this:
Please Sign up or sign in to vote.
It explains how we can call a method defined in Silverlight from Silverlight C++ host.

Introduction

I would like you to take a look at my previous article Host_Silverlight_In_ATL before continuing with this one. In this article, we will talk about communication between C++ Silverlight host and Silverlight application which means we can call a method of Silverlight app from C++ Silverlight host.

C__ToSilverlightCommunication.PNG

Things to Do in a Silverlight Application

 using System.Windows.Browser;

Use the above mentioned namespace for communication. We will use HTML communication bridge to communicate with the C++ host.

Use the following statement to register the Communicator object. We will get this communicator object in C++ Silverlight host using IDispath.

HtmlPage.RegisterScriptableObject("Communicator", this);

Write another function in Silverlight application which we will call from C++ Silverlight host.

[ScriptableMember]
public void SetDataInTextBox(string strData)
{
	txtData.Text = strData;
}

The complete code for the Silverlight page is as follows:

using System.Windows.Browser;
namespace SilverlightTestApp
{
	public partial class MainPage : UserControl
	{
		public MainPage()
		{
			HtmlPage.RegisterScriptableObject("Communicator", this);
			InitializeComponent();
		}
		private void ClickMe_Click(object sender, RoutedEventArgs e)
		{	
			MessageBox.Show("Button click handler is in Silverlight :)", 
			"SilverlightTestApp", MessageBoxButton.OK);
		}
		[ScriptableMember]
		public void SetDataInTextBox(string strData)
		{
			txtData.Text = strData;
		}
	}
}

I am not trying to explain Silverlight development issues here but I would like to say the same things we do for Silverlight and JavaScript interaction. If someone is not familiar with the above code, Google Silverlight and JavaScript interaction. Almost every Silverlight book covers this topic.

C++ Silverlight Host

Add an edit box and a button in your dialog box in ATL application. We will enter some test in the edit box and write some code in the click handler of the button we just added.

Handler function for button click. Getting the user input in strData...

LRESULT CCMainDlg::OnBnClickedBtnSendDataToSilverlight
	(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
 	CAtlStringW strData;
 	GetDlgItem(IDC_EDIT_DATA).GetWindowTextW(strData);

Now coming to the main point - getting DISPID of Communicator object we just registered in Silverlight app.

IDispatch *pContent;
HRESULT hr = CXcpControlHost::GetXcpControlPtr()->get_Content(&pContent);
CComBSTR bstrMember(L"Communicator");
DISPID dispIDCommunicator = NULL;
hr = pContent->GetIDsOfNames(IID_NULL, &bstrMember, 1, 
	LOCALE_SYSTEM_DEFAULT, &dispIDCommunicator); 
if(FAILED(hr))
{
	::MessageBox(NULL, L"Failed to get the DISPID for Communicator", 
		L"Error :(", 0);
	return 0;
}

Once we have a valid dispID for Communicator object, we are ready to get the IDispatch* for Communicator object. It's a bit tricky here, we will make an invoke call with DISPATCH_PROPERTYGET.

VARIANT pVarResult;
DISPPARAMS dispparams; 
memset(&dispparams, 0, sizeof dispparams);
dispparams.cArgs = 0;
dispparams.rgvarg = NULL;
dispparams.cNamedArgs = 0;
// Getting IDispatch from for Scriptable object exposed by Silverlight Application. 
// Passing dispid for "Communicator" to get its IDispatch* using DISPATCH_PROPERTYGET.
hr = pContent->Invoke(dispIDCommunicator,
IID_NULL,
LOCALE_USER_DEFAULT,
DISPATCH_PROPERTYGET,
&dispparams, &pVarResult, NULL, NULL);
if(FAILED(hr))
{
	::MessageBox(NULL, L"Failed to get the IDsipatch* of Communicator", 
		L"Error :(", 0);
	return 0;
}

IDispatch* pCommunicatorDispatch = pVarResult.pdispVal;

We stored IDispatch* for Communicator in pCommunicatorDispatch. Now we have to play with this pCommunicatorDispatch. This is our key player now, with pCommunicatorDispatch, we will get the DISPID for our exposed method (SetDataInEditBox) from a Silverlight app and then make an Invoke() call to call SetDataInEditBox() by passing that string into that function.

bstrMember = L"SetDataInTextBox";
DISPID dispIDSetDataInTextBox = NULL;
hr = pCommunicatorDispatch->GetIDsOfNames(IID_NULL,&bstrMember,1, 
	LOCALE_SYSTEM_DEFAULT,&dispIDSetDataInTextBox); 
EXCEPINFO pexcepinfo ;
if(SUCCEEDED(hr))
{
	dispparams.cArgs = 1;
	dispparams.cNamedArgs = 1;
	dispparams.rgvarg = new VARIANT[1];
	dispparams.rgvarg[0].vt = VT_BSTR;
	dispparams.rgvarg[0].bstrVal = SysAllocString(strData.GetString());
	hr = pCommunicatorDispatch->Invoke(dispIDSetDataInTextBox, 
		IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, 
		&dispparams, &pVarResult,&pexcepinfo,NULL);
	delete [] dispparams.rgvarg;

	if (hr != S_OK)
	{
		::MessageBox(NULL, L"Failed to Invoke Silverlight function", 
			L"Error :(", 0);
		return 0;
	}
}
else
{
	::MessageBox(NULL, L"Failed to get the DISPID for SetDataInTextBox 
		function defined in Silverlight.", L"Error :(", 0);
}

Conclusion

In this article, I've shown you how to call a function defined in a Silverlight application from C++ Silverlight host.

Hopefully, this article will help you a little bit for Silverlight and C++ interaction.

For questions, comments and remarks, please use the commenting section at the bottom of this article.

History

  • 20th May, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Syed Aftab Naqvi
Software Developer (Senior)
United States United States
Syed Aftab Hassan Naqvi

Comments and Discussions

 
GeneralRe: help PinmemberSyed Aftab Naqvi28-Jun-10 13:11 
GeneralRe: help Pinmemberliuliu29-Jun-10 16:44 
GeneralRe: help PinmemberSyed Aftab Naqvi30-Jun-10 5:57 
GeneralRe: help Pinmemberliuliu30-Jun-10 21:39 
Hi Syed,
I have modified the dispparams as you advised.
 
CMyScriptableObject* obj=new CMyScriptableObject;
dispparams.rgvarg[0].vt = VT_DISPATCH ;
dispparams.rgvarg[0].pdispVal =(IDispatch*) obj;
 
hr = pCommunicatorDispatch->Invoke(dispIDSetDataInTextBox, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &dispparams, &pVarResult,&pexcepinfo,NULL);
 
And now it returns S_OK.
 
I implement the IMyScriptableObject as following
 
// IMyScriptableObject type library's GUID
// {E1124082-5FCD-4a66-82A6-755E4D45A9FC}
DEFINE_GUID(CLSID_ScriptObject_TypeLib, 0xe1124082, 0x5fcd, 0x4a66, 0x82, 0xa6, 0x75, 0x5e, 0x4d, 0x45, 0xa9, 0xfc);
 
// IMyScriptableObject object's GUID
// {520F4CFD-61C6-4eed-8004-C26D514D3D19}
DEFINE_GUID(CLSID_IMyScriptableObject, 0x520f4cfd, 0x61c6, 0x4eed, 0x80, 0x4, 0xc2, 0x6d, 0x51, 0x4d, 0x3d, 0x19);
 
// IMyScriptableObject VTable's GUID
// {B6127C55-AC5F-4ba0-AFF6-7220C95EEF4D}
DEFINE_GUID(IID_IMyScriptableObject, 0xb6127c55, 0xac5f, 0x4ba0, 0xaf, 0xf6, 0x72, 0x20, 0xc9, 0x5e, 0xef, 0x4d);
 
// IMyScriptableObject's VTable
 
DECLARE_INTERFACE_ (IMyScriptableObject, IDispatch)
{
// IUnknown functions
STDMETHOD (QueryInterface) (REFIID, void **) PURE;
STDMETHOD_ (ULONG,AddRef) () PURE;
STDMETHOD_ (ULONG,Release) () PURE;
// IDispatch functions
STDMETHOD (GetTypeInfoCount)( UINT *) PURE;
STDMETHOD (GetTypeInfo) ( UINT, LCID, ITypeInfo **) PURE;
STDMETHOD (GetIDsOfNames) ( REFIID, LPOLESTR *, UINT, LCID, DISPID *) PURE;
STDMETHOD (Invoke) ( DISPID, REFIID, LCID, WORD, DISPPARAMS *, VARIANT *, EXCEPINFO *, UINT *) PURE;
// Extra functions
STDMETHOD (MethodInC) ( ) ;

};
 

DEFINE_GUID(IID_IDispatchEx, 0xa6ef9860, 0xc720, 0x11d0, 0x93, 0x37, 0x0, 0xa0, 0xc9, 0xd, 0xca, 0xa9);
 
class CMyScriptableObject : IMyScriptableObject
{
public:
static int m_dwRef;
static ITypeInfo* m_pTypeInfo;

 
static HRESULT LoadMyTypeInfo(void)
{
HRESULT hr;
ITypeLib* pTypeLib = NULL;
 
if (m_pTypeInfo)
return S_OK;
 
// Load type library.
hr = LoadRegTypeLib(CLSID_ScriptObject_TypeLib, 2, 0, LOCALE_USER_DEFAULT, &pTypeLib);
 
if (SUCCEEDED(hr))
{
// Get type information for interface of the object.
hr = pTypeLib->GetTypeInfoOfGuid(IID_IMyScriptableObject, &m_pTypeInfo);
pTypeLib->Release();
 
if (FAILED(hr))
m_pTypeInfo = NULL;
}
 
return hr;
 
}
 
STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppv)
{
*ppv = NULL;
if (IID_IUnknown == riid)
{
*ppv = (LPUNKNOWN)this;
AddRef();
return NOERROR;
}
else if (IID_IDispatch == riid)
{
*ppv = (IDispatch*)this;
AddRef();
return NOERROR;
}
else if (IID_IDispatchEx == riid)
{
*ppv = (IDispatch*)this;
AddRef();
return NOERROR;
}
else
{
return E_NOTIMPL;
}
}
 
STDMETHOD_(ULONG, AddRef)()
{
return ++m_dwRef;
}
 
STDMETHOD_(ULONG, Release)()
{
if (--m_dwRef == 0)
{
delete this;
return 0;
}
return m_dwRef;
}
 
// IDispatch method
STDMETHOD(GetTypeInfoCount)(UINT* pctinfo)
{
*pctinfo = 1;
return S_OK;
 

}
 
STDMETHOD(GetTypeInfo)(UINT iTInfo,
LCID lcid,
ITypeInfo** ppTInfo)
{
*ppTInfo = NULL;
if(iTInfo != 0)
return DISP_E_BADINDEX;
m_pTypeInfo->AddRef();
*ppTInfo = m_pTypeInfo;
return S_OK;
 
}
 
STDMETHOD(GetIDsOfNames)(REFIID riid,
LPOLESTR* rgszNames,
UINT cNames,
LCID lcid,
DISPID* rgDispId)
{
if(riid != IID_NULL)
return DISP_E_UNKNOWNINTERFACE;
 
if (!m_pTypeInfo)
{
register HRESULT hr;
 
if ((hr =LoadMyTypeInfo()))
return(hr);
}

return(DispGetIDsOfNames(m_pTypeInfo, rgszNames, cNames, rgDispId));
 
}
 
STDMETHOD(Invoke)(DISPID dispIdMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS __RPC_FAR *pDispParams,
VARIANT __RPC_FAR *pVarResult,
EXCEPINFO __RPC_FAR *pExcepInfo,
UINT __RPC_FAR *puArgErr)
{

return NOERROR;
}
 
STDMETHOD(MethodInC)()
{
MessageBox(NULL,"hello","ok",MB_OK);
return S_OK;
}
};
I put this file into your TestProject, but I don't know how to register typelib of IMyScriptableObject, so that it fails when GetIDsOfNames is invoked.
Actually I want to implement GetIDsOfNames like this,
STDMETHOD(GetIDsOfNames)(REFIID riid,
LPOLESTR* rgszNames,
UINT cNames,
LCID lcid,
DISPID* rgDispId)
{
if(riid != IID_NULL)
return DISP_E_UNKNOWNINTERFACE;

*rgDispId=1; //define MethodInC id 1
return S_OK;
 
}
but it doesn't work.
 
Do you have any advice?
Thank you again.
GeneralRe: help PinmemberSyed Aftab Naqvi1-Jul-10 10:45 
GeneralRe: help Pinmemberliuliu1-Jul-10 16:35 
GeneralRe: help PinmemberSyed Aftab Naqvi2-Jul-10 9:54 
GeneralSilverlight 4 - COM integration PinmemberErnest Laurentin24-May-10 10:07 
GeneralRe: Silverlight 4 - COM integration PinmemberSyed Aftab Naqvi24-May-10 10:26 
GeneralRe: Silverlight 4 - COM integration [modified] PinmemberErnest Laurentin24-May-10 19:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140916.1 | Last Updated 20 May 2010
Article Copyright 2010 by Syed Aftab Naqvi
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid