Rick Strahl's FoxPro and Web Connection Web Log
 
 

Web Connection Tip: Virtual Path Resolving with ~/


No comments
Sunday, March 31, 2013, 4:54:28 PM

Getting paths to resources and media content right in Web applications is a crucial part of Web development. In general you never ever want to hard code a path to an internal site link or resource within your application. Whenever possible any page code that creates HREF links or links to CSS, Images or scripts should either be relative (ie. ../image.png) or relative to the root site.

Relative linking is native to HTML and CSS and if possible you should use it. Relative paths are relative to the currently active URL and so allow you to always keep relative locations in sync. If the entire application moves to a new folder the links still work because they're relative to each other.

Relative paths can break down when you move a page to a different location within a site. If you have a page that has relative links to an image in a subfolder (like images/refresh.png) and you then move this page up one level in the folder hierarchy it no longer finds the image in an images subfolder so either the link needs to be adjusted or your link breaks.

Relative paths are good, but you can't always use them - specifically from generic code that doesn't live on a page per se or that code that needs to run from different pages. Generally this happens in custom components or helper functions. Examples of these inside of Web Connection itself are Web Control Framework pages or helper functions like the wwHtmlHelper functions.

Enter Virtual Path Syntax ~/

Web Connection - much like ASP.NET - supports virtual path syntax which is as follows:

  • ~/page.wwd
  • ~/images/home.png
  • ~/admin/monitor/monitor.htm

This syntax provides virtual directory path relative links that are in effect application specific. Give a virtual directory of /wconnect the above paths evaluate to:

  • /wconnect/page.wwd
  • /wconnect/images/home.png
  • /wconnect/admin/monitor/monitor.htm

Where does it work?

You can use ~/ paths anywhere in Web Connection output when using the wwPageResponse class (which is the default for Web Connection 5.0 and later). When using this class all output that includes ~/ based paths are automatically fixed up. So regardless of whether you're using Response.Write() and ExpandTemplate(), ExpandScript() or a Web Control Framework page links are fixed up.

The following automatically convert to a full virtual path:

  • <a href="~/admin/admin.aspx">Admin Page</a>
  • <img src="~/css/images/home.png" />
  • <a href="~/">Home</a>
  • <div style="background-image: url(/wconnect/weblog/images/closebox.png)"></div>

So the first link would render as /wconnect/admin/admin.aspx etc.

How does it work?

Web Connection does this as part of the Response class Rendering process where it checks for text/html content and if it is searches for ~/ strings to replace. Specifically it looks for ="~/ and url(/wconnect/weblog/ in the output and replaces it. Internally Web Connection uses Process.cBaseUrl which provides the base virtual url for a virtual directory or root site. The value is filled from the YourApp.ini file and the VirtualPath property for your Process class:

[Wwdemo]
Datapath=C:\WWAPPS\WC3\wwDemo\
Htmlpagepath=c:\westwind\wconnect\
Virtualpath=/wconnect/

This value is read on application startup and then assigned to the Process.cUrlBasePath and is available anywhere. When wwPageResponse renders it does the replacement like so:

FUNCTION Render()
LOCAL lcOutput, lcBasePath
 
*** Fix up ~/ paths with UrlBasePath
IF THIS.contentType = "text/html" AND VARTYPE(Process) = "O"
   lcBasePath = Process.cUrlBasePath
   IF !EMPTY(lcBasePath)
      this.cOutput = STRTRAN(this.cOutput,[="~/],[="] + lcBasePath)
      this.cOutput = STRTRAN(this.cOutput,[url(/wconnect/weblog/],[url(] + lcBasePath)
   ENDIF
ENDIF

Resolving Virtual Path Syntax Programmatically

In your own code if you need to parse a virtual path you can use the same virtual path syntax with Process.ResolveUrl() method. As long as you are running with a Web Connection Process instance the top level PRIVATE Process object is in scope and you can call ResolveUrl() on it to resolve virtual relative paths.

So from within your FoxPro code you can easily do:

lcjQuery = Process.ResolveUrl("~/scripts/jquery.min.js")

to get /wconnect/scripts/jquery.min.js.

Paths are important!

This seems like a very simple thing and you might even write this off as, "ah I don't need this I just use relative paths". But I'm often surprised how often I see code that doesn't take this into account and ends up hardcoding virtual paths like /wconnect/css/images/home.png. You should NEVER write code like that either in HTML or in FoxPro codebehind. If in the future your app moves to a new virtual directory or - more commonly  - a root site links like that simply will no longer work and break your code.

Remember to always use relative paths - either page relative or virtual relative - to ensure that your site remains portable. It's an important lesson to remember if you've had to go in and fix up links once after the fact.


wwDotnetBridge and .NET Versions


No comments
Monday, February 25, 2013, 1:09:05 AM

Judging from questions on the message board and private support issues that I've been debugging with customers, there's some confusion on how wwDotnetBridge works with various .NET versions. In this post I discuss how wwDotnetBridge loads the .NET Runtime and how .NET versions are managed by the hosted  runtime.

In case you haven't heard about wwDotnetBridge, it is an open source library to make it easier to interact with .NET components by providing much richer access to .NET functionality than is possible through regular COM Interop.

You can find out more about wwDotnetBridge here:

How wwDotnetBridge loads the .NET Runtime

One of the main features of wwDotnetBridge is that it hosts the .NET Runtime directly inside of Visual FoxPro. By doing so it's possible to bypass the COM registration requirements for .NET COM Interop, which is both cumbersome and seriously limits of what .NET components you can access with standard COM Interop. wwDotnetBridge hosts the .NET Runtime explicitly and loads wwDotnetBridge into it, which allows loading of arbitrary .NET components directly and offering access to a much richer feature set and most .NET components.

In more detail here is how it works:

wwDotnetBridge hosts the .NET Runtime manually inside your Visual FoxPro IDE or compiled FoxPro EXE process. It does this by using some Windows APIs - CorBindRuntimeEx() specifically - to create a Runtime host instance and loading an AppDomain into it. The Windows API returns an instance to the .NET Runtime Host via COM and passes back a reference to the host and AppDomain. wwDotnetBridge's loader then loads the wwDotnetBridge .NET component into the appdomain and retrieves the reference to this .NET object reference over COM and passes it back to FoxPro.

This instance is then stored on the wwDotnetBridge::oDotnetBridge property, which hangs on to that instance. The instance can then be used to load assemblies, creating .NET object instances (without COM registration) and perform all the other rich features that are available on the wwDotnetBridge object and most of these tasks are wrapped by the FoxPro wwDotnetBridge instance.

Although, the .NET component was launched using COM and acts basically like any other .NET COM object once returned to Visual FoxPro, the component did not have to be registered in the registry. Rather wwDotnetBridge loads components from within the .NET Runtime based on the component's name. This makes it possible to launch just about any .NET component directly, regardless of whether it's registered for COM interop or not.

Here's a graph that shows the general flow of of the loader process:

wwdotnetBridgeArchitecture

The end result of this process is that you end up with the wwDotnetBridge FoxPro class that lets you instantiate any .NET component - including those that aren't registered through COM - directly from Visual FoxPro:

do wwDotNetBridge  && Load library
loBridge = CreateObject("wwDotNetBridge","V4")
loBridge.LoadAssembly("bin\InteropExamples.dll")

*** Create a .NET object instance
loFox = loBridge.CreateInstance("InteropExamples.Examples") *** Call a method on the .NET object and return another .NET object loPerson = loFox.GetNewPerson() ? loPerson.FirstName ...

You can find out more about what you can do with this functionality in the white paper. In this post the focus is the .NET Runtime loading and how to manage the .NET version loaded.

Understanding .NET Runtime Loading

One key aspect to about .NET Runtime loading to understand is that only one instance of the .NET Runtime can be active in a process at any given point in time. Although wwDotnetBridge allows you to explicitly specify the runtime version to load, only the first load of the .NET actually loads an instance of the Runtime. All subsequent loads simply use the already loaded instance of the .NET Runtime that exists in memory.

This means if you do something like the following:

loBridge = CreateObject("wwDotNetBridge","V4")
? loBridge.GetDotnetVersion()
 
loBridge2 = CreateObject("wwDotNetBridge","V2")
? loBridge2.GetDotnetVersion()

both instances actually get Version 4.0 instances of the .NET Runtime. The results from GetDotnetVersion() in both cases returns:

.NET Version: 4.0.30319.18033
file:///C:/WWAPPS/WC3/WWDOTNETBRIDGE.DLL

This behavior has caused some confusion to some developers, as they expect to get different versions of the runtime for each of those commands.

It bears repeating: Only one instance of the .NET Runtime will be loaded - the first instance loaded is the instance that all wwDotnetBridge code will run under.

Runtime Compatibility

It's important to understand that .NET 2.0 components (that is .NET 2, 3 and 3.5 components all of which use the .NET 2.0 Runtime) are forward compatible and can run in .NET 4.0. You can easily load .NET 1.x and 2.0 components in a .NET 4.0 Runtime instance. The reverse however is not true even if the .NET 4.0 compiled component only uses .NET 2.0 code. .NET 4.0 compiled assemblies will not load in .NET 2.0 Runtimes.

SideEffects

As you might expect, this behavior can potentially be confusing or cause some problems if you're not careful about which Runtime gets loaded first. Essentially you need to ensure that the highest version of .NET that you expect to use gets loaded first, so that all other components will then also use this highest version.

If you have a component that expects Version 4.0, but somewhere along the line the .NET 2.0 Runtime was loaded (with the "V2" switch explicitly set), the version 4.0 component will not be able to load.

This can be especially tricky if you use .NET as part of reusable components that internally load up instances of wwDotnetBridge. Examples of this in Web Connection and Client Tools are wwSmtp and wwJsonSerializer, both of which load up wwDotnetBridge internally where you can't control the .NET runtime version.

Internally these components use GetwwDotnetBridge() - which is a helper function in wwdotnetbridge.prg - that provides a cached instance of wwdotnetbridge and tries to load the highest version of .NET installed on the machine. This function scours the the .NET install folder and tries to find the latest version of .NET installed and then uses that to load the .NET runtime. Using GetwwDotnetBridge() is a good idea for your own applications as it is a simple way to minimize load time for wwdotnetbridge and share a single instance of wwdotnetbridge.

In most cases GetwwDotnetBridge() does the right thing by finding the highest version installed and using it. For this reason we recommend that you use this function - especially if you expose wwDotnetBridge in other components where the calling application may not be able to explicitly set the .NET version. By using GetwwDotnetBridge everything uses the same logic to find the same runtime version which should ensure there's no confusion over which version is used.

But using this helper does not by itself guarantee that you get the right runtime - it only guarantees you get the latest version. But it won't prevent problems if somewhere in your application another component or your own code explicitly creates wwDotnetBridge with another explicit .NET Runtime version, before your call to load the runtime.

Making sure you load the right .NET Runtime

Ok - so getting the right version can potentially suck, especially when you might have multiple components that also load wwdotnetbridge!

However, there's a simple trick you can use to make sure your application always gets the right version of the runtime and under your control and not some random component's:

In your applications startup code force to load wwDotnetBridge. Do it as part of the Application's initialization code and simply do:

loBridge = CreateObject("wwDotNetBridge","V4")

to force the .NET 4 runtime, or:

loBridge = CreateObject("wwDotNetBridge","V2")

to force use of the .NET 2 runtime.

You should specify the highest runtime that any of your application's .NET Interop requires. So if you'll have any .NET 4 components in your app, make sure you specify "V4". Otherwise specify "V2".

This works because the .NET Runtime loads only once - any other component that explicitly requests to load with V2 after you've specified V4 still gets the V4 .NET Runtime.

Summary

wwDotnetBridge can only load a single version of the .NET Runtime and the first load wins - all subsequent loads will use the same runtime. To make sure you don't get a version too low because one component explicitly loads a lower version, take proactive steps and explicitly create an instance of wwDotnetBridge right at application startup with the highest version of .NET that you expect to use. Since .NET is backward compatible old components built for an older version will run, while your latest and greatest components can take advantage of the newest .NET version.

Get to it!


Using IIS Express with West Wind Web Connection


No comments
Monday, December 10, 2012, 11:42:00 PM

One of the complaints that I hear a lot of when it comes to Web development on Windows is that IIS configuration is difficult, especially on a local development machine. IIS is a pretty hefty application that doesn't install in Windows by default so there's a bit of effort involved in setting up IIS and adding the appropriate options to it. In Web Connection 5.65 and later we've added direct support for IIS Express as part of the installation process.

A couple of years ago Microsoft released IIS Express, which is a standalone, installable version of IIS, that's very compact and can be run from the Windows Command Line. Unlike the Cassini/Visual Studio Web Server that came before it, IIS Express is 95% feature compatible with the full version of IIS and is based on the same code base. It includes all the core functionality including features like most of the security protocols (Windows Auth, Basic Auth) and support for ISAPI extensions. But most importantly IIS Express can run without Administrative rights and it doesn't require extensive installation or configuration.

Here are some of the features of IIS Express:

  • Doesn't require a full installation of IIS.
  • Can run without the need for administrative privileges.
  • Is manually launched and shut down via Command Line - no Service running.
  • Doesn't expose any remote connections by default.
  • Runs on all Windows XP and later versions of Windows including Home and Starter editions.
  • Is a full featured implementation of the full IIS Server functionality.
  • Supports ISAPI operation, Windows and Basic Security (unlike the Cassini server)
  • Is a small downloadable package that installs quickly (<5 megs)
  • Works with Web Connection with the .NET Handler (WebConnectionModule.dll) and classic ISAPI (wc.dll)

In essence IIS Express includes all the features required to run just about any Web application, including Web Connection applications from a small standalone server.

Getting IIS Express

IIS Express is a simple and small downloadable package you can grab from Microsoft from this URL:

IIS Express 7.5 (Windows XP,2003)
http://www.microsoft.com/en-us/download/details.aspx?id=1038

IIS Express 8.0 (Windows Vista, 7, 2008, 8)
http://www.microsoft.com/en-us/download/details.aspx?id=34679

Additionally IIS Express also requires .NET 4.0. If you don't have it already installed (Windows 8 ships with it and many application use .NET 4.0). A typical install is about a 25 meg download. The Web Installer checks what's on your system and downloads only what it needs and it doesn't require a reboot.

NET 4.0 Download
http://www.microsoft.com/en-us/download/details.aspx?id=17851

Using IIS Express with Web Connection

IIS Express is a standalone executable that can be launched from the Windows command line. Web Connection includes some helpers that let you launch directly it from within FoxPro. The easiest way is from the Web Connection menu:

DO WCSTART

to start the Web Connection menu in the Visual FoxPro IDE. Then click on Web Connection | Start IIS Express Standalone Web Server:

WebConnectionMenu

This brings up a dialog that lets you select a path and port for the server:

IISExpressDialog

You can also launch IIS Express from FoxPro via a Web Connection Console command programmatically:

DO console WITH "LAUNCHIISEXPRESS","c:\westwind\wconnect",8080

This means you can easily launch IIS Express under program control to point at your site, maybe on startup of your app when in debug mode or via some other metric (environment reset etc).

Once launched you can then access your Web site simply via this Url:

http://localhost:8080/

or to access a specific page:

http://localhost:8080/testpage.wwd

Note that IIS Express always launches as a root site, rather than in a virtual directory. So if you were testing a site like http://localhost/wconnect/default.htm in IIS before the new URL now will be a root site url at http://localhost:8080/default.htm. If you are using script map extensions and relative paths in your applications as you always should, this shouldn't affect your site's operation in any way. If it does cause problems, it's a good indicator that your URLs could use some adjustment to make them more portable and always work with relative paths (hint, hint).

Here's another tip: If you don't have any Web Server installed on your machine at all, you can just use port 80 for your site, which then lets you omit the port number on the URL. So your URL with port 80 simply becomes:

http://localhost/testpage.wwd

which is a little cleaner and easier to remember.

Configuration for IIS Express in Web Connection

When running IIS Express you'll want to use the .NET Handler for configuration. This is to ensure that your script installations are portable and can work out of any folder even if your app moves.

If you install Web Connection from scratch, or you use any of the Wizards to create a new Application, Process class or the Server Configuration Wizard, Web Connection offers a pre-configured option for IIS Express:

IIS Express Configuration

This option automatically configures the site for using the .NET Managed Handler for any script maps that are created. Specifically it creates entries like this in the web.config file:

<configuration>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <handlers>
      <!-- Web Connection Module in IIS 7 in Integrated Mode -->
      <add name=".wc_wconnect-module" path="*.wc" verb="*" 
type="Westwind.WebConnection.WebConnectionHandler,WebConnectionModule" preCondition="integratedMode" /> <add name=".wcsx_wconnect-module" path="*.wcsx" verb="*"
type="Westwind.WebConnection.WebConnectionHandler,WebConnectionModule" preCondition="integratedMode" />
… </handlers> </system.webServer> </configuration>

These are mappings to the Web Connection .NET Handler that are portable and allow the scriptmaps to be valid even if the site is moved to a new location on disk or on another machine. IIS 7 and later uses .NET natively so using the .NET handler is very efficient and additionally provides many performance and administration enhancements over the classic ISAPI module. When running IIS 7 and later in general we recommend using the .NET handler.

Administration - where does the Configuration come from?

IIS Express is a local Web Server and gets all of its settings from configuration files stored in your user profile, rather than in any system store that might require administrative rights. When launching IIS Express from Web Connection as described above, Web Connection uses an Application.config template from <WWWC install folder>\Visual Studio\IIS Express\ApplicationHost.config. This configuration file holds the site configuration for the IIS Express instance. This template is customized with the path and port specified and then written out to your temp folder as IISExpress_WebConnection_Application.config.

Once the server is running you can find all running instances of IIS Express in your Windows Task Tray:

TaskTray

You can right-click on the IIS Express icon and then get a list of all the running IIS Express instances (although you should run only one for Web Connection!):

IISExpressInstances

This shows the path and port and start url for the IIS Express instance and also the location of the temporary applicationhost.config file that's actually used. Remember that this file is temporary and created each time Web Connection launches an IIS Express instance, so if you need to make changes to theconfiguration, make it in the file located in the \VisualStudio\IIS Express\ApplicationHost.config, which is then applied to all IIS Express instances launched afterwards.

Shutting down IIS Express

As you saw in the previous image you can shut down IIS Express via the Stop All button. You can also right click any of the sites and click on Stop to shut each down individually. Once shut down they are gone and need to be explicitly restarted manually via the start options shown earlier.

IIS Express vs full IIS?

IIS Express is a nice addition to the supported Web Servers in Web Connection. I'm only sorry I waited so long to add this functionality until now - this could have been available at least a year ago. All of this is coming as part of an effort to revamp the configuration and deployment features of Web Connection which have been worked on in the last two versions of Web Connection. Web Connection 5.64 and 5.65 have added much better support for the Web Connection Module (which is recommended for IIS Express) and support for root Web site configuration. You will see more of these types of improvements in the future…

Does this mean you should not use full IIS on your machine to develop Web Connection application? Perhaps - IIS Express certainly makes getting up and running much easier. But - personally I still like to run with a full version of IIS and I recommend if you have access to IIS and can run in an administrative environment, that at some point at least you install and test your application in a full version of IIS. This lets you see operation in the full IIS version as well administer the server using the IIS Administration tools that you will also see on the server.

The good news is that if you configure a site to run with IIS Express moving to a full version of IIS is pretty easy - mainly you'll need to configure the Web Site and/or virtual directory for operation with IIS. This step can be automated with the Web Connection Server Configuration Wizard or you can manually do it.


SouthwestFox Sessions Slides, Samples and Links


No comments
Friday, November 16, 2012, 1:32:00 AM

Finally had some time to take a breather to package up the materials from the Southwest Fox Conference last month. The conference was a lot of fun - good to see so many old developer friends again even if it's such a small group that's left - sigh. In any case, I did two sessions on .NET Interop with FoxPro and .NET which is probably not the most popular topic there is, but I thought it was worthwhile to do these in light of changes since I touched on these topics originally many years in the past.

In particular, .NET 4.0 simplifies many aspects of COM Interop. For ASP.NET especially the dynamic features of C# make it much easier to access COM Components without all the type library import problems that plagued older versions, where FoxPro's screwed up type library exporter that lacks support for object hierarchies and proper naming without major headaches.

The other change is use of wwDotnetBridge - which is now free and open sourced - that makes it much easier to access just about any .NET component directly from FoxPro without having to create an intermediate .NET assembly first. This opens up a lot of new reach for FoxPro in interacting with new technologies that are not easily available otherwise. Just yesterday I spent a few hours re-working the JSON serialization features in Web Connection and the Internet and Client Tools to use a reliable .NET library instead of the slow and inefficient logic that can be used in FoxPro with much faster and much more reliable plug-in replacement. There's so much that can be done to enhance FoxPro's features and reach that I hope more people will take a look at this to extend the life of their Web apps or to start dabbling with .NET without giving up FoxPro altogether. I only wish I had made this library open sourced a bit earlier…

Anyway… here are the two session white papers and slides and samples which are linked from them:

Calling .NET Components from Visual FoxPro using wwDotnetBridge

.NET is here to stay, and you can take advantage of the rich functionality in the .NET framework from Visual FoxPro. You can access code in the .NET framework as well as Microsoft, third party and your own .NET libraries. This article expands on my previous COM Interop articles and introduces the open source wwDotnetBridge library that lets you instantiate and interact with most .NET types directly from Visual FoxPro code. It's a great way to extend Visual FoxPro's reach as well as allowing FoxPro developers to create their own .NET components that can be interacted with from FoxPro.

Resources:

Calling FoxPro COM Components from ASP.NET Revisited

If you need to call FoxPro COM components from ASP.NET recent changes in .NET 4.0 have made this process a bit easier. This article expands on how to create FoxPro COM components in .NET and ASP.NET in particular by using more modern technologies like ASP.NET MVC and Web Services to call FoxPro COM Components taking advantage of the Dynamic language improvements in .NET that make it much easier to consume FoxPro COM components.

Resources:

Enjoy.


wwDotnetBridge for FoxPro .NET Interop is now Free and Open Source


3 comments
Thursday, September 27, 2012, 10:33:00 PM

I'm happy to announce that as of a couple of days ago, wwDotnet Bridge - our library to access .NET components from Visual FoxPro code - is now free and open source. You can find out more wwDotnetBridge here:

What is wwDotnetBridge?

wwDotnetBridge is a library that makes it easy for FoxPro to access .NET code. While there's a native mechanism available to call .NET components via .NET COM Interop, that mechanism is woefully limited to components that are actually registered to COM, which is very few components, except those you create yourself. wwDotnetBridge provides an easy mechanism to access just about any .NET component regardless of whether it's registered to COM or not and provides access to methods and classes that native COM Interop can't handle directly.

wwDotnetBridge provides a custom .NET Runtime host that is loaded into Visual Foxpro. This custom host allows instantiating of .NET Components without requiring COM registration as it manages the instantiation of the runtime and providing a proxy into the .NET framework. wwDotnetBridge still uses COM Interop - the objects you interact with are still COM objects and exhibit the same behaviors as objects used with native COM Interop, but the instantiation process is different going through wwDotnetBridge.

To create custom in wwDotnetBridge you use syntax like this:

loBridge = CreateObject("wwDotNetBridge","V4") 
loBridge.LoadAssembly("InteropExamples.dll")
loFox = loBridge.CreateInstance("InteropExamples.Examples")

So instead of CREATEOBJECT("ComProgId") using wwDotnetBridge involves calling loBridge.CreateInstance("dotnetnamespace.dotnetclass"). Once instantiated the object behaves the same way as one returned from native COM Interop. CreateInstance also allows instantiation .NET classes that have parameterized constructors - the parameters can be passed after the type name of CreateInstance.

Once the wwDotnetBridge instance has been instantiated and any required assemblies have been loaded a bunch of additional functionality becomes available to access on .NET components. One of the most common things you might do with the native classes in the .NET runtime is to access static methods.

For example the following uses the .NET EventLog component to write an entry into the Windows Event Log:

loBridge = CreateObject("wwDotNetBridge","V4")
 
lcSource = "FoxProEvents"
lcLogType = "Application"
 
IF !loBridge.Invokestaticmethod("System.Diagnostics.EventLog",;
                                "SourceExists","FoxProEvents")
    loBridge.Invokestaticmethod("System.Diagnostics.EventLog",;
                                "CreateEventSource",;
                                "FoxProEvents","Application")
ENDIF
 
*** Write out default message - Information
* public static void WriteEntry(string source, string message)
loBridge.Invokestaticmethod("System.Diagnostics.EventLog",;
                            "WriteEntry",lcSource,;
                            "Logging from FoxPro " + TRANSFORM(DATETIME()) )  

This sort of thing was not possible directly with native .NET COM Interop since it only works with types exported to COM. Without wwDotnetBridge, using COM Interop often involved writing a custom .NET component that performed a few tasks like the above and then calling that component from FoxPro via COM interop. With wwDotnetBridge many of these simple tasks can be accomplished directly from within Visual FoxPro code, not requiring you to create any code in .NET. Not that it's a bad idea to create an intermediary .NET assembly for truly complex tasks - sometimes that can be vastly easier to write a little bit of .NET code IN .NET rather than trying to access it all from FoxPro, but for many things that are built into the .NET framework that are abstracted enough it's simply no longer necessary to have to create a separate component.

I don't want to show too many examples here since I covered a ton of them in the wwDotnetBridge White Paper. If you want to see more examples of what wwDotnetBridge can do and how it improves upon native COM Interop the white paper is a great place to start.

Why now?

wwDotnetBridge has been around for quite a while since 2007 and has been a part of West Wind Web Connection and West Wind Client Tools ever since. However, it hasn't exactly gotten the attention I thought it would get,  primarily because it's been buried inside the many fold functionality of these two very rich products. I'm hoping by releasing it as a free standalone component it will show up on more people's radar as they need to expand the reach of their FoxPro applications into new functionality available through .NET.

I created wwDotnetBridge because I had several applications that needed to interface with .NET functionality and the COM registration issues were causing me major issues during installation on some machines. Additionally I continually ran into issues with not being able to access certain .NET objects and members from FoxPro code. After some thought I decided to create an interop library that would help with proxying values back and forth between .NET and FoxPro. What started as a small support library quickly turned into a comprehensive set of components that can fix up many common problem types that don't marshal from .NET FoxPro and back and the end result was wwDotnetBridge. Since then I've been using wwDotnetBridge for all of my COM Interop code from FoxPro.

One product in particular - the West Wind Web Service Proxy Generator for Visual FoxPro - relies heavily on the features of wwDotnetBridge and has also been a source of many, many improvements to the library as it generated a huge variety of different usage scenarios for result .NET types. Whenever there have been problems with specific types I've been able to either find workarounds to access them in FoxPro or - more often than not - provided helper objects that can automatically handle these problem types.

In any case, I've found wwDotnetBridge immensely useful in my FoxPro work, and I'm hoping it'll be useful to some of you as well. If you plan on using .NET functionality in your FoxPro applications, check out wwDotnetBridge - it'll make life a lot easier.


Custom Manifest Files in Visual FoxPro EXEs


8 comments
Saturday, August 25, 2012, 2:46:00 AM

Here's a something I didn't know about Visual FoxPro: If you place a Windows Manifest file in the same folder as the FoxPro project you are compiling, you can embed that manifest into the compiled EXE. By default FoxPro will generate it's own manifest file - one problem with this is that if an EXE has an embedded manifest file external manifest files are ignored. So effectively that means that external manifest files are not executed by FoxPro - only the internal compiled in one is.

All you have to do is to create a valid manifest file with the same name as the output EXE and then put that manifest file into the same folder as the PJX file.

So if I have an EXE called:

DotNetWsdlGeneratorConsole.exe

I have to create a matching manifest file in the Project's folder:

DotNetWsdlGeneratorConsole.exe.manifest

When you compile and now use any sort of resource editor you can take a look at the generated manifest in the FoxPro EXE. Cool, eh?

What can I use a Manifest For?

Manifest files are useful for a number of things. They can tell the OS under which security context to load, add self-registering COM components and a host of other things. It often also contains information on how to render themes under Windows XP (I'll get back to that in a minute).

Let's talk about two things that are quite common requests in the FoxPro Community:

Force your application to run in administrative mode

If you're running in Windows Vista, 7 or 8, User Account Control forces all users to run as Standard users even if you are effectively marked as an Administrator. When UAC is on, applications that require Administrative rights should always prompt for this on startup explicitly. If you need admin rights you will be automatically prompted. The only way that this can be done is via a manifest file.

If you don't do this, and you don't have the appropriate rights your app will start but fail on required administrative operations, which is problematic - it's better to notify the user on startup to elevate the rights of the Application.

I need to do this for the West Wind Web Service Proxy Generator tool, the Wizard of which lives in an EXE called:

DotNetWsdlGeneratorConsole.exe

So - in the project directory - I create a manifest file:

DotNetWsdlGeneratorConsole.manifest.exe

that looks like this:

<?xml version="1.0" encoding="utf-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
          manifestVersion="1.0">
  <assemblyIdentity name="DotNetWsdlGeneratorX.exe"
                    version="1.0.0.0"
                    processorArchitecture="x86"
                    type="win32" 
                    />
    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
      <security>
        <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
          <requestedExecutionLevel level="requireAdministrator"
                                   uiAccess="false" />
        </requestedPrivileges>
      </security>
    </trustInfo>

</assembly>

These settings force ask for Administrator rights when launching and should force the app to pop up a UAC dialog that asks for permission to run this application.

  • Now build the EXE - in my case I build it into a separate folder (the manifest file is not required).
  • And run the EXE from Explorer when UAC is on

When I do I see:

UacDialog

Unfortunately, notice the Publisher Unknown - this because my EXE isn't properly signed, which is OK in my case, but if you need it here is some background information on how to sign an EXE:

http://www.wintellect.com/cs/blogs/jrobbins/archive/2007/12/21/code-signing-it-s-cheaper-and-easier-than-you-thought.aspx

Registrationless COM Activation

Another common task for manifest files is registrationless COM, which allows you to define each COM object you need access to and 'register' it inside of the manifest file. It's very easy to do this with code like the following:

<?xml version="1.0" encoding="utf-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity name="DotNetWsdlGeneratorConsole.exe"
                    version="1.0.0.0"
                    processorArchitecture="x86"
                    type="win32" 
                    />
  <file name="multithreadserver.dll">
  <comClass clsid="{af2c2811-0657-4264-a1f5-06d033a969ff}"
              threadingModel="Apartment"
              progid="multithread.multithreadserver"
              description="multithread.multithreadserver" />
  </file>

  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
        <requestedExecutionLevel level="requireAdministrator"
                                 uiAccess="false" />
      </requestedPrivileges>
    </security>
  </trustInfo>

</assembly>

Notice the file and comClass elements in the configuration. The file describes the file name (in the same folder) and comClass describes the clsid and progid and threading model for the DLL to be loaded. You can have multiple comClass elements for multiple COM objects contained with in the one physical file on disk.

FoxPro's Default Manifest

It's interesting to take a look and see what a FoxPro EXE's manifest actually looks like. If you have Visual Studio installed (or any other tool that can extract and view Embedded Resources) you can take a look at the EXE file.

To do this:

  1. Open Visual Studio
  2. File | Open | File and pick your compiled EXE file

If you do this Visual Studio opens the Resource editor for the EXE since manifests are embedded as resources. Here's what it looks like in VS2012:

ResourcesVs

If you drill into the '1' file - the manifest you get a split binary/text view of the data. You can cut and paste the text into a new File | New | Xml File window to view the manifest a little bit easier.

The resulting XML looks like this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
          manifestVersion="1.0">
  <assemblyIdentity
    version="1.0.0.0"
    type="win32"
    name="Microsoft.VisualFoxPro"
    processorArchitecture="x86"
/>
  <description>Visual FoxPro</description>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level="asInvoker" />
      </requestedPrivileges>
    </security>
  </trustInfo>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity
        type="win32"
        name="Microsoft.Windows.Common-Controls"
        version="6.0.0.0"
        language="*"
        processorArchitecture="x86"
        publicKeyToken="6595b64144ccf1df"
        />
    </dependentAssembly>
  </dependency>
</assembly>

 

Note that FoxPro automatically adds the requestedPrivileges attribute, but it's defaulted to 'asInvoker' which means it runs in the default system context of the user (which is the default for Windows). In effect this is not necessary, but for whatever reason the FoxPro developers thought this should be there.

The default manifest also has a dependency on the Windows Common controls assembly which forces FoxPro to uses the latest version of the common controls. This is important on XP, because it allows FoxPro to use themes on XP, which without this entry would not work.

So, for this reason, it's important that you add that last dependency into your custom manifest files as well.

So, my complete custom manifest file now looks like this:

<?xml version="1.0" encoding="utf-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
          manifestVersion="1.0">
  <assemblyIdentity name="DotNetWsdlGenerator.exe"
                    version="1.0.0.0"
                    processorArchitecture="x86"
                    type="win32"
                    />

  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
        <requestedExecutionLevel level="requireAdministrator"
                                 uiAccess="false" />
      </requestedPrivileges>
    </security>
  </trustInfo>

  <file name="multithreadserver.dll">
    <comClass clsid="{af2c2811-0657-4264-a1f5-06d033a969ff}"
              threadingModel="Apartment"
              progid="multithread.multithreadserver"
              description="multithread.multithreadserver" />
  </file>

  <dependency>
    <dependentAssembly>
      <assemblyIdentity
        type="win32"
        name="Microsoft.Windows.Common-Controls"
        version="6.0.0.0"
        language="*"
        processorArchitecture="x86"
        publicKeyToken="6595b64144ccf1df"
        />
    </dependentAssembly>
  </dependency>

</assembly>

and if I build my EXE and put the manifest file into the same folder as the project file, I get this exact manifest embedded into my FoxPro project.

Hope some of you find this useful - I know I have as I have a couple of administrative apps that simply work better with admin rights enabled, instead of having to write instructions somewhere that "you have to run this application with administrative rights." Sweet.


UTF-8 Encoding with West Wind Web Connection


2 comments
Monday, August 13, 2012, 4:45:57 PM

For Web applications UTF-8 encoding has become fairly universal. According to WikiPedia:

"UTF-8 (UCS Transformation Format—8-bit[1]) is a variable-width encoding that can represent every character in the Unicode character set. It was designed for backward compatibility with ASCII and to avoid the complications of endianness and byte order marks in UTF-16 and UTF-32"

As FoxPro developers stuck with 255 character wide character sets, UTF-8 allows us to represent Unicode output fairly easily, especially since version 8 when STRCONV() introduced easy UTF-8 conversions to and from UTF-8, Unicode and ANSI character sets, making it super easy to create and parse UTF-8 into the current active character set.

UTF-8 solves a lot of problems with character set display issues on the Web especially and I'd highly recommend that you use UTF-8 as your output format for Web content from Web Connection or otherwise. Although early versions of Web Connection didn't do anything special with extended character sets and UTF-8 , more recent versions make it fairly easy to deal create content in UTF-8 format and parse UTF-8 Request data (form vars and query strings) just about automatically.

Setting up UTF-8 Encoding and Decoding in Web Connection

Starting with Web Connection 5.0, there are properties on both the Request and Response objects that allow UTF-8 transformations automatically. By default these aren't enabled so as to not break existing code, although enabling them is unlikely to cause a problem unless you explicitly set character set encoding via meta tags and actually encode your content already.

It's almost trivial to enable UTF-8 processing in Web Connection for all requests routed through a given Process class with this code:

************************************************************************
* wwDemo :: OnProcessInit
***************************
FUNCTION OnProcessInit
 
*** Explicitly specify UTF-8 encoding and decoding
Response.Encoding = "UTF8" 
Request.lUtf8Encoding = .t. ENDFUNC * wwDemo :: OnProcessInit

These two innocuous looking property assignments tell Web Connection to:

  • Encode all output going through the Response object to UTF-8
  • Decode all input from Form Variables and QueryStrings to use UTF-8 Decoding

Note that both of these require Web Connection 5.x and later and Response.Encoding is available only on the wwPageResponse class, which is the default  in Web Connection 5. It is not available for the older wwResponse/wwResponseFile/wwResponseString classes.

The above code to enable UTF-8 encoding is hooked to the wwProcess::OnProcessInit() method which if a custom implementation you can create in your custom wwProcess subclasses. This method is a per request hook that allows hooking tasks that need to fire on every request. Typically you set up things things like Session initialization (this.InitSession()) or checking authentication etc. Setting the request encoding is just another simple task. Once the properties are set on the Response and Request object all Response output and Request input is automatically UTF-8 parsed.

Response Encoding

The Response encoding works on the wwPageResponse class only which is a string based output generation mechanism. When UTF-8 encoding is enabled your code basically builds up the Response into a string throughout your request code. Whether you explicitly call Response.Write() or other low level Response method, or whether a more high level handler like the Web Control Framework or the script and template engines create the output doesn't really matter - it all ends up as a string on the Response.cOutput property.

Once the process method is complete Web Connection assembles the final Response output by combining the Response.cOutput string plus the request headers into a complete response, which is then UTF8 encoded and returned back to IIS via the Web Connection .NET Handler or the ISAPI module.

The result is a fully UTF8 encoded response that properly displays any upper ASCII characters.

While it's possible to send extended characters back without UTF-8, it's much more complex for clients - especially non-Web Browser clients - to deal with custom character sets. The server would have to specify which character set was used (such as Windows-1252) and browsers have to parse and decode the charset. UTF-8 simplifies this because UTF-8 is fairly easy to automatically map to the active character set in the client's OS. If you received an raw UTF-8 response in FoxPro (say by calling a UTF-8 URL with wwHTTP) it's as easy as calling STRCONV(lcOutput,11) to turn it into FoxPro usable ANSI characters which is much easier than trying to match a specific encoding type and character set.

UTF-8 makes it much easier to share text data spanning potentially many different character sets using a single encoding mechanism. This is why it's a good idea to always create Web output using UTF-8, rather than any other encoding.

Request Encoding

If you use UTF-8 Response encoding you will actually need to match it with UTF-8 Request Decoding. Why? Because if you embed a URL like this in a document:

http://localhost/wconnect/testpage.wwd?address=TamStraße

you'll find that this URL is turned into a UTF-8 encoded URL that looks like this by the browser when clicked:

http://localhost/wconnect/testpage.wwd?address=TamStra%C3%9Fee

Notice that there are TWO escaped values next to each other for the ß character: %C3%9F which is the UTF-8 encoded character. Why? Well, your document is UTF-8 encoded and so the URL sent to the server also is. The same goes for form data you enter into a form. In other words, the Request data is UTF-8 encoded.

In order to properly decode those UTF-8 values you need to use:

Request.lUtf8Encoding = .t.

or else Request.Form() or Request.QueryString() will return weird looking characters for any extended characters in strings. For example if I type:

TamStraße

into a textbox and retrieve the value when lUtf8Encoding = .F. I'll get:

TamStraße

which is basically the UTF-8 encoded version which is clearly not what you want. You can manually fix this easily enough:

? STRCONV("TamStraße",11)

which properly produces TamStraße, but the easier solution is to just set Request.lUtf8Encoding = .T. and have this happen automatically.

Note that UTF8 encoding is common in the browser and for most Web pages it's considered the default if no other encoding is specified. One big issue with character encoding is that the server doesn't always receive information on what encoding is used. In fact most Form posts don't specify the encoding so you'd have to guess. But since in most applications you control the page generation (ie. you generate the page that posts back) you know what the encoding of the parent page is which in turn determines the POST encoding and querystring encoding for embedded links.

The Moral of the Story is: Use UTF-8

If you run into any problems with character encoding in your Web Connection applications, the most likely culprit is that you forgot to properly encode your content. If this happens to you the easiest way to fix it almost always is to opt to output everything in UTF-8. If you're dealing with any extended character formats, or even multi-cultural applications, UTF-8 will always work. Whether FoxPro can map all characters received from the Web to the current character set - that is another issue altogether, but that's a tricky limitation of FoxPro that has no easy solutions short of switching character sets at runtime.


Extensionless Urls with Web Connection and the IIS UrlRewrite Module


No comments
Thursday, August 02, 2012, 12:02:12 AM

In the last few months I've gotten a lot of requests from people who want to use extensionless URLs in their Web Connection applications. Extensionless URLs are URLs that - as the name implies - don't have an extension, but rather terminate in what looks like a directory. For example, you might expose customer data with formats like these:

http://localhost/wconnect/customers
http://localhost/wconnect/customers/32131

As you can see the URLs here don't appear to be ending in a 'file name' like index.htm or MyPage.wcsx etc. Rather they end in what looks more like a directory path. While it's mostly semantics, there's a lot of thought on the Web surrounding 'clean urls' like the above, that better describe Web resources. Extensionless Urls tend to use the URL path rather to describe the 'routing' and parameters of a request rather than more traditional approaches using script file links coupled with query strings; although querystrings are supported and often necessary still even on extensionless Urls. When using extensionless URLs we often think about 'nouns' or 'resources' (ie. Customers) rather than actions (GetCustomer.wc or GetCustomerList.wc).

Long story short, extensionless URLs are not something that have been natively supported in Web Connection in the past. There are a number of ways that this can be accomplished - here are a couple:

  • Wildcard ScriptMaps
    Wildcard scriptmaps essentially allow you to route EVERY request the Web server receives to a specific extension like wc.dll or the .NET managed handler. You then need to override the default Web Connection processing to handle the incoming URLS.

  • The UrlRewrite Module for IIS7+
    IIS 7 provides for a downloadable UrlRewrite module that can be plugged into IIS and that provides a rules based engine that can either rewrite or redirect URLs. Url rewriting basically takes an incoming URL that matches and rewrites it as another - in the process changing all the path based IIS request data (like LogicalPath,PhysicalPath,ScriptName etc.), while keeping the other request data like QueryString() and Form() intact. Unlike a Redirect operation, a rewrite only changes the URL your application sees to the new URL. This process is better suited for Web Connection because it allows some control over what URLs specifically are routed to Web Connection.

For Web Connection applications Wildcard ScriptMaps are generally NOT a good idea because Wildcard scriptmaps map EVERYTHING to the Web Connection server including images, css, scripts, static HTML etc. and Web Connection (and any dynamic content engine really) has quite a bit of overhead compared to the static file services and the caching that IIS natively provides. This is really not a good match for Web Connection.

UrlRewrite on the other hand allows you to specifically create a rule that filters URLs and sends matches (or non-matches in our case) to another URL by 'rewriting' the URL - that is the paths are updated to reflect the new URL while the browser's address bar retains the original URL. This is much more efficient for Web Connection because it'll only get hit by requests that match the search criteria. The UrlRewrite approach is what I'll discuss in this post.

The IIS UrlRewrite Module

You can download the URL Rewrite Module for IIS7 and later by using the Microsoft Web Platform Installer. Find the UrlRewrite Module in the Products | Server and select Url Rewrite 2.0:

 WebPlatformInstaller

and install from there. The installer does all the work of pulling the module and any dependencies.

Once the module is installed you can enable it in your  Web Connection virtual or root directory by creating a rewrite rule. As most IIS 7 settings, rewrite rules are stored in web.config. Here's a rule that rewrites extensionless URLs:

    <system.webServer>
         <rewrite>
            <rules>
                <rule name="ExtensionLessUrls" patternSyntax="Wildcard" stopProcessing="true">
                    <match url="*.*" negate="true" />
                    <conditions>
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="UrlRewriteHandler.wwd" appendQueryString="true" />
                </rule>
            </rules>
        </rewrite>        
    </system.webServer>

The above is pretty much cut and paste - you only need to change the Rewrite action url to ensure it points to a valid URL for your virtual directory.

Alternately you can also enter rewrite rules from the IIS Management Console:

RewriteIISManagementConsole

What this rule says basically is this:

  • Don't match files that have a . in the name (ie. no extension)
  • Match files that are not a physical directory
  • Match files that are not a physical file
  • If true Rewrite the URL to UrlRewriteHandler.wwd and forward the query string data from the original url

In this case I specified that I want any extensionless URL to be re-written to UrlRewriteHandler.wwd which points at my wwDemo process class example which has a .wwd extension.

Note that this uses WildCard pattern matching - you can also use RegEx expressions matching instead. Here I look for a . in the URL, which will work as long as none of your paths in your site contain '.'.

Creating a FoxPro Handler for Extensionless Urls - UrlRewriteHandler

Prior to Web Connection 5.64 UrlRewriteHandler does not exist but as of 5.64 there are a couple of special handler methods in the wwProcess that can act as an endpoint for extensionless URLs. Basically when you create the Rewrite route you can point it at UrlRewriteHandler.wwd (use your own Process Class scriptmap here instead of wwd) and off you go.

If you're using a version prior to 5.64 you can implement UrlRewriteHandler and OnUrlRewrite like this either on your wwProcess subclass or on wwProcess itself as is the case for 5.64 and later:

************************************************************************
* wwProcess ::  UrlRewriteHandler
****************************************
***  Function: Handler called when a UrlRewrite occurs via 
***            Specify UrlRewriteHandler.wwd (use your extension)
***            as the endpoint for extensionless URLS and then 
***            implement OnUrlRewrite() in your process class.
************************************************************************
FUNCTION UrlRewriteHandler()
LOCAL lcOrigUrl, lnItems, loRewrite, lcQuery, lnX
LOCAL ARRAY laTokens[1]
 
*** Rewrite URL injects the original URL as a Server Variable
lcOrigUrl = Request.ServerVariables("HTTP_X_ORIGINAL_URL")
IF EMPTY(lcOrigUrl)
   lcOrigUrl = Request.GetExtraHeader("HTTP_X_ORIGINAL_URL")
   IF EMPTY(lcOrigUrl)
       *** or pull it off the querystring
       lcOrigUrl = Request.QueryString("url")
   ENDIF
ENDIF
 
*** Create result object
loRewrite = CREATEOBJECT("Empty")
ADDPROPERTY(loRewrite,"cOriginalUrl",lcOrigUrl)
 
*** Split path and query string
lnItems = ALINES(laTokens,lcOrigUrl,1 + 4,"?")
IF lnItems > 0
   ADDPROPERTY(loRewrite,"cOriginalPath",laTokens[1])   
ELSE
   ADDPROPERTY(loRewrite,"cOriginalPath",lcOrigUrl)   
ENDIF
 
*** Split path into Collection
loSegments = CREATEOBJECT("wwCollection")
lcVirtual = STRTRAN(LOWER(Request.GetVirtualPath()),"/","")
 
lnItems = ALINES(laTokens,loRewrite.cOriginalPath,1 + 4,"/")
FOR lnX = 1 TO lnItems
    lcToken = laTokens[lnX]
    IF !(LOWER(lcToken) == lcVirtual)
        loSegments.Add(lcToken)
    ENDIF
ENDFOR
 
ADDPROPERTY(loRewrite,"oPathSegments",loSegments)
 
THIS.OnUrlRewrite(loRewrite)   
 
ENDFUNC
*  wwProcess ::  UrlRewriteHandler

The RewriteUrl method acts as the endpoint for a URL like UrlRewriteHandler.wwd which should be plugged into the rewrite rule. When an extensionless URL is fired it then fires this method which in turn picks up the original URL via the HTTP_X_ORIGINAL_URL header that IIS injects into the Request data. This header contains the original URL which then method then forwards along with a couple of parsed variables, to the OnUrlRewrite method. The OnUrlRewrite method is a convenience method you can easily override when a hit occurs that receives this parsed object.

The loRewrite object parameter has the following properties:

Property Function
cOriginalUrl The original extensionless URL that triggered the rewrite. Full server relative path that includes the query string.
/wconnect/customers/3211?parm=val
cOriginalPath The original extensionless URL that triggered the re-write, without query string.
/wconnect/customers/3211
oPathSegments Each of the path's segments relative to the virtual directory or root in a wwCollection instance.
Two segments: customers and 3211
lcId = loRewrite.oPathSegments.Item(2)

which makes it fairly easy to do something useful with the data passed.

A simple example of an Extensionless Url Handler

Let's look at a somewhat simplistic example implementation of an Extensionless URL handler that basically routes extensionless requests to requests with a known extension.

It would allow you to effectively create a handler for your application that maps a URL like:

http://localhost/wconnect/TestPage

and route them to the TestPage method in your process class.

The implementation of such a handler can be very basic:

************************************************************************
* wwDemo ::  OnUrlRewrite
****************************************
FUNCTION OnUrlRewrite(loRewrite)

this.oRewrite = loRewrite

*** Assume second segment is our method name
IF loRewrite.oPathSegments.Count > 0
   lcMethod = loRewrite.oPathSegments.Item(1)   
   RETURN EVALUATE("THIS." + lcMethod  + "()")   
ENDIF

this.ErrorMsg("Invalid Route",;
   "Route values must at least include 1 segments relative to the virtual or root application")

ENDFUNC

Basically this code does little more than checking for the first segment (TestPage) and assuming that this segment  is the name of the method you want to call in the process class. It takes the method and then simply Evaluates the method. Since the rewrite keeps the QueryString() and Form() data intact the behavior of the method is almost the same as if you called it directly with:

http://localhost/wconnect/TestPage.wwd

There is a difference however, between a rewrite to this URL and directly accessing this URL: All the paths inside of the TestPage method on the rewrite point to UrlRewriteHandler.wwd - not the original Url or testpage.wwd because the rewrite rule points at UrlRewriteHandler.wwd. The original extensionless URL is only available as a parameter to the OnRewriteUrl(loRewrite) method. So things like Request.GetPhysicalPath(), GetLogicalPath(), GetExecutablePath(), GetCurrentUrl() etc. will point at UrlRewriteHandler.wwd, so be aware that if you simply want to map existing scriptmapped extensions to non-scriptmapped extensions there might be pathing issues.

Using multiple Path Segments

Let's expand on that last example with something a little more practical and consider creating a handler for the following two URLs which return customer information:

http://localhost/wconnect/customers

http://localhost/wconnect/customers/West+Wind+Technologies

Here I have two URLs that return a list of customers and a specific customer respectively. In order to do this, I need to make a slight change to the OnRewrite method to capture the loRewrite object and make it available to my handlers and store it in an oRewrite object:

oRewrite = NULL

************************************************************************
* wwDemo ::  OnUrlRewrite
****************************************
FUNCTION OnUrlRewrite(loRewrite)

this.oRewrite = loRewrite

*** Assume second segment is our method name
IF loRewrite.oPathSegments.Count > 0
   lcMethod = loRewrite.oPathSegments.Item(1)   
   RETURN EVALUATE("THIS." + lcMethod  + "()")   
ENDIF

this.ErrorMsg("Invalid Route",;
   "Route values must at least include 1 segments relative to the virtual or root application")

ENDFUNC

************************************************************************
*  wwDemo :: Customer
****************************************
***  Function:
***    Assume:
***      Pass:
***    Return:
************************************************************************
FUNCTION Customers()

lcId = ""
IF this.oRewrite.oPathSegments.Count > 1
    *** Retrieve second parameter segment and query
    lcId = this.oRewrite.oPathSegments.Item(2)
ENDIF

*** No id passed - display a list
IF EMPTY(lcId)
    SELECT * FROM tt_cust ;
       INTO CURSOR TQuery ;
       ORDER BY Company   
    lcHtml = HtmlDataGrid("TQuery")
ELSE
    SELECT * FROM tt_cust ;
       WHERE company = lcId ;
       INTO CURSOR TQuery
    lcHtml = HtmlRecord("TQuery")
ENDIF

THIS.Standardpage("Show Customer",lcHtml)
ENDFUNC
*   Customers

The Customers method is now implemented in such a way that it can look at the second path segment to determine the ID of the customer to look up. If no second segment exists - assume that a customer list gets displayed. If there is an ID segment - load the individual customer and display it.

Both of those URLs can now be handled fairly easily as you can see.

Note that this is just one example of how you can handle your routing. I suspect in other real-world scenarios the logic to determine which method needs to be fired in OnUrlRewrite() might be more complex especially if URL paths nest several levels deep. You can apply more complex rules in the OnRewriteUrl method to fit your needs.

Feedback Wanted

To be honest I haven't had much need to build extensionless URLs with FoxPro, so I don't have a real good idea what people are planning to accomplish with URLs. I'm pretty familiar with ASP.NET MVC style routing where you can add route parameters which is a possible future addition for Web Connection when using the Web Connection .NET Handler.

For the moment I'm curious to hear feedback on what you need to accomplish and/or whether the relatively simple implementation outlined here would address your needs or how you think it might be improved. Any feedback would be greatly welcome.


FoxPro EXE DCOM Configuration on 64 Bit Systems


2 comments
Monday, July 23, 2012, 11:09:43 PM

Visual FoxPro is a 32 bit application and when you register a VFP EXE COM server it gets registered in the 32 bit DCOM registry.  If you recall, you can create a FoxPro EXE server by creating a class and marking it as OlePublic:

DEFINE CLASS TestServer as Custom OLEPUBLIC

FUNCTION Foo()
RETURN "HELLO FOO!"

ENDDEFINE

You can then add the PRG to a FoxPro project and compile the server with:

BUILD EXE TestServer FROM TestServer

At this point you have a working DCOM server on your local machine that you built the project on. In order to register the server on another machine you'll have to explicitly register it with the following command:

TestServer.exe /regserver

Whenever you register an EXE COM server it is actually a DCOM server - meaning it can potentially be launched remotely from another machine. DCOM stands for Distributed COM but realistically any EXE server (out of process server) is essentially a DCOM server even if it's called locally - the same exact APIs are used to launch it and manage the RPC COM interface.

DCOM Configuration: Doing the Bitness Dance

If you register your EXE COM server on a 32 bit system you can easily access DCOM Configuration manager in Component Services with:

DCOMCNFG

by typing the above into the Windows Run box or typing Component Services and drilling to the DCOM Config section of Component Services. The Windows Component Manager will pop up and show you all your DCOM servers and their individual settings including (hopefully) your EXE COM server.

DCOMCNFG32[4]

Remember that by default your server will show up by the ProgId (ie. TestServer.TestServer) unless you change the server definition. If you have more than one COM server in your EXE only one will show up. For this reason it's a good idea to always add an explicit name to your server in the project's Project Info | Server's | Description property.

All this is fine and dandy on a 32 bit system. However, if you type DCOMCNFG into the Run box on a 64 bit system you will find… well, you won't actually find your server entry there. That's because when you type DCOMCNFG on a 64 bit machine you only see COM servers registered for 64 bit, which VFP COM servers are not.

To get around this you can explicitly launch the 32 bit version of Component Services with this more verbose command that you can type into the Windows Run box or command prompt:

mmc comexp.msc /32

Once you do this, you should again see your server show up in the configuration.

Why does this happen?

Windows registers 32 and 64 bit components in different places and DCOMCNFG only displays the 64 bit servers that are written into the 64 bit registry hive. VFP's COM registration code predates that and so it doesn't write some of the corresponding registry keys. DCOMCNFG works the same way for 32 and 64 bit components so any registration performed in the editor actually sets the same settings regardless of the bitness, but due to the registry differences VFP COM servers don't show up there.

If you're using West Wind Web Connection 5.60 and later, the COM registration tools actually provide proper registration for VFP servers so they do show up in the 64 and 32 bit DCOM registry. You can use the Server Configuration Wizard or the programmatic or command line COM registration tools provided with Web Connection via the Console.exe application to get this functionality.


Root Path Support with ~ in Web Connection Response Output


No comments
Saturday, May 12, 2012, 3:44:55 PM

Here's a little not so well known tip that's useful if you're using Web Connection: When output is rendered, the output automatically expand URLs that use the ~/path syntax, expanding any URLs found to fully application relative URLs on the page.

What it does is that you can write URLs like this (assuming a virtual directory of wconnect):

<a href="~/WebControls/Helloworld.wcsx">Hello World</a>

and expand that URL out to:

<a href="/wconnect/WebControls/Helloworld.wcsx">HelloWorld</a>

~/ paths basically say: Fix up the ~ to mean the base path of my Web Application. This allows you to create application rooted URLs consistently regardless of which virtual directory or root folder the URL is called from.  This different than relative paths which depend on the current location of the page that is loaded in the browser - the path generated is a Web server root relative path that was generated by the server based on the active application's path.

This means if your app runs in a virtual directory called wconnect the root path resolves to /wconnect (root folder/wconnect subfolder). If you're running in the root of the site, the root path resolves to /. Why should this matter? If you're developing applications that target different environments the base path of the Application might change. For example, you might develop your application in a virtual directory for development, but deploy the live application into a root Web, or a virtual with different names. Using ~/ urls, no changes are required when the application is moved to the live server.

Web Connection 5.0 also introduced a code based equivalent in Process.ResolveUrl():

lcUrl = Process.ResolveUrl("~/WebControls/HelloWorld.wcsx")

which accomplishes the same thing for you in code so you can use this function for creating root relative paths from your code. This is especially useful for generic and reusable code which may not know where it is running from. The Web Control framework makes extensive use of ResolveUrl for any URL properties on which it always calls ResolveUrl(). ResolveUrl() ignores full path urls if passed and returns them as-is. Urls that contain ~/ in the path are translated.

Note that this feature started out as a feature specific to the the Web Control Framework, but it since then has migrated into the core framework and the wwPageResponse/wwResponseString objects to post-process in all output generated through the Response object. This means it works for Web Control Framework Pages, raw Response.Write() output, templates and scripts etc. It's only applied to HTML output when the Content Type is set to text/html.

Url Pathing Options

Note that this ~/ pathing is different than relative pathing using ../ or rooted pathing which starts with a / on the root site. Rather the ~ syntax creates an application relative URL that is always the same regardless of where your app runs.

Whenever possible when creating URLs in your applications both in markup and code you should choose paths in this order:

  1. Relative Paths
    Use relative paths whenever possible because they're the most portable. Relative paths (../css/standard.css or css/standard.css) tend to be the most portable because as long as your resources are relatively stored to each other they will always work. Problems with this arise only if the resources are moved relative to each other which is probably rare.
  2. Virtual Paths
    Use application relative paths because they are portable per application. Virtual path specifiers (~/css/standard.css which expands to /wconnect/css/standard.css) are always rooted to the application's root folder. In Web Connection you specify this value in the server configuration via the cVirtualPath in wc.ini or web.config using the VirtualPath key. The great thing about ~/ paths are that there's no relative pathing involved if you don't know where your request might be running from. They are also super useful for Web Control Framework User Controls that might be dropped into pages running for many different subfolders.
  3. Rooted Paths
    Never use hardcoded paths to site or even rooted Webs. You should NEVER EVER use hard coded URL paths to reference anything in your local site. While this will work in whatever environment you're working in it'll break as soon as you move the site. At that point you have lots of links to clean up and that's never a good idea.

Both relative paths and virtual paths are adaptable and the big advantage of these are that they are portable. If you move the site or move to a different virtual folder you don't have to change anything (ok - you have to change the cVirtualPath config property, for ~/).

Relative paths are native to HTML and are parsed as the page is loaded. ~/ paths are expanded on the server and if you're using the wwPageResponse class as your Response class in Web Connection (which is the default in Web Connection 5.0) then any embedded ~/ paths are automatically expanded for you.

How it works and What is Transformed

The ~/ parsing is implemented as a post-processing feature of the wwPageResponse and wwResponseString classes. Basically Web Connection by default renders all of its output into a string first before potentially rendering the output into other outputs. For example, when running in file based mode Web Connection's output goes into a temporary file, but Web Connection (5.0) first renders the output to a string before explicitly writing the output to a file later. This switch occurred in Web Connection 5.0 to allow more control over the HTTP output created - using a string allowed for header manipulation because the final output doesn't get written to IIS until the response is complete. This means you can now add headers and cookies even when output has already been written to the Response.

In Web Connection 5.0 the wwPageResponse class handles all primary output, and the ~/ processing is hooked up in the Render method like this:

*** Fix up ~/ paths with UrlBasePath
IF THIS.contentType = "text/html" AND VARTYPE(Process) = "O"
   lcBasePath = Process.cUrlBasePath
   IF !EMPTY(lcBasePath)
      this.cOutput = STRTRAN(this.cOutput,[="~/],[="] + lcBasePath)
      this.cOutput = STRTRAN(this.cOutput,[url(/wconnect/weblog/],[url(] + lcBasePath)
   ENDIF
ENDIF

The code first makes sure we're dealing with HTML content only and then looks to translates any embedded URL expressions in attributes (the first STRTRAN) and in CSS styles (the second url() based syntax).

Based on this Web Connection will expand:

<a href="~/webcontrols/helloworld.wcsx">Hello World</a>

to:

<a href="/wconnect/webcontrols/Helloworld.wwd">Hello World</a>

Notice thought that this will produce different results:

<a href="~/webcontrols/helloworld.wcsx">~/webcontrols/HelloWorld.wcsx</a>

which produces:

<a href="/wconnect/webcontrols/helloworld.wcsx">~/webcontrols/HelloWorld.wcsx</a>

Note that the second ~/ tag wasn't expanded because it's not defined inside of an attribute - the plain string just stays as designed. If you want that sort of thing to work you have to use <%= Process.ResolveUrl() %> instead:

<a href="~/webcontrols/helloworld.wcsx"><%= Process.ResolveUrl("~/webcontrols/HelloWorld.wcsx") %></a>

You can also expand paths in css tags like the following:

    <style>
        .salesitem
        {
            padding: 10px;
            padding-right: 30px;
            background-image: url(/wconnect/weblog/css/images/pin.gif);
        }
    </style>

which expands out to:

    <style>
        .salesitem
        {
            padding: 10px;
            padding-right: 30px;
            background-image: url(/wconnect/css/images/pin.gif);
        }
    </style>

It also works for inline styles:

<a style="background-image:url(/wconnect/weblog/css/images/help.gif)" 
href="~/webcontrols/Helloworld.wwd">Hello World</a>

which transforms into:

<a style="background-image:url(/wconnect/css/images/help.gif)" 
href="/wconnect/webcontrols/Helloworld.wwd">Hello World example</a>

Configuration

This feature is mostly transparent - there's nothing you have to set up or configure to make the render parse these values. There are two requirements though:

  • Make sure you're using wwPageResponse or the wwResponseString class for Response rendering
  • Make sure the VirtualPath property is set in web.config or wc.ini

wwPageResponse

wwPageResponse is Web Connection 5.0's default response class and unless you've explicitly overridden it this is the class used. If you're using an older version of Web Connection or you have an old application that was migrated to Web Connection 5.0 you might want to check and potentially switch to this class.

cResponseClass = "wwPageResponse"   && wwPageResponse40 for 4.x compatibility

in your Process class property definitions header.

VirtualPath Configuration

Process.ResolvePath and Process.cBaseUrl retrieve the application's base path from the VirtualPath setting in the YourApplication.ini file. Specifically it comes out of the process class configuration section. For example, here's the wwDemo configuration section:

[Wwdemo]
Datapath=C:\WWAPPS\WC3\wwDemo\
Htmlpagepath=c:\westwind\wconnect\
Virtualpath=/wconnect/

The VirtualPath key specifies the virtual path that is used. Note that this path is used for various other things as well so you should always set this in your applications. Web Connection's configuration Wizard automatically sets this but if you manually copy applications always make sure to set these values explicitly.

Summary

If you haven't used this feature before - take advantage of it. As small as this feature is, it's very useful and greatly simplifies creation of cleaner and consistent URLs in your application. The post parsing mechanism is totally transparent - you don't have to do anything to make this work. No changes are required other than making sure that the application's INI file is set.




© Rick Strahl, West Wind Technologies, 2004 - 2013