A little while back Martin sent me a question on some of the examples in Chapter 10 of our book (Pro WCF). The point of the question was how to dynamically consume a WCF services (late bind) from C++ using COM.
The root of the capability lies in the moniker implementation, which is provided for inside of System.ServiceModel.ComIntegration. There's a series of types, attribute type as well, that the ServiceModel framework will build up the COM client along with the interfaces based upon a "GetObject" call from VB or even C++.
The thing with VB is it makes things so much easier. So, I finally got around to fiddling with it. I did end up with an issue, but the jist is easy to follow.
The source code link here has a couple of samples that dynamically call a COM interface - one using the "script" moniker which is for Windows Scripting Host Components (built a whole site based upon that years ago) and WCF.
Let's take a look at calling a Scripting Component using C++ COM. Note that the CoGetObject is a call that combines 3 distinct calls into 1 convenient method. Take a look at the attached sample wsc file to see the inners of the component.
The script moniker is the key. Just as we'll see in a bit the service moniker. Both are defined in the registy under HKCR\script and HKCR\service as to which COM component implements the moniker (IMoniker) interface.
The main steps are: 1) Initialize COM, 2) GetObject, 3) Get Dispatch Interface for method, 4) Invoke interface
Sample Solution
1 int CallWscComponent()
2 {
3 HRESULT hr;
4 IDispatch* objWsc;
5
6 hr = CoGetObject(L"script:D:\\Data\\Projects\\CallingWcf\\test.wsc",
7 NULL,
8 IID_IDispatch,
9 (void**)&objWsc);
10
11 if (FAILED(hr))
12 {
13 Message(TEXT("Client: CoGetObject"), hr);
14 return(hr);
15 }
16
17
18 DISPID dispid;
19 CString strFxName = "methodname";
20 OLECHAR * szMember2 = strFxName.AllocSysString();
21
22 hr = objWsc->GetIDsOfNames(
23 IID_NULL,
24 &szMember2,
25 1,
26 LOCALE_SYSTEM_DEFAULT,
27 &dispid);
28
29 if (FAILED(hr))
30 {
31 Message(TEXT("Client: GetIDsOfNames"), hr);
32 return(hr);
33 }
34
35 DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
36
37 hr = objWsc->Invoke(
38 dispid,
39 IID_NULL,
40 LOCALE_USER_DEFAULT,
41 DISPATCH_METHOD,
42 &dispparamsNoArgs, NULL, NULL, NULL);
43
44 if (FAILED(hr))
45 {
46 Message(TEXT("Client: Invoke"), hr);
47 return(hr);
48 }
49
50 }
Now, here's an example of using an untyped proxy and Mex discovery.
The first thing is to do is build the moniker:
LPTSTR moniker = L"service:mexAddress=\"http://localhost:8899/WebService/Service.svc/mex\",\
address=\"http://localhost:8899/WebService/Service.svc\",\
contract=IMyService, \
contractNamespace=http://tempuri.org/,binding=BasicHttpBinding_IMyService, \
bindingNamespace=http://tempuri.org/";
Now, here's a quick walk-through of calling WCF through COM.
1 void CallWcf()
2 {
3 HRESULT hr;
4 IDispatch* objWsc;
5
6 hr = CoGetObject(moniker,
7 NULL,
8 IID_IDispatch,
9 (void**)&objWsc);
10
11 if (FAILED(hr))
12 {
13 Message(TEXT("Client: CoGetObject"), hr);
14 return(hr);
15 }
16
17 DISPID dispid;
18 CString strFxName = "MyOperation3";
19 OLECHAR * szMember2 = strFxName.AllocSysString();
20
21 hr = objWsc->GetIDsOfNames(
22 IID_NULL,
23 &szMember2,
24 1,
25 GetUserDefaultLCID(),
26 &dispid);
27
28 if (FAILED(hr))
29 {
30 Message(TEXT("Client: GetIDsOfNames"), hr);
31 return(hr);
32 }
33
34 DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
35 EXCEPINFO pExcepInfo;
36 memset(&pExcepInfo, 0, sizeof(EXCEPINFO));
37
38
39 hr = objWsc->Invoke(
40 dispid,
41 IID_NULL,
42 GetUserDefaultLCID(),
43 DISPATCH_METHOD,
44 &dispparamsNoArgs, NULL, NULL, NULL);
45
46 if (FAILED(hr))
47 {
48 Message(TEXT("Client: Invoke"), hr);
49 return(hr);
50 }
51
52 }