Tuesday, December 16, 2014, 3:04:06 AM
A couple of months ago Microsoft announced the release of a new Visual Studio Community version to replace all the Express versions. As you probably know the Express versions of Visual Studio were severely trimmed down and specialized versions of Visual Studio – essentially a core version of Visual Studio with only certain packages enabled. While these versions were functional and certainly provided a lot of value, each of the Express versions had some major features missing. The Visual Studio Community edition is a full version of Visual Studio Professional and is available to most non-large enterprise developers free of charge.
Web Express Limitations for Web Connection
With the intro of Visual Studio Community edition, that limitation – and many others are gone.
Visual Studio Community Edition is Visual Studio Professional
The new Community edition is essentially a free version of Visual Studio Professional for most individuals and small organizations. It provides the full feature set of Visual Studio Pro which includes the support for plug-ins, multiple projects and the whole gamut of projects that Visual Studio supports. There are no limitations or disablements – it’s a full version of Visual Studio Pro.
If you are a Web Connection developer and you’ve been using the Web Express Edition you should definitely go ahead and download and install Community Edition to get support for the Web Connection Add-in. This version also allows you to open multiple projects in the same solution so you can have multiple Web sites open in one project, as well as open the Web Connection Web Controls design time control project for creating your own components.
You can download the Community Edition from the Visual Studio Download site
Who can use the Community Edition
As mentioned the Community Edition is free to most small to medium sized organizations. With a few exceptions of larger organizations you can use Visual Studio Community edition for free. Here’s what the Community Edition site shows for who can use the Community Edition:
- Any individual developer can use Visual Studio Community to create their own free or paid apps.
Here’s how Visual Studio Community can be used in organizations:
- An unlimited number of users within an organization can use Visual Studio Community for the following scenarios: in a classroom learning environment, for academic research, or for contributing to open source projects.
- For all other usage scenarios: In non-enterprise organizations, up to 5 users can use Visual Studio Community. In enterprise organizations (meaning those with >250 PCs or > $1 Million US Dollars in annual revenue), no use is permitted beyond the open source, academic research, and classroom learning environment scenarios described above.
What’s the Downside?
The one downside is that Visual Studio is large. It uses a fair amount of system resources so you need a reasonably fast machine to run it. However, it’s nothing like older versions of Visual Studio that were. If it’s been few years since you’ve last tried Visual Studio you really should give it another shot as performance and especially editor performance and load times have improved significantly.
As a Web Connection Developer Should you care?
If you’re using the Web Control Framework you will definitely want to use the Community Edition as it gives you full support for the designer integration and the Web Connection Add-in to load your WCF pages. If you’re using any other edition, go make the jump – it’s totally worth it.
Regardless of whether you do Web Connection development, or raw HTML coding, here are some features in Visual Studio that I wouldn’t want to live without:
- HTML Element and Attribute IntelliSense
- HTML and CSS Formatting (as you type or explicit)
- CSS IntelliSense (CSS properties, reference props in docs)
- F12 Navigation to CSS classes
- Automatic LESS parsing and saving
- Zen Code
- Script and CSS Compression
- HTML Color Picker, Darken/Lighten
- CSS Browser Support Preview
- CSS Vendor Prefix injection
- Font and Image Previewers
There’s lots more but those are some of the highlights for me. If you haven’t tried Visual Studio in a long while or where put off by the pricing of the full versions, give the community edition a try…
Wednesday, October 15, 2014, 6:32:36 PM
Several people have sent me frantic email messages today in light of the latest POODLE SSL vulnerability and whether it affects West Wind products.
The vulnerability essentially deals with older SSL protocol bugs and the concern is especially on the server that protocol fallback from TLS to SSL can cause problems. Current protocols are TLS vs. the older SSL technology. Only the older SSL stacks are vulnerable. This is most critical on Web Servers, and you'll want to disable the older SSL protocols on IIS and leave only TLS enabled. TLS is the newer standard that superseded SSL, although we still talk about SSL certificates – most modern certificates are actually TLS certificates and have support for SSL for fallback validation.
You can check your server’s SSL certificate status here:
You’ll want to see that all versions of SSL are either disabled or at the very least that the server doesn’t support protocol fallback to SSL protocols.
When I ran this for my site running Server 2008 (IIS 7.0) I found SSL3 enabled, but the server not supporting automatic protocol fallback which avoids the main POODLE issue. I’ve since disabled SSL V3 on the server. The good news is, that while the certificate isn’t using maximum strength, the server was not found to be vulnerable from this issue even with the default settings in place.
Disabling SSL on IIS
If you see that SSL versions are enabled even if you have TLS certificates (which just about all of them should be by now) you can disable specific protocols. This is done through registry keys, but be aware that this is global for the entire machine. You can disable/enable protocols for both the client and server.
Here's a KB article from Microsoft to check out that tells you how to disable older SSL protocols.
SSL and WinInet
The West Wind Client Tools and specifically the wwHTTP class rely on the Windows WinInet library to provide HTTP services. The POODLE issue is much less critical for the client as the client is the actually attacking entity. But I double checked here on my local machine and I can see that WinInet uses TLS 1.2 on my Windows 8.1 when connecting to a certificate on the server.
Capturing an https:// session from Fiddler shows the following request header signature on the CONNECT call:
Version: 3.3 (TLS/1.2)
Random: 54 3F 15 D6 B5 3E 6B F5 AD 71 41 FB 4C 39 B9 30 C5 21 04 A4 76 7F 87 A5 1A BA D6 83 19 B3 10 3B
which suggests TLS/1.2 as the initial request sent.
I don’t know what older versions of Windows – XP in particular use – but I suspect even XP will be using TLS/1.0 at the very least at this point. Maybe somebody can check (Use Fiddler, disable Decrypt HTTPS connections option, then capture HTTPS requests and look at the raw request/response headers.
Nothing to see here
From the West Wind perspective this issue is not specific to the tools, but to the operating system. So make sure the latest patches are installed and that if you have to remove the server SSL certificates. Client software is not really at risk, since the attack vector is a receiving Web Server. Regardless even the client tools appear to be using the latest safer protocols and so any West Wind tools are good to go.
Friday, September 26, 2014, 7:40:23 PM
On various occasions I’ve gotten questions regarding how to figure out what version of Web Connection is running on the server. ‘Web Connection’ has several components so the version numbers may be separate and while it’s not absolutely critical that all components are on the same versions it’s a good idea to keep them in sync.
Web Connection has three different connector versions:
- Web Connection .NET Module (recommended for IIS 7 and later)
- Web Connection ISAPI Module
- Web Connection Apache Plug in
For most applications I’d recommend using the .NET module as it has the most features and is the most reliable, especially when running under COM since it has a high performance managed threadpool for managing the COM instances.
Each of these components has their own signature, but there are several ways you can check for versions:
- Check the actual DLLs
- Check the Status Form after a Request
- Check the Admin ISAPI Admin Form
Check the actual DLLs
The DLLs are stamped with a Version attribute that indicates the actual version. To check the version you can right click the file and use Properties | File Details to get to the embedded resource data that’ll contain the version number. The .NET module and ISAPI modules are updated regularly. You can find the version numbers in either dll right clicking and using File | Details:
Check Web Connection Status After a Request was fired
You can also check the version after at least one request was fired by using the Status form and then reviewing the request data.
This is a good way to see if you’re getting the right DLL file. It’s also a good place to check the wcConfig path, which will tell you which configuration is being read which indirectly also tells you where the DLL lives (in the same folder for wc.dll or the bin folder for webconnectionmodule.dll).
Check the Web Connection Module Administration Page
The Web Connection Module page also shows the full version of the active DLL. The module screens looks slightly different depending which version of the DLL you are running – the .NET Module or the ISAPI component. But both contain the version number and status information. For the module it looks like this:
For the ISAPI page the DLL version shows (ISAPI) in parenthesis instead.
FoxPro Server Version
Note that the Module page also displays the compiled server’s EXE version number. This is useful to let you know what version your own code is. Note this is only avaiable with the module and not with the ISAPI component.
FoxPro Code Versions
To figure out the Web Connection code versions you can easily check the version number in wconnect.h. The version number in there shows up like this:
#DEFINE WWVERSION "Version 5.69"
#DEFINE WWVERSION_NUMBER 5.69
#DEFINE WWVERSIONDATE "September 26th, 2014"
This is the only FoxPro code based version that Web Connection provides – individual components are not versioned directly, so if you make changes to any of the Web Connection code these version numbers may not be accurate.
Keeping Versions Straight
These days I get a lot of email in regards to old versions that still float around and lots of people are asking what versions they are running and how various components can interact and work with newer version framework code.
The short answer is that the update from .NET 4.x to 5.x is not major, but does require some changes. Updates from Version 5.0 to 5.50+ is almost seamless. There are very few changes on the latter upgrade path.
Smaller version changes are generally just small maintenance releases and I would recommend that from time to time you update to the latest version just to keep up to date. Newer versions tend to fix reported bugs, improve performance for some operations and are maintained for security and other critical changes that might be required. It’s probably not necessary to update to every new version that comes along but I suggest that at least once or twice a year you update your code to the latest. Partial version updates are free (5.50+ versions) and it’s a good idea to keep your code up to date to minimize future update pain. It’s easier to fix one or two minor items at a time, the 20.
I hope this addresses some of the questions that have come my way regarding versioning and on how to best keep your Web Connection version up to date.
Sunday, July 20, 2014, 12:49:01 AM
On IIS 7 and later, Web Connection’s preferred connector interface is the .NET connector, which internally uses an ASP.NET HttpHandler implementation to interface between IIS and the Web Connection server. Functionality wise the .NET connector has the same feature set as the ISAPI one, plus some additional features, but the main reason it’s the preferred choice is because it’s much easier to set up with the default configuration of IIS, and – surprisingly it actually offers better performance and stability than the ISAPI implementation especially in COM mode.
The managed module also works with IISExpress out of the box, so in a development environment you can easily use a non-system level tiny IIS Express implementation that’s easy to run and configure vs. having to deal with the full IIS environment configuration (which can be a bit daunting).
Lots of wc.dll Apps still around
I do quite a bit of IIS configuration support these days – a lot of people are finally upgrading ancient servers to newer versions of Windows, typically Windows 2012 these days. One issue that has kept some people from updating to the .NET managed module has been, that especially older Web Connection applications, are accessing the raw wc.dll directly as part of the URL. I’ve long recommended this as a bad practice, because accessing the ISAPI DLL is both a potential configuration issue as IIS makes it hard to access DLLs directly and in some cases disallows it altogether. But nevertheless there are a lot of old applications out there that still use direct wc.dll links. Direct DLL links also make it much more difficult to deal with paths as you always have to reference a physical location for the dll – script maps are much more flexible as they can be called from anywhere so path dependencies just go away in many cases.
Using a *.dll Script Map with the .NET Managed Module
So what if you have a complex application and you can’t (or won’t) give up the wc.dll links in your application?
Today I was working with a customer through some configuration issues. We started discussing the application, and as I always do I recommend using the .NET module instead of ISAPI. We went over the improvements and why it’s great, and we both agreed that this would be great, but what about our .DLL extensions in the URL?
After some problems with the ISAPI configuration, I actually went ahead and set up the .NET Handler configuration to ascertain that things were working and running and sure enough with the .NET module things were just working. Since it worked with the module -in a flash of enlightenment - we decided to try to create a script map for .DLL and point it at the Web Connection .NET handler.
And lo and behold – the .dll Script mapping mapping worked perfectly!
It’s absolutely possible to create .DLL script map to a .NET Managed handler, which makes it possible to run existing Web Connection applications that use wc.dll directly on the .NET managed module.
Here’s what the relevant web.config settings look like (or you can use the IIS Admin interface to create this as well):
<!-- use this on IIS 7.5 and later -->
<httpErrors existingResponse="PassThrough" />
<!-- IIS 7 Script Map Configuration -->
<add name=".wc_wconnect-module" path="*.wc" verb="*"
<add name=".dll_wconnect-module" path="*.dll" verb="*"
When creating a script map to a .NET handler a DLL script handler is just like any other script map. Using the mapping you can run requests like this:
Ok that looks weird and is probably not your typical use case, but it actually works. More interestingly though you can do this:
When this page comes up you’ll see that it loaded the .NET Managed handler, even though we referenced wc.dll! For this to work – you’ll want to make sure you remove the physical wc.dll from disk and have the webconnectionmodule.dll in the bin folder of your site.
This means that if you have an existing application that used any wc.dll links directly you can now use them with the .NET handler. Additionally you can start to clean up your application to use scriptmaps instead of the DLL link in the first place.
Why you should use ScriptMaps instead wc.dll Links
As already mentioned ISAPI configuration is getting more and more tricky as IIS versions progress. IIS today mainly relies on .NET to handle extensibility to other services and interfaces and ISAPI is more of an deprecated prototocol. It’s still there but it’s an optionally installed feature. Further if you are accessing a DLL directly using ISAPI (not through a scriptmap) you are directly accessing a binary file which is generally discouraged. In order for this to work you have to explicitly enable generic ISAPI extensions in the IIS configuration.
Scriptmaps are simply a nicer way to write URLs. Instead of the ugly ~ syntax of:
you can use scriptmap to method mapping:
where sm is a scriptmap, and method is the method of the process class that sm is mapped to. The URLs are much cleaner and easier for users to parse and understand.
Finally script maps allow you to simplify relative paths. Because a script map is not referencing a physical file in a specific folder like wc.dll, you can reference a scriptmap from any location and have it behave the same way. This means if you create a page in a subdirectory and you want to access the scriptmapped page you can use the same path and it will work. IOW:
all are treated exactly the same. The only difference is that the script path passed as part of the server variables will point to a different location, so Request.GetPhysicalPath() will point at the site relative physical path on disk. Otherwise each of those three commands are identical.
Scriptmaps are the way to go!
Scriptmaps make life easier and once again I urge you, if you’re not using them to think about integrating them in your Web Connection applications. I suspect sometime in the not too distant future IIS will stop supporting direct access .DLL links and will force all operation to occur against script maps. Plus the easier usage for referencing dynamic links make code much more portable across sites or virtual directories if your development and live environment aren’t 100% identical.
Monday, June 09, 2014, 11:07:46 PM
Representing dates and times across timezones can be a challenge especially if you don’t lay out a plan up front on how to store dates consistently. The sneaky thing with date time management in larger applications and especially applications that live on the Web or are shared across locations, is that problems don’t usually show up until much later in the lifetime of the application. For FoxPro in particular it’s not natural to store dates in anything but local machine format as the language doesn’t support direct UTC formats so it’s very common to see FoxPro applications use local dates which is usually a bad idea.
Here’s why and how we can address these issues…
TimeZones and Offsets
Depending on where you are in the world your local time is defined by an offset from UTC (Coordinated Universal Time) time or the baseline zero time. As you know if you’ve ever talked to somebody half way across the world at a certain time of day, while you just got done with breakfast, they are getting ready to go to bed on the other side of the world. This is the timezone offset. If you build applications that deal with customers that enter data into a system from multiple locations then using local times becomes problematic.
The problem of local times is made worse by Daylight Savings time. Most of the world – especially those further away from the equator – have daylight savings time which is applied on different dates in different locations around the world. Usually it’s “spring forward” and “fall back” with time getting set one hour forward for the summer. Some countries have it, others don’t, and surprisingly – some countries actually half hour DST offsets. You can see where this is going – dealing with dates from multiple locations around the world can get complicated fast.
A generally accepted solution to this problem is to store date values using a single time format that is adjusted from local time. Typically this date format is UTC time – or zero offset time.
I often get questions about why you should – and you REALLY, REALLY should - store dates in UTC format. The simple reason is: Things change! You never know how the data that you are capturing today will be used in the future. Maybe today you’re using the data in one location but maybe in the future you have a Web application and you have multiple locations that access the data. Or you end up building a service for other people to consume your data. If your data is in local time, the data will be much less useful then being in a universal format.
While it’s possible to convert data later on.
In fact on this project I worked on the client insisted on going with local dates over my vehement protests. The argument almost always is that ‘hey, we just have one location – everybody’s running in this domain and we want to see data from the database in local time.’ Convenient – yes absolutely. A good idea: NEVER! In every application where this has come up for consideration it’s always caused a problem eventually. Yes you may not see this right away because when you choose to stick with local time it’s usually based on the assumption you’re staying with inputs from a single timezone. Over time, as applications age however, things change. Data is accessed in other ways, possibly from different applications or shared with other customers around the world. And all of a sudden you have a problem that you never thought would happen.
It’s a generally accept good software practice to store Date and Time values in a consistent format and the easiest way to do this is to use UTC dates. The idea is simple: All data that is persisted to a permanent store is turned into UTC dates and written out that way. Any data retrieved is converted into a local date explicitly – only for display purposes. For things like queries input dates that are locale specific are converted into UTC dates first before the query is applied. IOW, if you use a common date format there will be conversion, but typically only when accessing/querying the data from the UI.
Most software systems provide easy support for date conversions. In .NET for example, there’s a DateTime.UtcNow value you can use to get the UTC time and there FromUniversalTime() and ToUniversalTime() and ToLocal
FoxPro and Date and Time
FoxPro doesn’t make this easy because it can only represent dates in local time – that is the time that is current for the computer that the machine is running on. However, it’s quite common in other environments such as .NET and Java to always write out date time values as UTC time. UTC time is Zero time, Greenwich (England) time, Zulu time – whatever you want to call it, it’s the time that doesn’t have an offset.
At the very least if you need to interact with systems that use UTC time you’ll need to make FoxPro play nice in this space. But I would urge you to consider to ALWAYS use UTC time for applications. While it is definitely a little more work to deal with UTC deformatting it’s not that much effort as long as you realize that the only time you care about this is when you convert dates to and from the User interface. Internally all date operations can relatively easily be made with native UTC.
Some UTC functions for FoxPro
If you are using any of our West Wind products – West Wind Web Connection, West Wind Client Tools or Internet Protocols – you already have this functionality I’m going to describe below. It’s built in with two functions (contained in wwAPI.prg):
Gets the current UTC time, or converts a FoxPro local DateTime to a UTC time.
Converts a date in UTC time format to local time.
Additionally there’s also:
Returns the current timezone offset from UTC for the local machine. This is useful if you DIDN’T use UTC dates and are later forced to adjust dates based on local time and calculating time offsets based on user options or external locale access (ie. over a service). Essentially what this allows you to do is calculate relative offsets between two timezones and calculate a time for a different timezone. This function is also used by GetUtcTime() and FromUtcTime().
If you’re wondering about the inconsistent naming – the original function that existed in the framework for years was GetUtcTime which simply returned the current UTC time. Then at a later point I added the functionality to arbitrarily convert any DateTime value to a UTC data, so the function name stayed.
So using the two Utc conversion functions you can do the following:
? "Timezone: " + TRANSFORM(GetTimeZone()) + " minutes"
ltTime = DATETIME()
? "Current Time: ", ltTime
ltUtc = GetUtcTime(ltTime)
? "UTC Time: ", ltUtc
ltTime = FromUtcTime(ltUtc)
? "Back to local: ", ltTime
I’m currently in the PDT (Pacific Daylight Time) zone and I get:
- 06/09/2014 07:45:36 PM - current
- 06/10/2014 02:45:36 AM - UTC
- 06/09/2014 07:45:36 PM - back to current
- 420 - timezone offset in minutes (-7 hours)
Note that GetTimeZone() will change if you change your system timezone, but VFP doesn’t see the change until you restart. The GetTimeZone() value also seems backwards: It’s +420 for Portland Oregon (PDT) and –600 for Sydney Australia, but that’s how the Windows API is actually returning it. Essentially you can add the GetTimeZone() value to a local date to get a UTC date.
This is all nice and neat if you have West Wind tools, but what about the rest of you that don’t? Ok, here’s some code that provides this same functionality (or pretty close to it actually):
*** Code exists also in wwAPI of any West Wind Tools!
*** SET PROCEDURE TO wwAPI Additive
#define Testing .t.
SET PROCEDURE TO TimeZone additive
? "Timezone: " + TRANSFORM(GetTimeZone()) + " minutes"
ltTime = DATETIME()
? "Current Time: ", ltTime
ltUtc = GetUtcTime(ltTime)
? "UTC Time: ", ltUtc
ltTime = FromUtcTime(ltUtc)
? "Back to local: ", ltTime
*** Function: Returns UTC time from local time
ltTime = DATETIME()
*** Adjust the timezone offset
RETURN ltTime + (GetTimeZone() * 60)
*** Function: Returns local time from UTC Time
RETURN ltTime - (GetTimeZone() * 60)
*** Function: Returns the TimeZone offset from GMT including
*** daylight savings. Result is returned in minutes.
*** Cache the timezone so this is fast
IF VARTYPE(__TimeZone) = "N"
DECLARE integer GetTimeZoneInformation IN Win32API ;
STRING @ TimeZoneStruct
lcTZ = SPACE(256)
lnDayLightSavings = GetTimeZoneInformation(@lcTZ)
lnOffset = CharToBin(SUBSTR(lcTZ,1,4),.T.)
*** Subtract an hour if daylight savings is active
IF lnDaylightSavings = 2
lnOffset = lnOffset - 60
__TimeZone = lnOffset
*** Function: Binary Numeric conversion routine.
*** Converts DWORD or Unsigned Integer string
*** to Fox numeric integer value.
*** Pass: lcBinString - String that contains the binary data
*** llSigned - if .T. uses signed conversion
*** otherwise value is unsigned (DWORD)
*** Return: Fox number
LOCAL m.i, lnWord
lnWord = 0
FOR m.i = 1 TO LEN(lcBinString)
lnWord = lnWord + (ASC(SUBSTR(lcBinString, m.i, 1)) * (2 ^ (8 * (m.i - 1))))
IF llSigned AND lnWord > 0x80000000
lnWord = lnWord - 1 - 0xFFFFFFFF
The code is pretty self-explanatory. GetTimeZone() makes a call to a Windows API function to retrieve the Timezone structure and then needs to do some binary conversion to peel out the timezone offset. The timezone value is cached so only the first call actually makes the API call for efficiency.
Again if you are already using any West Wind tools you won’t want to use this code as it’s already included, but if you don’t then these functions are feature compatible with the West Wind Versions.
Working with UTC Dates
Using UTC dates in your application is pretty straight forward. Your user interface captures dates and times as local datetime values as it always has, but when you actually write the data to the database you convert the date to UTC dates before writing them.
Unfortunately in FoxPro there no safeguards for this situation as dates are always local dates with no built in way to convert. So it’s up to you to make sure that you know which format a date is stored in.
When running queries against the data on disk with dates input by users or from other sources that are in local date format, you first convert the input dates to UTC dates, then run your queries with the adjusted date values:
*** some date that comes from the UI
ltUserDate1 = DATETIME()
ltUserDate2 = DATETIME() - 3600 * 24 * 30
ltTo = GetUtcDate(ltUserDate1)
ltFrom = GetUtcDate(ltUserDate2)
SELECT * FROM orders ;
WHERE OrderDate >= ?ltFrom AND OrderDate <= ?ltTo
into cursor Orders
Likewise if you write data to disk that was captured from user input you have to capture the local date and convert it. If you’re displaying value you have to convert them to local dates. If you’re using business objects, you can do this as part of the business object’s save operation which can automatically update dates as they are saved. Properties can have setters and getters that automatically convert dates to the right format.
Typically this will be a two step process – loading and saving.
For saving you might do:
*** Save Operation
loOrderBus = CREATEOBJECT("Order")
loOrder = loOrderBus.oData
loOrder.Entered = GetUtcDate()
loOrder.Entered = GetUtcDate(loOrder.Entered)
and for reading you would do the opposite:
*** Load Operation
loOrderBus = CREATEOBJECT("Order")
loOrder = loOrderBus.oData
loOrder.Entered = FromUtcDate(loOrder.Entered)
If you’re using business objects like the above you can make this even more transparent by automatically doing these transformations right inside of the business object itself:
DEFINE CLASS busOrder as wwBusiness
this.oData.Entered = FromUtcDate(this.oData.Entered)
this.oData.Entered = GetUtcDate(this.oData.Entered)
which makes the assumption that your user code deals with local timezones while the data saved is UTC.
Clearly all of this isn’t just totally transparent, even in languages that support UTC more easily there’s some effort involved to make this work. The main reason being that database – the storage mechanism in most cases doesn’t differentiate between dates either. FoxPro data doesn’t, neither does SQL Server. NoSQL solutions like Mongo do because they’re using JSON values which are ALWAYS UTC dates – you don’t get a choice (which in my opinion is the right way).
It’s not a totally transparent process, but it’s a good idea to do this nevertheless especially if you’re building applications that run on the Web or in other places where the applications are accessed from multiple locations – which is most applications these days. It’s worth the effort for peace of mind in the future and a good skill to learn as this is the norm for other platforms that are more date aware than FoxPro.
Tuesday, February 11, 2014, 1:49:33 PM
Here’s a question that comes up quite frequently: I’m running a backend request that takes a few seconds to run – how do I show a simple ‘wait’ message while this request runs?
There are a number of ways to approach this problem including some more complex async processing concepts that offload processing to a separate process to avoid tieing up Web Connection instances that are running. But, if your process is a one off process that rarely runs and doesn’t take a huge amount of time (my cutoff is usually in the 10-20 second range before I’d consider async operations), there’s a simple UI solution that might make waiting a little easier to deal with.
It’s the UI Stupid
The problem with long running submit requests is that when the user clicks a button to submit a form, they likely expect something to happen right away. If your app however, just sits around waiting for a response to come back, the user is very likely to think that something is wrong. The most likely response will be that they either wander off without waiting for completion, or in a typical UI will try to click the Submit button again. For the latter, where you had one small problem before, you now have multiple small problems that can very easily tie up your server because those requests are slow.
So a UI solution to this should address two things:
- Providing some sort of UI that lets the user know that something’s happening
- Prevents the user from clicking the submit button again
It turns out doing this is pretty simple. Let’s look at an example. The following is a simple form that has a submit button.
I’m using a Web Connection Web Control Framework button here, but the same rules apply if you’re using Web Connection templates or script and plain HTML controls – the WCF just makes the code more self-contained.
<form id="form1" runat="server">
<ww:wwWebErrorDisplay runat="server" id="ErrorDisplay" />
<h2>Slow Submit Operation</h2>
The following button click will take a few seconds to run.
If your code doesn't do anything, the application will appear
to simply be unresponsive but the existing form is still active
daring the user to click the submit button again... and again...
<ww:wwWebButton runat="server" id="btnSubmit" Text="Submit to Server"
On the server side the FoxPro code in the button simply waits for a few seconds and dumps a message to the ErrorDisplay control on the page.
WAIT WINDOW "Hold on - processing..." TIMEOUT 10
this.ErrorDisplay.ShowMessage("Waited for 10 seconds.")
This forces the server to essentially ‘hang’ for 10 seconds to simulate some long running task.
If you run this now by hitting the Submit button, you’ll see that the UI doesn’t change after the submit button is hit. The HTML DOM pointer changes to a spinning pointer, but otherwise the UI stays the same. The Submit button is still active so you can actually press it again, and if you do you generate another hit against the server.
Ok, so this UI works for quick requests that process in under 1 second, but for anything over a few seconds this becomes problematic.
The HTML DOM has a form submission event that fires when an HTML form is submitted. When you click the Submit button the form.submit() event is fired and we can capture this event to modify the browser UI by showing a progress dialog.
I’m going to be using the Web Connection client modal dialog control that makes this super easy. Modal dialogs show a black overlay on the page and overlay whatever content you apply it to ontop of this black overlay. The overlay basically prevents access to the underlying document – so you can’t click the submit button again – and provides nice visual feedback that the content is not accessible. You can then overlay some content of your choice.
Here I use a rotating image gif and some text. Here’s what it looks like:
The markup for this simple HTML looks like this and I simply add this to the bottom of the HTML page.
<div id="WaitDialog" class="hidden" style="text-align: center">
<img src="css/images/loading_black.gif" />
<div style="margin-top: 10px; color: white">
Note this uses the westwind.css stylesheet for the hidden CSS class and the loading_black.gif from the CSS folder.
I’m loading jQUery and ww.jquery.js from the scripts folder. jQuery provides the base functionality for document parsing and ww.jquery.js provides the modal dialog feature (and a lot more modular functionality built-ontop of jQuery).
The .modalDialog() jQuery plug-in is attached to the element you want to display centered on the screen on top of the darkened background. Here I’m using the default modal dialog behavior without any options parameters, but there are a host of options available to customize the modal popup and you can see some more examples on the Web Connection sample site.
Using a modal overlay like this is a simple solution to longish but not overly long server side requests where you want to prevent users from just walking off or double clicking buttons. Capturing the form submission can also be even simpler – for example you could simply disable the submit button, or change the submit button text into something like “Processing…” and disabled rather than displaying the overlay which is a good idea for shorter requests where it might be jarring to have a modal dialog pop up and then disappear immediately.
Monday, November 25, 2013, 4:49:20 PM
In the last few weeks I've gotten a few notes from various customers that wwFtp has started to break for them after upgrading to IE 11. It appears there's been a new bug introduced with IE 11 that causes failures on certain types of connections.
There's more info on this in this StackOverflow question:
Specifically it points at some reproducible problems.
So far all the problems I've heard reported are related to file uploads rather than downloads. And for uploads there are a few workaround available.
Setting the Scenario
So I set up a simple test to upload some files to my own FTP server and sure enough I can see the problem occurring with this code:
LOCAL o as wwFtp
Here I'm using FtpSendFileEx() to upload multiple files on a single connection and when I run this the third FTPSendFileEx call ends up failing with a 12003 error (extended error) with an error message basically echoing back the transfer command.
200 Type set to I
226 Transfer OK
Oddly this looks like the transfer worked even though the error message shows, but examing the target folder on the server reveals that the file was not actually sent or at least not written over there.
So what can we do?
wwFtp by default uses Passive FTP as it's more flexible for going through firewalls and proxied HTTP connections. By default lPassiveFtp is .T. and so passive FTP is used. Passive FTP basically creates a connection does it's transfer and drops the connection and automatically reestablishes as needed. In the process the FTP connection might actually jump ports.
o.lPassiveFtp = .F.
wwFtp uses Active connections which stay alive for the duration of the connection made and stays fixed on a single port.
Using Active Connections I had no problems with uploading many files.
Unfortunately, active connections do not always work and can be flaky with connection drop offs, but it entirely depends on the environment. In general I recommend using Passive as the default but in light of the current bug, using Active at least should be tried (and you should always expose that setting as an option in your application settings so you can easily switch modes.
Connect and disconnect for each Transfer
Another option for sending files is to simply not reuse connections and send files by opening, sending and closing the connection. Doing this is reliable but it'll add a little overhead especially if you're sending lots of files.
So this works too:
LOCAL o as wwFtp
o.lPassiveFtp = .T.
Some time ago I quietly added an wwFtp::FtpSendFileEx2() function to wwFtp. The original FtpSendEx() method is a very low level function that makes a bunch of internal API calls from within Visual FoxPro. It also adds some additional features like giving you the ability to get notified of chunks of data being sent.
Even prior to this IE 11 issue, I've found that on occasion when sending large numbers of files, FtpSendFileEx() would occasionally stall for no apparent reason. It was rare but enough of a problem to consider alternatives. The alternative was to build another routine - FtpSendFileEx2(), which provides the same functionality but calls one of the higher level WinInet functions (FtpPutFile()) which is basically a single command. It turns out that using FtpPutFile() under the hood is a lot more reliable than the various API streaming functions.
FptSendFileEx2() is parameter compatible with FtpSendFile() but - it doesn't support the update even calls the OnFtpBufferUpdate() to provide progress information as the file is sent off a single API call into WinInet.
LOCAL o as wwFtp
and that works reliably too.
I use FtpSendFileEx2() in Help Builder to upload HTML help files to a Web site, and that can be 1000s of files in a session - and it works without problems, so this is what I would recommend you use for uploading files in bulk.
What to use?
If you're uploading a bunch of smallish files a use FtpSendFileEx2() - you won't need progress info on a per file basis, but you can certainly handle intra file upload info. If you upload just one or two larger files and you need the OnFtpUpdate() API to provide progress info, use FtpSendFileEx() but just make sure you reconnect for each file upload.
Switching active/passive mode is just a quick fix that might help get you out of a bind, but as a long term solution I'd still recommend you use Passive mode as it's much more reliable.
Hopefully this will be a temporary issue that Microsoft addresses soon - this is turning out to be a major headache for some of my customers who've been calling in frantically asking to see if there's a solution to this problem. It sucks when an application that's been running for 10 years mysteriously breaks after a silly browser update.
Thursday, October 24, 2013, 5:32:42 AM
In recent weeks a few people have run into the Cannot load CLR Instance error when trying to use the wwDotnetBridge class from Visual FoxPro. The cause for this problem is a security issue in Windows and some new security features in .NET 4.0 when running under User Account Control (UAC) in Windows.
wwDotnetBridge is distributed as part of a simple .Zip archive - you download a .zip file and copy the files to your local machine. Unfortunately Windows recognizes that .zip files out of the Downloads folder contain 'downloaded' files and so adds some additional security flags onto files copied to the local machine. These security attributes cause a problem when trying to load the dll.
If you now try the simplest thing possible:
loBridge = CREATEOBJECT("wwDotnetBridge","V4")
you'll find that you get an error Unable to load wwDotnetBridge: Unable to load Clr Instance. 0x80131515.
in this code:
*** Fail creation if the object couldn't be created
ERROR "Unable to load wwDotNetBridge: " + this.cErrorMsg
However, if you try to load "V2" of the runtime you'll find that the DLL loads just fine.
Unblocking the DLL
Luckily the solution to this problem is generally quite simple. You simply need to unblock the DLL. To do so:
- Open Explorer
- Find wwDotnetBridge.dll
- Right click and select Properties
- Click the Unblock button
Here's what the dialog looks like:
Distributing your App and avoiding Unblocking
It would really suck if these steps had to be done with a distributed application. But fortunately this problem pops up only if the DLL originated from an Internet Download either as the file directly or as part of a zip that just copied the file to the hard disk. These specific steps of actions effectively mark the DLL as suspicious code which is the cause for the error.
For your final deployed applications however, this shouldn't be an issue. If you install your application using a Windows installer or other process that runs as an Administrative task (ie. permissions were elavated as they are for software installations typically) the DLL file gets properly installed and you will never see this issue pop up.
Unfortunately this is a bummer for development and people trying for the first time to use wwDotnetBridge because the component is shipped as part of a .Zip file that is likely downloaded. So a large chunk of developers are likely to see an issue with this.
Other load Problems
Note while Windows blocking likely is the most common problem, there can be other things that cause problems. Loading from network shares is problematic for example, unless you either provide custom policy settings or a configuration file setting. You can find out more in the helpfile on how to run wwDotnetBridge or any other .NET assemblies from a network location.
Remember me, Remember me, Remember me
I'm hoping that this blog post will make the issue at least a bit easier to search for, since the solution is typically relatively simple.
Here's a funny anecdote about this error: I had run into this problem before, and actually had spent the time to add some documentation in this regard into the help file. In fact, if you look at the wwDotnetBridge topic in the help file - in the remarks on the bottom - you'll see this issue addressed along with a detailed link to more info. I however had forgotten about the whole issue, so last week I've been going back and forth with two developers who were running into this problem. Not only that but I couldn't duplicate it, because my copy of wwDotnetBridge.dll isn't copied from a downloaded .zip file and I typically run as an Administrator. Only when I looked up the error code and found out the security issue and reference to unblocking did I finally remember that I solved this problem before :-)
So, yes, writing it down again is probably a good idea.
Sunday, October 13, 2013, 10:19:00 PM
Visual Studio 2012 (Update 2) and later includes the ability to publish Web Site projects to the Web server. Using either IIS WebDeploy or FTP, you can basically publish an entire Web site - all the HTML templates, HTML, CSS, Scripts and configuration files and even your executables if they exist as part of the live application.
The Web Publish feature is designed for ASP.NET or plain HTML projects, and is drop dead easy for those deployments, a few small adjustments have to be made to ensure smooth publishing for Web Connection projects. With a little forethought you can even make Web Deploy deploy both HTML and FoxPro executable content.
IIS Web Deploy
IIS Web Deploy is a Microsoft IIS related transfer protocol that allows you to publish files from a client machine to the Web Server. This is a two-way service protocol that has some smarts associated with it to know what's been published on the server and transfers only the content that has been changed. FTP deployment is also available but this deployment is generally less efficient and currently at least doesn't support individual file publishing.
If you're going to use this feature - Web Deploy is the preferred choice.
To run Web Deploy you have to install it on the IIS Web Server. The latest version is Web Deploy 3.5 which can be installed using the IIS Web Platform Installer.
The latest version - unlike older version - is completely self-configuring and once installed is ready to go. Older versions required multiple steps and service startup, but the current version once installed just works.
If you can't install Web Deploy you can also use FTP to publish. The rest of this tutorial assumes Web Deploy, but most features work the same except configuration, and as of VS 2012 Update 2 individual file publishing was supported only with Web Deploy.
What can you publish?
Before digging in on Web Publishing works, let's discuss how Web Deploy fits with Web Connection. Web Deploy is typically meant for ASP.NET based Web sites. Web Connection doesn't quite fit that profile, but you can still use Web Deploy to publish all the Web related files of a project. This means you can publish all your HTML templates (ie. your script mapped templates), all static files (HTML, CSS, JS, Images etc). For Web Sites projects Web Deploy basically copies ALL files in your folders to the server. Exceptions are the Web Deploy related files, and user specific configuration files.
If you decide to hold your executables in a subfolder of the Web folder (like the Web Connection.deploy folder for example), you can also publish an executable this way. But you have to be careful - if the executable is running and locked the publish will fail at that point and not update files based on the locked file failure. In order to copy executables you'll likely have to stop the application before updating.
Using Web Deploy with a Web Connection Project
By default Web Connection uses what is known as a Web Site project in Visual Studio. A Web Site project is a free file project which means that there's no explicit project file, but rather the project is simply a directory with files in it. This works well for Web Connection because in although Web Connection simulates ASP.NET applications, it's not really an ASP.NET application.
To publish files you can simply go the Site's root node and right click, and select Publish Web Site:
Here I'm on the TimeTrakker project and then selecting the Publish Web site option from the context menu.
Create Publish Profile
The first thing you need to do is create a Publish Profile where you specify where to copy files to on the server. To do this click the drop down on the publish dialog and select <New Profile> (note: the pic below shows an existing profile of mine in the dropdown- for a new one it'll be blank).
In the dialog that pops up give it a name and press enter. I'm going to use FoxMobile for my project, and I'm going to install it as a virtual directory underneath a TimeTrakkerFox Web site on my server.
Next you're presented with connection properties. Here you specify the root Web Server URL and a site name:
The Server is the root Web server name or IP address. If you have multiple sites any site will work - this is just to connect to IIS and connect with Web Deploy on the server. The Web Deploy client then hits this url: http://yoursite.com/MSDEPLOYAGENTSERVICE).
The Site name is the name of the IIS site *as entered in the IIS Service Manager* and this is where files are copied to. If the site does not exist yet Web Deploy will crate it in its default configuration. I don't recommend this - pre-create your site and virtual folder and disk location for your files.
So if you look in IIS under the Sites node, you'll find the name of the site. If you're installing to the root of a Web site, just use the sitename. If you're installing into a virtual folder below a root site as I'm doing here provide the name of the virtual with a forwardslash after the site name:
if you just publish to the site root the syntax is simpler:
Finally provide your username and password and check the checkbox to remember your credentials. Next validate the connection - this is important. Click the validate button to ensure that the connection actually works before moving on. If you have a problem you'll get a reasonably useful error message here such as site doesn't exist or invalid credentials etc.
If the connection validates go ahead click Next and/or Publish and Visual Studio will now copy all your project files to the server.
What gets copied
Because Web Site Projects that Web Connection uses by default are based on simple file structure, Web Publish copies everything in all of your folders to the server. There are a few exceptions of Visual Studio work files, but otherwise everything gets copied.
One thing that is important when you publish to a live server is that your configuration setting for the local machine and the server might be different. If you're using the Web Connection module configuration settings are stored in the web.config file and you typically have a few values that are different between the local test environment and the live site.
Web Deploy includes a feature called config transformations that allow you basically create different configurations and based on that configuration apply some transformations to the configuration file. This allows you to customize the handful of keys that might be different between local and remote installs.
AFAIK this is the only way to really handle configuration files because at least on Web Site projects there's no easy way to exclude files from uploading. So the web.config file is always sent. Config transformations allow you to customize the settings however.
To create a config transformation go to App_Data/PublishProfiles/YourProject.pubxml in your project:
This enables config transforms in the project and adds a new web.debug.config file to the project. This file is the transformation file. 'Debug' refers to the build configuration which by default is 'Debug' and which you can see in the top toolbar. You can create additional build configurations, so it's possible to associate multiple deploy targets in the publishing configuration. For example, you can publish to staging and live servers with different configuration.
The transformation file uses XSL syntax to allow you to replace sections and values in the config file. Here's the one I use for the FoxMobile app:
<?xml version="1.0" encoding="utf-8"?>
<compilation xdt:Transform="RemoveAttributes(debug)" />
<add key="ExeFile" value="d:\Web Sites\fox\FoxMobile.exe" xdt:Transform="Replace" xdt:Locator="Match(key)" />
<add key="ServerCount" value="1" xdt:Transform="Replace" xdt:Locator="Match(key)" />
<add key="TempPath" value="d:\temp\wc\" xdt:Transform="Replace" xdt:Locator="Match(key)" />
A transformation file is basically a file that only has all the changes to the original. So web.config is the base file, and web.debug.config (where 'debug' is the name of the build configuration) contains all the overrides and changes specific to the live setup. This allows me to have two sets of configuration settings - one locally and on the remote site so that both sites can run.
With this in place I can now simply publish the entire site without having to worry about Web Deploy writing over my configuration file.
In the example above I only override a couple of the webconnectionConfiguration settings that are different between local and remote setup. All other settings are left alone. It's also possible to apply the Replace transform to the entire webConnectionConfiguration section by using the Replace transform without a locator. You can find much more detail on what you can do with Web.config transformations here:
Publishing Individual Files
You can also publish individual files rather than the whole site. To do this highlight one or more files in the Web project and then choose Publish Files from the context menu:
This is a quick and easy way to send up individual files to the server. Note that this option only works once you've set up a publish profile as described earlier. You don't have to publish a full project first, but you have to at least go through the Profile setup and validate steps for this option to work.
Unless Publish Web Site is too inclusive for you or you have a special need where you don't want to push everything up to the server just yet, there should be little need for individual publishing of files over publishing the project. Remember that Web Deploy is smart and only publishes stuff that has actually changed, so a site publish tends to send only a few files after the initial publish. The advantage of using a full deploy is that you don't have to keep track of what's changed - the publish feature does that for you.
What about Binaries?
So far I've only talked about the Web site content. But you can also deploy binary content in the same way.
In recent versions of Web Connection we've provided a webConnection.deploy folder which was meant for just this purpose. You can copy your executables to this folder and if you do those files get published as well. The webconnection.deploy folder should be set up just like you would set up your main application folder, but because it lives under the Web site it can be deployed all at once along with the rest of the application.
Here's what the deploy folder looks like for the Time Trakker project:
The root folder contains the executable and configuration files. Also wconnect.h is there (required for dynamic compilation) of dynamic pages.
You also optionally can add a Data folder for the first deploy to push application data in DBF files up to the server. But you'll want to remove those files from the deployment folder after the first deploy so that you don't overwrite the data captured on the server. Also be very careful if you try to run the application out of this folder locally as some files are auto-created. wwSession, wwWebRequestLog and foxUser and you don't want to end up pushing these large files to the server each time :-).
If you update the server when it's running the update will likely fail because the EXE server is actually running and the EXE file on disk can't be updated. So in order for subsequent deploys to work (only when the EXE has changed) you'll have to stop the EXE. If you're running in COM mode, or you're running the Web Connection .NET Module and EXE based servers you can temporarily stop servers using the administration page.
For standalone EXE servers make sure you have the following key set:
<add key="ExeFile" value="~\WebConnection.deploy\FoxMobile.exe" xdt:Transform="Replace" xdt:Locator="Match(key)" />
<add key="ServerCount" value="1" xdt:Transform="Replace" xdt:Locator="Match(key)" />
This points Web Connection at where the EXE file lives and allows it to remote start and stop the server, assuming your service account has rights to start and stop processes.
In file mode use:
The process is:
- Unload File Servers on Admin page
- Run the Web Publish operation from Visual Studio
- Load File Servers on Admin page
The Unload File Servers option unloads all of the running EXE servers by killing them. After unload run the publish operation. When that's done you can hit Load File Servers to start the servers back up.
For COM operation you can click on the Hold Requests | Switch option to flip the server into ON HOLD mode:
The process is:
- Click on Hold Requests Switch to show Servers ON HOLD
- Run the Web Publish operation from Visual Studio
- Clock on Hold Requests Switch to show Servers running
Clearly, once you add executables to the mix things get a little more complicated with publishing due to FoxPro's specific needs of updating executable files. You may even have to resort to this same mechanism if you're updating only FXP/APP files as well as these files can also get locked at times.
Web Publish makes it almost too easy to publish so you'll want to decide when it's good to push a full deploy to the server. Typically you can work locally until you got everything right then push to the server.
If you do decide to deploy your EXE to the server using Web Deploy I'd recommend that you treat the webconnection.deploy folder as just that - a deployment folder with the files in that folder only changing when you actually want to deploy a new EXE, rather than building your EXE directly into this folder. You don't want to be sending up a new EXE every time your recompile even if there are no changes in the EXE, especially since Web Connection EXEs tend to be at least 1meg in size just for the base features plus your application code.
So develop your app in some other folder, debug and tweak as needed and then when the EXE is ready, copy it into the webconnection.deploy folder for publishing.
The Visual Studio Web Publish feature is very nice, especially in recent versions of Visual Studio that have made it possible to use Web Publish for Web Connection projects. It's an easy way to very quickly and effectively copy your Web projects files - and potentially your executable files to the Web server more easily. Between quick publish operations for the individual files and full project publishing, it's one of the cleanest way to deploy your applications to a live Web site that beats manual FTP deploys.
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:
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:
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:
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:
LOCAL lcOutput, lcBasePath
*** Fix up ~/ paths with UrlBasePath
IF THIS.contentType = "text/html" AND VARTYPE(Process) = "O"
lcBasePath = Process.cUrlBasePath
this.cOutput = STRTRAN(this.cOutput,[="~/],[="] + lcBasePath)
this.cOutput = STRTRAN(this.cOutput,[url(/wconnect/weblog/],[url(] + lcBasePath)
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.
© Rick Strahl, West Wind Technologies, 2004 - 2014