Consuming WCF Services from COM+

Up to now, we've focused on solutions that need to leverage existing legacy application logic that is hosted in COM+. We've focused primarily on Visual Basic 6 given its distinct ability to hide some things that you need control over in order to fully leverage and reuse your application logic.

This section approaches the problem scenario from the perspective that these legacy solutions are not stagnant. In fact, it has been estimated that nearly 90 percent [] of IT budgets are focused on maintaining and extending existing solutions-many of those built on Visual Basic 6 and other legacy technologies.

[] Erlikh, L. "Leveraging Legacy System Dollars for E-Business." (IEEE) IT Pro, May/June 2000. See also http://doi.ieeecomputersociety.org/10.1109/6294.846201 and http://www.cs.jyu.fi/~koskinen/smcosts.htm.

So, those applications aren't going away. In fact, they most likely will need to be extended to support new functionality or just change the way they interface with other applications.

For the examples in this chapter, you'll look at how you can make a WCF service look like a COM+ component. This allows your legacy clients that understand COM+ to work with your new .NET 3.0–based applications that expose service endpoints. Note that both the .NET 3.0 and .NET 2.0 runtimes are required when calling from any client. This is a requirement because the dynamic invocation framework is leveraged in process by the client process.

QuickReturns Ltd. Quote Service

The QuickReturns Ltd. system, built on .NET 2.0, provides a quote service using WCF. All parts of the QuickReturns Ltd. application leverage this service. Some of the OldHorse custody systems, however, require the ability to reuse this application logic, and they've chosen to use WCF COM integration capabilities. The new QuickReturns Ltd. quote service is hosted in ASP.NET and IIS and exposes its services using WCF.

Alternatively, we'll also discuss how you can leverage runtime registration of the COM interface through the use of the WSDL and MEX service monikers.

Typed Contract Service Moniker

We'll provide a quick walk-through for the first scenario, consuming a WCF service from COM clients. This example will provide both an automation client (VBScript) and an early binding client, Visual Basic 6. The Visual Studio 2005 solution file QuickReturnsQuotes.sln contains the website and proxy projects.

The first part of the solution is the QuickReturnsQuotes WCF service, which is hosted in IIS and ASP.NET. If you haven't already run the setup script, to set up this virtual directory in IIS, run the batch file CreateVirtualDirs.bat. The requirements are that IIS is installed along with .NET 2.0 and the .NET 3.0 runtime components.

Open the solution file QuickReturnsQuotes.sln. The solution file contains two projects. The first is the website that was just mapped using the scripts mentioned previously. If the project doesn't load, there's a problem with the script on your machine, and you'll have to map the site manually and reload the project. Ensure that you have IIS and .NET 2.0 installed and ASP.NET registered with IIS (use the aspnet_regiis.exe command in the Framework folder).

The second project represents the proxy that when compiled, with a strong name, will be registered both in the GAC and as a COM interface using the RegSvcs.exe utility that's part of the .NET 2.0 Framework.

This project has several extra member files along with both prebuild and postbuild event command lines:


makeProxy.bat

This is the batch file that calls SvcUtil.exe to generate the proxy stub source files; this file is part of the project prebuild steps.


reg.bat

This is the batch file that registers the assembly in the GAC and for COM interoperability; this file is part of the project post-build steps.


unreg.bat

This is the batch file that will remove the assembly from the GAC and from COM interoperability.

NOTE

For the build steps and these batch files to work, Visual Studio 2005 must be installed in the default path. If you chose a different path or haven't installed Visual Studio 2005, you need to update the path to the utilities as required.

If you build the solution and all is successful, then you should have a GAC-installed assembly registered for COM interoperability and ready for use by COM clients. To verify, you can open Windows Explorer to the C:WindowsAssembly path and see the assembly TypedServiceProxy listed, as shown in Figure 10-17.

QuickReturns Ltd. WCF proxy in the GAC

NOTE

If you haven't modified any of the project Guid attributes, then the next two steps are not required for this project to work. This would be a normal step in your solutions to validate the correct interface GUIDs.

The next step is to both verify the registration for COM and retrieve the interface ID that is stored in the registry. The best tool for this is OleView.exe, which comes with the Windows SDK. Start OleView.exe, and open the top-level node labeled Type Libraries. Scroll down until you find TypedServiceProxy, as shown in Figure 10-18.

TypedServiceProxy registered in COM

The next step is you need to retrieve the interface ID (the GUID) for the IQuoteService interface. The OleView.exe utility can view the IDL for any COM registered classes. Double-click the item TypedServiceProxy in the list to open the ITypeLib Viewer, as shown in Figure 10-19.

ITypeLib Viewer for TypedServiceProxy

Find in the right pane of the viewer the IDL definition for the IQuoteService interface (which inherits from IDispatch—implying it supports automation as well as early bind COM clients). Now, just above it (like attributes in .NET) is a list of IDL attributes for this interface. We're looking for the universally unique identifier (UUID) just above it. For this component, its value is 058E1BEC-C44A-31FB-98C8-9FB223C46FAF.

Inside the project file TypedServiceProxy, you'll see a VBScript file that illustrates how to call from an automation client. Since this is an early bound client, it requires the interface ID to be part of the service moniker construction string for the GetObject call. The call sequence is into the quote service through COM and then through the WCF framework to the quote service .NET assembly hosted in IIS/ASP.NET.

Listing 10-11 is the source file for QuickReturnsScriptClient.vbs; note the wrap on some lines.

Example. QuickReturnsScriptClient.vbs Automation Client
Option Explicit
Dim quoteProxy, moniker, result
moniker = "service:address=http://localhost/QuickReturnsQuotes/service.svc,
							binding=wsHttpBinding"
							moniker = moniker + ", contract={058E1BEC-C44A-31FB-98C8-9FB223C46FAF}"
 '...  cut comments

Set quoteProxy = GetObject(moniker)
							result = quoteProxy.GetQuote("MSFT")
WScript.Echo "MSFT's price is " + CStr(result)

The moniker string value used for the GetObject COM call provides the address endpoint URI in addition to the binding type, which is wsHttpBinding because we're hosting in IIS. The final parameter of the moniker is the contract type. Using this GUID, the WCF framework looks up the type library information to obtain the COM interface and instantiates the proxy on the client side. The proxy in turn leverages the .NET Framework 3.0 to construct a channel and message for the request through the service boundary. This is all done "automagically" by the WCF components, which must be installed on the client tier as well.

Typed Contract: Early Bound

Visual Basic 6 can use early binding. Early binding allows the lookup and discovery of the interfaces in your COM component at design time. So, at runtime the COM client is expecting that the same UUID of your interface is registered (via type library registration). The type library that needs to be registered and referenced is part of the reg.bat batch file in the TypedServiceProxy project—QuickReturnsProxy.tlb. COM interfaces are to be considered immutable. If they change, then the underlying IDL will change. Therefore, any changes to your interfaces in the base WCF service class will require a regeneration of the proxy and a regeneration of the type library for use by clients.

NOTE

If you open the Visual Basic 6 project, you may need to reset the project references back to the TypedServiceProxy on your machine. That's accessible from Project References from within the Visual Basic 6 IDE.

If you look at the Visual Basic 6 project TypedServiceVbClient, you can see that the project should have a reference to the TypedServiceProxy type library. In the button click event handler, you can now make references directly to the types inside the COM interface (see Listing 10-12; please note the line wrap).

Example. Early Bound Visual Basic 6 Client
Private Sub Command1_Click()
    Dim obj As TypedServiceProxy.QuoteServiceClient
    Dim moniker, Ticker As String
    Dim price As Double

    moniker = "service:address=http://localhost/QuickReturnsQuotes/
							service.svc, binding=wsHttpBinding"
   On Error GoTo ErrHandler

    Ticker = UCase(Trim(txtTicker.Text))
    Set obj = GetObject(moniker)
							   price = obj.GetQuote(Ticker)
     MsgBox "Price is " & CStr(price)
    Exit Sub

ErrHandler:
    MsgBox Err.Number & " : " & Err.Description & " : " & Err.Source
End Sub

The obj object is declared to be of the interface proxy type. The moniker is then set to include only the address and the binding type. Since you're using just the default settings for the wsHttpBinding, you aren't required to supply a bindingConfiguration value. If you required overriding any of the default settings for the binding, you could supply an application configuration file with the name file.exe.config and place it in the program directory of the client. For this example, the filename would be TypedServiceVbClient.exe.config.

You then use the COM GetObject statement, which makes a call through the COM framework into the Service Control Manager (SCM, or affectionately known as "scum"), activating the COM registered WCF proxy type. Then as each method is called on the activated instance, the WCF framework is responsible for both transforming and marshaling the call from COM into the WCF framework and ultimately across the service boundary to the service class.

Dynamic Discovery

There are scenarios where registering the COM type library is not feasible. An example is Microsoft Excel spreadsheets that require dynamic discovery and invocation through COM locally to WCF services. For this, the WCF framework and the COM integration provide a dynamic model, or what's known as late binding.

What the WCF framework provides is the runtime construction of a proxy and COM interface for the COM client at object construction time. By first querying the service metadata, after being provided some initialization parameters, the WCF framework generates both a WCF proxy and a COM callable wrapper that the COM client interfaces with. You currently have two choices for the service monikers: WS-MetadataExchange (MEX) and WSDL. Given this is a nontyped model, it is callable only by clients that support automation (IDispatch) such as VBScript, Visual Basic 6, Excel, and so on.

Metadata Exchange Contract Service Moniker

WCF supports the WS-MetadataExchange protocol that provides the discovery of services in addition to policy and schema information. Please see Chapter 4 for more information. The WCF COM integration framework uses this to dynamically derive the service endpoint interfaces along with binding and service behavior.

Starting with the scripting sample from the project file in Listing 10-2, there's an additional VBScript file: QuickReturnsScriptClientMex.vbs. Listing 10-13 shows its contents (note the line wrap).

Example. QuickReturns Ltd. Script Using Mex Service Moniker
Option Explicit
Dim quoteProxy, moniker, result
moniker="service:mexAddress=http://localhost/QuickReturnsQuotes/service.svc/mex, "
							moniker=moniker + "address=http://localhost/QuickReturnsQuotes/service.svc,"
							moniker=moniker + "contract=IQuoteService, "
							moniker=moniker + "contractNamespace=http://PracticalWcf/QuoteService, "
							moniker=moniker + "binding=WSHttpBinding_IQuoteService, "
							moniker=moniker + "bindingNamespace=http://tempuri.org/"

Set quoteProxy = GetObject(moniker)
result = quoteProxy.GetQuote("MSFT")
WScript.Echo "MSFT's price is " + CStr(result) WSDL Contract Service Moniker

From the code in Listing 10-13, you don't have a local configuration file or a strongly typed object (in COM or .NET). Therefore, you must supply the "discovery" information to the GetObject call. One part is the URI for where the MEX metadata is found. The others are the URI of the service endpoint, binding, and contract information that will be mapped into the MEX response.

The contract and contractNamespace comes directly from the metadata binding information inside the <wsdl:binding> element from the metadata. This must match what the MEX response contains; otherwise, you'll receive a mismatch on the contract error. For this sample, this represents the <wsdl:binding> element that is visible if you request the WSDL for the service using the following URI:

http://localhost/QuickReturnsQuotes/service.svc?wsdl

WSDL Contract Service Moniker

Similar to how WCF works with the WS-MetadataExchange protocol to dynamically derive the COM and WCF interfaces and types, the service moniker can also work with a WSDL contract. Listing 10-14, contained in the file QuickReturnsScriptClientWsdl.vbs, illustrates how to make a call using the service moniker for WSDL.

Example. QuickReturns Ltd. Script Using WSDL Service Moniker
Option Explicit
Dim quoteProxy, wsdl, moniker, result

wsdl = GetWsdlFromUrl ("http://localhost/QuickReturnsQuotes/service.svc?wsdl)
							moniker="service:wsdl=" & wsdl & ", "
							moniker=moniker + "address=http://localhost/QuickReturnsQuotes/service.svc,"
							moniker=moniker + "contract=IQuoteService, "
							moniker=moniker + "contractNamespace=http://tempuri.org/, "
							moniker=moniker + "binding=WSHttpBinding_IQuoteService, "
							moniker=moniker + "bindingNamespace=http://tempuri.org/"
Set quoteProxy = GetObject(moniker)

result = quoteProxy.GetQuote("MSFT")
WScript.Echo "MSFT's price is " + CStr(result)

Function GetWsdlFromUrl( strUrl )
    Dim WinHttpReq, resp
    Set WinHttpReq = CreateObject("WinHttp.WinHttpRequest.5)
    resp = WinHttpReq.Open("GET", strUrl, False)
    WinHttpReq.Send()
    GetWsdlFromUrl = WinHttpReq.ResponseText
End Function

NOTE

For the previous dynamic HTTP request for the WSDL to work, the correct version of the WinHttp services component needs to be referenced by the CreateObject call. On some installations, this may be WinHttp.WinHttpRequest.5.1. Please see http://msdn.microsoft.com/library/en-us/winhttp/http/winhttp_start_page.asp for more information.

The first statement after the variable declarations makes a call to the included function that invokes GetWsdlFromUrl. This VBScript function just makes an HTTP get call to the URI to retrieve the HTTP response, which for that URI is the WSDL document for the service interface.

The moniker initialization string is then composed of the WSDL response along with the remaining service moniker attributes. The WSDL string is an XML response that fully describes the IQuoteService interface exposed at the endpoint address. It's the same XML you would see if you opened the URL http://localhost/QuickReturnsQuotes/service.svc?wsdl directly from a browser.

Again, using the dynamic service moniker, the COM interface makes a call into the WCF framework to dynamically construct the types necessary to make a round-trip request into the WCF service that is hosted in IIS—all without the COM client knowing the underlying workings of how to work with WCF (other than the moniker construction). What the dynamic generation provides is the generation of a fully configured proxy that matches the service endpoints' advertised metadata including policy, security, and contract information.

Briefly, let's summarize what the high-level steps are required to consume a WCF service as a COM interface leveraging a typed contract service moniker:

  1. Generate a proxy using the WCF SvcUtil.exe utility.

  2. Create a project/solution in Visual Studio 2005 that contains the generated proxy class file.[]

    [] This is not required, but it makes things easier.

  3. Add the attribute ComVisible to the solution; you can add this to the AssemblyInfo.cs file.

  4. Provide a strong name for the assembly; this is optional but allows loading to the GAC to ensure a single version is loaded.

  5. Register the assembly for COM using the RegAsm tool.

  6. Install the assembly in the GAC; this is optional but ensures a single version is loaded.

  7. Create an application configuration file for your client executable; for example, if the client is called OldHorseClient.exe, then the configuration file is OldHorseClient.exe.config. This is the standard .NET configuration file-naming requirement.

  8. Use the GetObject statement in your COM environment (Visual Basic 6, scripting, and so on) with the service moniker to instantiate the WCF service interface.

Security Credentials with IChannelCredentials

In any modern IT infrastructure, security is paramount for interoperability amongst applications in a distributed solution. In our example, there are clear requirements for securing our services. The examples so far haven't demonstrated any way of securing our services, whether exposed to legacy COM+ clients or wrapping COM+ services with the WCF services.

Chapter 7 discussed WCF security in detail and covered both the declarative and programmatic means for securing services. Fortunately, the COM+ interoperability tier of WCF leverages the same base framework for supplying both transport and message security.

For the final steps in the COM+ integration, we'll show how to secure the WCF service that you are consuming from late-bound COM+ clients. A few additional steps are required to configure the IIS instance that hosts the QuickReturnsQuotesSecure website. Again, we've supplied a script, CreateVirtualDirs.bat, that configures IIS and sets the ASP.NET runtime to .NET 2.0, which is required for running .NET 3.0 services.

The first modification is to the service Web.config. Here you add the necessary security configuration elements to enable both transport-level and message-level security. Make the modification to the binding element that is associated with the endpoint, as shown in Listing 10-15.

ENABLING SSL ON IISY

Using transport security requires SSL; consequently, you need to enable the website to use SSL, which requires the presence of a server certificate.

To install a test certificate, you can leverage the IIS 6 Resource Kit SelfSSL.exe ).[] Simply execute the following command:

selfssl /t

This installs a server certificate for use by IIS and places it in the Trusted Certificate list. WCF by default validates the certificate if you trust the issuer, so, for this example, it will fail without the /t switch.


[] You can download the IIS 6 Resource Kit from the following URL: http://www.microsoft.com/downloads/details.aspx?FamilyID=56fc92ee-a71a-4c73-b628-ade629c89499&DisplayLang=en.

Example. Security-Enabled Web.config
<bindings>
  <wsHttpBinding>
    <binding name="Binding1">
<security mode="TransportWithMessageCredential">
							  <message clientCredentialType="UserName" />
							     </security>
    </binding>
  </wsHttpBinding>
</bindings>

For this solution, we are using wsHttpBinding. To add transport and message security, you add the security element and set the mode to TransportWithMessageCredential. This mode provides both an SSL-based channel along with message-encoded authentication. Additionally, for the message element, you inform the WCF framework that you require UserName authentication. This informs WCF that the client embeds a username and password into the message, and this will pass over SSL.

The solution includes the project TypedServiceProxySecure class library that contains a set of batch files that dynamically generate the proxy file along with providing both GAC installation and COM+ registration. These steps are in the prebuild and post-build steps associated with the project.

Once you've configured your service to support the security model required, you can now update the late-bound VBScript client to make a call matching the security requirements of the service.

If you take the late-bound VBScript file QuickReturnsScriptClientMex.vbs and run it without specifying any credentials, you'll see an exception raised to the client indicating that the username is not provided, so you need to specify it in ClientCredentials.

Fortunately, COM+ integration makes that easy from the COM+ client. You just need to make a call to set the credentials on the channel. Listing 10-16 shows the updated VBScript that supports username and password authentication (watch the line wrap).

Example. Late-Bound VBScript with Security
Option Explicit
Dim quoteProxy, moniker, result

moniker="service:mexAddress=http://xpshawnci/QuickReturnsQuotesSecure/"
moniker=moniker + "service.svc/mex, "
moniker=moniker + "address=https://xpshawnci/QuickReturnsQuotesSecure/service.svc, "
moniker=moniker + "contract=IQuoteService, "
moniker=moniker + "contractNamespace=http://tempuri.org/, "
moniker=moniker + "binding=WSHttpBinding_IQuoteService, "
moniker=moniker + "bindingNamespace=http://tempuri.org/"

Set quoteProxy = GetObject(moniker)
quoteProxy.ChannelCredentials.SetUserNameCredential "xpshawncisoauser", "p@ssw0rd"

result = quoteProxy.GetQuote("MSFT")
WScript.Echo "MSFT's 8price is " + CStr(result)

The only modification you needed to make is adding the call, shown in bold. Here we made a call to the ChannelCredentials.SetUserNameCredential method, passing in the username and password of the principal. The WCF framework then generates a security token that is put into the message. On the receiving side, the server validates that token. If we supplied an invalid username or password, the error that the late-bound client receives is that the "security token could not be validated" message.

Internally, the SetUserNameCredential call creates a new ClientCredential object, setting the username and password, and adds it to an internal collection of security credentials that are associated to the channel.

Other methods of supplying credentials are part of the IChannelCredentials interface, imple-mented by the System.ServiceModel.ComIntegration.ChannelCredentials type. The IChannelCredentials interface supports other credential types such as Windows credentials (SetWindowsCredentials), certificate based (SetClientCertificateFromStore, SetClientCertificateFromStoreByName), and SetIssueToken for use with Security Token Service (STS) services.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset