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.
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:
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")
*** 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()
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")
loBridge2 = CreateObject("wwDotNetBridge","V2")
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
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.
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.
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.
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!
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)
IIS Express 8.0 (Windows Vista, 7, 2008, 8)
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
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:
to start the Web Connection menu in the Visual FoxPro IDE. Then click on Web Connection | Start IIS Express Standalone Web Server:
This brings up a dialog that lets you select a path and port for the server:
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:
or to access a specific page:
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:
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:
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:
<validation validateIntegratedModeConfiguration="false" />
<!-- 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" />
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:
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!):
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.
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:
.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.
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.
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")
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"
*** Write out default message - Information
* public static void WriteEntry(string source, string message)
"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.
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.
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:
I have to create a matching manifest file in the Project's folder:
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:
So - in the project directory - I create a manifest file:
that looks like this:
<?xml version="1.0" encoding="utf-8"?>
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:
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:
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">
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:
- Open Visual Studio
- 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:
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"?>
<requestedExecutionLevel level="asInvoker" />
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"?>
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.
© Rick Strahl, West Wind Technologies, 2004 - 2014