10 Cool Examples of using wwDotnetBridge to extend your FoxPro code

Prepared for: Southwest Fox 2016

By Rick Strahl weblog.west-wind.com [email protected]

wwDotnetBridge is a small library for Visual FoxPro, that allows you to call just about any .NET component from Visual FoxPro. .NET has now been around for 15+ years and there’s a huge amount of library content available that you can plug into your own applications. Whether it’s accessing native functionality that comes with the .NET framework or whether it’s third party or free open source components, or whether it’s components that you’d rather offload to .NET for processing rather than doing it in FoxPro.

Here’s what we’re going to cover:

1. Create a powerful string formatter 2. Create an encryption library 3. Add Markdown parsing to your applications 4. Upload and Download files using SFTP securely 5. Add spell checking to your applications 6. Call a SOAP Web Service 7. Humanizer 8. Inter-application messaging with Named Pipes 9. Hosting a Web Server in Visual FoxPro 10. Make Asynchronous .NET method calls 11. Bonus: Broadcast messages with SignalR

You can find the examples related to this articles in a BitBucket repository:  https://bitbucket.org/RickStrahl/swfox16_wwdotnetbridge_10uses  wwDotnetBridge on GitHub

Create a Number and Date String Formatter This example demonstrates: Simple use case of calling native .NET BCL methods and wrapping with FoxPro function wrappers.

For this first example lets look at something very simple, that takes advantage of what’s built into the .NET framework natively and doesn’t require any external .NET dependencies.

.NET has a few built-in powerful language string formatters. Specifically, it has a nice way of converting numbers and dates into strings using a variety of formatting options that are not available in FoxPro. The formatter can also deal with logical values and convert to string any value or object that supports a custom .ToString() method. If you’ve used style languages before you probably know about printf based formatting and .NET has a much more powerful version of this type of string formatter. Formatting Dates and Numbers The first function is FormatValue(), which is a super simple way of passing through a passed value and using the .NET ToString() basically just a way to call the .NET string function. This is especially useful for formatting dates and times with custom and UI display formats which is much richer than what you get from FoxPro natively.

************************************************************************ * FormatValue **************************************** FUNCTION FormatValue(lvValue,lcFormatString) LOCAL loBridge

IF ISNULL(lvValue) RETURN "null" ENDIF loBridge = GetwwDotnetBridge()

IF EMPTY(lcFormatString) RETURN loBridge.InvokeMethod(lvValue,"ToString") ENDIF

RETURN loBridge.InvokeMethod(lvValue,"ToString",lcFormatString)

The idea of this function is that you pass in a FoxPro value and that value is passed to .NET and mapped to a .NET type. A number becomes a System.Double, System.Decimal or System.Int32 a date turns in System.DateTime. The function then calls the ToString(string format) version of this method which supports many string formatting options.

Note the use of GetwwDotnetBridge() to retrieve a cached instance of wwDotnetBridge – this ensures that you don’t recreate the .NET environment everytime you call this function.

So what can you do with this? Let’s take a look:

DO wwDotnetBridge

? FormatValue(DATETIME(),"") * 6/6/2016 7:49:26 PM

? FormatValue(DATETIME(),"MMM dd, yyyy") * Jun 10, 2016

?FormatValue(DATETIME(),"MMMM dd, yyyy") * August 1, 2016

? FormatValue(DATETIME(),"HH:mm:ss") * 20:15:10

? FormatValue(DATETIME(),"h:m:s tt") * 8:5:10 PM

? FormatValue(DATETIME(),"MMM d @ HH:mm") * Aug 1 @ 20:44

? lcFormat ? FormatValue(DATETIME(),"") * Mon, 06 Jun 2016 22:41:33 GMT lcFormat = "u" && ISO time format Json/xml ? lcFormat ? FormatValue(DATETIME(),lcFormat) * 8:5:10 PM

*** Number formats ? FormatValue(2,"00") * 02

? FormatValue(12,"00") * 12

? FormatValue(1233.22, "c") * $1,233.22

? FormatValue(1233.2255, "n2") * $1,233.23

As you can see here you get a plethora of options for formatting numbers and dates, much more so than what’s available in FoxPro natively. For a reference of what’s available for dates and numbers you can check these links:

Date Formats: https://msdn.microsoft.com/en-us/library/8kb3ddd4(v=vs.110).aspx Number formats: https://msdn.microsoft.com/en-us/library/dwhawy9k(v=vs.110).aspx

It’s also nice that these values are automatically formatted using the current culture – if you’re running in German the formatting will automatically pick up German names for months and the number format automatically picks up the proper separators for 1000s and decimal points.

Another useful function is FormatString() which allows you to use a format string, and embed values into it. This is similar to TextMerge() in FoxPro except that you get the same formatting features shown above.

Here’s the FormatString() function:

************************************************************************ * FormatString **************************************** *** Function: Uses a string template to embed formatted values *** into a string. *** Assume: *** Pass: lcFormat - Format string use {0} - {10} for parameters *** lv1..lv10 - Up to 10 parameters *** Return: ************************************************************************ FUNCTION FormatString(lcFormat, lv1,lv2,lv3,lv4,lv5,lv6,lv7,lv8,lv9,lv10) LOCAL lnParms, loBridge lnParms = PCOUNT() loBridge = GetwwDotnetBridge()

DO CASE CASE lnParms = 2 RETURN loBridge.InvokeStaticMethod("System.String","Format",lcFormat,lv1) CASE lnParms = 3 RETURN loBridge.InvokeStaticMethod("System.String","Format",lcFormat,lv1,lv2) CASE lnParms = 4 RETURN loBridge.InvokeStaticMethod("System.String","Format",lcFormat,lv1,lv2,lv3) CASE lnParms = 5 RETURN loBridge.InvokeStaticMethod("System.String","Format",lcFormat,lv1,lv2,lv3,lv4) CASE lnParms = 6 RETURN loBridge.InvokeStaticMethod("System.String","Format",lcFormat,lv1,lv2,lv3,lv4,lv5) CASE lnParms = 7 RETURN loBridge.InvokeStaticMethod("System.String","Format",lcFormat,lv1,lv2,lv3,lv4,lv5,lv6) CASE lnParms = 8 RETURN loBridge.InvokeStaticMethod("System.String","Format",lcFormat,lv1,lv2,lv3,lv4,lv5,lv6,lv7) CASE lnParms = 9 RETURN loBridge.InvokeStaticMethod("System.String","Format",lcFormat,lv1,lv2,lv3,lv4,lv5,lv6,lv7,lv8) CASE lnParms = 10 RETURN loBridge.InvokeStaticMethod("System.String","Format",lcFormat,lv1,lv2,lv3,lv4,lv5,lv6,lv7,lv8,lv9) CASE lnParms = 11 RETURN loBridge.InvokeStaticMethod("System.String","Format",lcFormat,lv1,lv2,lv3,lv4,lv5,lv6,lv7,lv8,lv10 ) OTHERWISE THROW "Too many parameters for FormatString" ENDCASE

ENDFUNC The implementation of this function is a little more involved in that you have to pass each of the parameter explicitly to the .NET function. Since each overload is effectively a different method call there are all these overloads. There’s actually an easier way to just pass an array as a parameter, but while less code it’s actually slower from the FoxPro end, so this approach is most effective.

To call the function is very simple though:

? FormatString("Hey {0}, the date and time is: {1:MMM dd, yyyy - h:mm}","Rick",DATETIME())

Which produces:

Hey Rick, the date and time is: Jul 10, 2016 – 9:20

Any values that you pass are automatically converted to string using the .NET ToString() function, so if you pass a custom object that has a custom ToString() function you can render that value.

This function is not a replacement for TextMerge() in FoxPro as you can’t eval FoxPro expressions inline, but it’s very useful if you need to format dates and numbers as part of a string.

Create an Encryption Library This example demonstrates: Creating a wrapper library of static functions and calling them from FoxPro and creating a wrapper FoxPro class to simplify calling the library.

Encryption is a thorny topic in development and the tools available for FoxPro natively via COM are getting a bit long in the tooth. While there are libraries that can address a number of use cases these libraries are fixed in functionality. If there’s something you need that works slightly different, you generally can’t enhance the functionality yourself. By using .NET you can take advantage of the large set of encryption libraries that are relatively easy to use, and can easily be customized to create custom encryption solutions or expose common encryption formats.

Creating a .NET Wrapper Class Here’s a small .NET encryption library implementation that provides some common Encryption features. If there are other requirements or providers you might need it’s easy to extend and add those as needed. The class is easily callable from FoxPro and supports:

 Two Encryption/Decryption using TripleDES with Salted Hashes You typically use two-way encryption for encrypted content you want to store on disk. Encrypted text or binary files or encrypted data in configuration settings are examples where you store an encrypted value and then retrieve the decrypted value to present to the user.

 Hashing using SHAXXX and MD5 Unlike Encryption/Decryption, Hashing is a one-way, meaning you only encrypt data using a hash value that is stored. You then compare the computed hashvalue, but never retrieve the original unencrypted data. Hashing is typically used for storing things like passwords or other security values that are only validated but never require getting the original value.

 CheckSum Computation SHA256 and MD5 Provides computed checksums on files or binary values, which is quite common for validation of content sizes. public static class EncryptionUtils { ///

/// Replace this value with some unique key of your own /// Best set this in your App start up in a Static constructor /// public static string EncryptionKey = "4a3f131c";

///

/// Replace this Salt value with a unique value for your application /// by assigning it at application startup to a fixed value /// public static byte[] SaltBytes = new byte[] { 3, 20, 129, 11, 223, 1, 55, 53, 143, 112, 51, 39, 22, 189 };

///

/// Encodes a stream of bytes using DES encryption with a pass key. Lowest level method that /// handles all work. /// /// /// /// public static byte[] EncryptBytes(byte[] inputBytes, string encryptionKey) { if (encryptionKey == null) encryptionKey = EncryptionUtils.EncryptionKey;

TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider(); MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();

des.Key = hashmd5.ComputeHash(Encoding.ASCII.GetBytes(encryptionKey)); des.Mode = CipherMode.ECB;

ICryptoTransform Transform = des.CreateEncryptor();

byte[] Buffer = inputBytes; return Transform.TransformFinalBlock(Buffer, 0, Buffer.Length); }

///

/// Encrypts a string into bytes using DES encryption with a Passkey. /// /// /// /// public static byte[] EncryptBytes(string inputString, string encryptionKey) { return EncryptBytes(Encoding.ASCII.GetBytes(inputString), encryptionKey); }

///

/// Encrypts a string using Triple DES encryption with a two way encryption key.String is returned as Base64 encoded value /// rather than binary. /// /// /// /// public static string EncryptString(string inputString, string encryptionKey) { return Convert.ToBase64String(EncryptBytes(Encoding.ASCII.GetBytes(inputString), encryptionKey)); }

///

/// Decrypts a Byte array from DES with an Encryption Key. /// /// /// /// public static byte[] DecryptBytes(byte[] decryptBuffer, string encryptionKey) { if (decryptBuffer == null || decryptBuffer.Length == 0) return null;

if (encryptionKey == null) encryptionKey = EncryptionUtils.EncryptionKey;

TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider(); MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();

des.Key = hashmd5.ComputeHash(Encoding.ASCII.GetBytes(encryptionKey)); des.Mode = CipherMode.ECB;

ICryptoTransform Transform = des.CreateDecryptor();

return Transform.TransformFinalBlock(decryptBuffer, 0, decryptBuffer.Length); }

public static byte[] DecryptBytes(string decryptString, string encryptionKey) { return DecryptBytes(Convert.FromBase64String(decryptString), encryptionKey); }

///

/// Decrypts a string using DES encryption and a pass key that was used for /// encryption. /// Class wwEncrypt /// /// /// /// String public static string DecryptString(string decryptString, string encryptionKey) { try { return Encoding.ASCII.GetString(DecryptBytes(Convert.FromBase64String(decryptString), encryptionKey)); } catch { return string.Empty; } // Probably not encoded }

///

/// Generates a hash for the given plain text value and returns a /// base64-encoded result. Before the hash is computed, a random salt /// is generated and appended to the plain text. This salt is stored at /// the end of the hash value, so it can be used later for hash /// verification. /// /// /// Plaintext value to be hashed. /// /// /// Name of the hash algorithm. Allowed values are: "MD5", "SHA1", /// "SHA256", "SHA384", and "SHA512" (if any other value is specified /// MD5 hashing algorithm will be used). This value is case-insensitive. /// /// /// Optinoal salt bytes to apply to the hash. If not passed the /// raw encoding is used. /// /// /// Hash value formatted as a base64-encoded string. /// public static string ComputeHash(string plainText, string hashAlgorithm, byte[] saltBytes) { if (string.IsNullOrEmpty(plainText)) return plainText;

// Convert plain text into a byte array. byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText); byte[] plainTextWithSaltBytes;

if (saltBytes != null) { // Allocate array, which will hold plain text and salt. plainTextWithSaltBytes = new byte[plainTextBytes.Length + saltBytes.Length];

// Copy plain text bytes into resulting array. for (int i = 0; i < plainTextBytes.Length; i++) plainTextWithSaltBytes[i] = plainTextBytes[i];

// Append salt bytes to the resulting array. for (int i = 0; i < saltBytes.Length; i++) plainTextWithSaltBytes[plainTextBytes.Length + i] = saltBytes[i]; } else plainTextWithSaltBytes = plainTextBytes;

HashAlgorithm hash;

// Make sure hashing algorithm name is specified. if (hashAlgorithm == null) hashAlgorithm = "";

// Initialize appropriate hashing algorithm class. switch (hashAlgorithm.ToUpper()) { case "SHA1": hash = new SHA1Managed(); break;

case "SHA256": hash = new SHA256Managed(); break;

case "SHA384": hash = new SHA384Managed(); break;

case "SHA512": hash = new SHA512Managed(); break; default: // default to MD5 hash = new MD5CryptoServiceProvider(); break; }

// Compute hash value of our plain text with appended salt. byte[] hashBytes = hash.ComputeHash(plainTextWithSaltBytes);

return Convert.ToBase64String(hashBytes); }

///

/// Creates an SHA256 or MD5 checksum of a file /// /// /// SHA256,MD5 /// public static string GetChecksumFromFile(string file, string mode) { using (FileStream stream = File.OpenRead(file)) { if (mode == "SHA256") { var sha = new SHA256Managed(); byte[] checksum = sha.ComputeHash(stream); return Convert.ToBase64String(checksum); } if (mode == "MD5") { var md = new MD5CryptoServiceProvider(); byte[] checkSum = md.ComputeHash(stream); return Convert.ToBase64String(checkSum); } }

return null; }

///

/// Create a SHA256 or MD5 checksum from a bunch of bytes /// /// /// SHA256,MD5 /// public static string GetChecksumFromBytes(byte[] fileData, string mode) { using (MemoryStream stream = new MemoryStream(fileData)) { if (mode == "SHA256") { var sha = new SHA256Managed(); byte[] checksum = sha.ComputeHash(stream); return Convert.ToBase64String(checksum); } if (mode == "MD5") { var md = new MD5CryptoServiceProvider(); byte[] checkSum = md.ComputeHash(stream); return Convert.ToBase64String(checkSum); } }

return null; } }

These functions are super easy to call from FoxPro because they are just standalone static functions. You can use loBridge.InvokeMethodStatic() to access each of these functions with a single line of code which is much easier than trying to use lower level functions directly with wwDotnetBridge.

The Encryption/Decryption functions use TripeDES encryption and relies on a specific algorithm to create its retrievable encrypted values. You pass in an Encryption key which is first hashed and then applied in the actual Encryption process. The same Encryption key is used in the Decryption process that reverses this process by first hashing the encryption key passed and then decrypting the data. Two-way Encryption is almost always unique and application specific unless you use only the base algorithm.

The ComputeHash() functions provide one-way encryption to create a hash with a Salt value. The Salt is appended to the value before it is hashed using the specified algorithm. The preferred algorithm is SHA512, but other variations of SHA and MD5 can also be used. You can control the Salt value by passing in NULL which causes no salt to be applied. Use this if you need to match an exact hash created by another application. Otherwise you can apply a hash of your choice. The best way to create secure hashes is to use varying Salt values that are specific to a record. For example, when generating a password hash you might use the PK as the Salt value which results in every entry having a different salt value which is very difficult to crack.

Creating a FoxPro Wrapper Class Anyway, as you can see the .NET implementations use quite a bit of code and while it’s possible to use wwDotnetBridge to access these functions directly by creating a small .NET component it’s much easier to write this code and call into the high level methods from FoxPro with wwDotnetBridge.

Here’s a FoxPro PRG that wraps these .NET calls neatly into a single easily callable wwEncryption class.

************************************************************* DEFINE CLASS wwEncryption AS Custom ************************************************************* oBridge = null

************************************************************************ * Init **************************************** FUNCTION Init() this.oBridge = GetwwDotNetBridge() IF (this.oBridge == null) ERROR "Unable to load wwDotnetBridge" ENDIF

IF !this.oBridge.LoadAssembly("wwDotnetBridgeDemos.dll") ERROR "Unable to load wwDotnetBridgeDemos: " + this.oBridge.cErrorMsg ENDIF

ENDFUNC

************************************************************************ * EncryptString **************************************** *** Function: Encrypts a string with a pass phrase using TripleDES *** encryption. *** Assume: *** Pass: lcInput - String to encrypt *** lcEncryptionKey - pass phrase to encrypt string with *** Optional - if not uses global EncryptionKey *** Return: ************************************************************************ FUNCTION EncryptString(lcInput, lcEncryptionKey)

IF EMPTY(lcEncryptionKey) lcEncryptionKey = null ENDIF

RETURN this.oBridge.InvokeStaticMethod(; "Westwind.WebConnection.EncryptionUtils",; "EncryptString",lcInput,lcEncryptionKey) ENDFUNC * EncryptString

************************************************************************ * DecryptString **************************************** *** Function: Decrypts a string with a pass phrase using TripleDES *** encryption. The Decrypt function should use the same *** encryption key that was used to encrypt the string. *** Pass: lcEncryptedText - Encrypted text *** lcEncryptionKey - Same key that was used to encrypt *** Return: ************************************************************************ FUNCTION DecryptString(lcEncryptedText, lcEncryptionKey)

IF EMPTY(lcEncryptionKey) lcEncryptionKey = null ENDIF

RETURN this.oBridge.InvokeStaticMethod(; "Westwind.WebConnection.EncryptionUtils",; "DecryptString",lcEncryptedText,lcEncryptionKey) ENDFUNC

************************************************************************ * ComputeHash **************************************** *** Function: *** Pass: lcText - Text to hash, or binary data (type Q) *** lcAlgorith - MD5*,SHA1,SHA256 *** Return: ************************************************************************ FUNCTION ComputeHash(lcText, lcAlgorithm, lvHashSalt) LOCAL lcSaltType

IF EMPTY(lcAlgorithm) lcAlgorithm = "MD5" ENDIF lcSaltType = VARTYPE(lvHashSalt) DO CASE CASE (lcSaltType = "Q") * do nothing - already binary CASE (lcSaltType = "C") #IF VERSION(5) > 800 lvHashSalt = CAST(lvHashSalt as Blob) #ELSE lvHashSalt = CREATEBINARY(lvHashSalt) #ENDIF OTHERWISE lvHashSalt = null ENDCASE

RETURN this.oBridge.InvokeStaticMethod(; "Westwind.WebConnection.EncryptionUtils",; "ComputeHash",lcText,lcAlgorithm,lvHashSalt) ENDFUNC

************************************************************************ * GetCheckSumFromFile **************************************** *** Function: Calculates a CheckSum from a file using MD5 or SHA256 *** Assume: *** Pass: lcFilename - File to check *** lcMode - "SHA256","MD5"* *** Return: ************************************************************************ FUNCTION GetCheckSumFromFile(lcFilename, lcMode)

IF EMPTY(lcMode) lcMode = "MD5" ENDIF

RETURN this.oBridge.InvokeStaticMethod(; "Westwind.WebConnection.EncryptionUtils",; "GetChecksumFromFile",lcFilename, lcMode) ENDFUNC * GetCheckSumFromFile

************************************************************************ * GetChecksum **************************************** *** Function: Calculates a CheckSum from a string or binary blob using MD5 or SHA256 *** Assume: *** Pass: lqBytes - Binary data to check *** lcMode - "SHA256","MD5"* *** Return: ************************************************************************ FUNCTION GetChecksum(lqBytes, lcMode)

IF VARTYPE(lqBytes) = "C" lqBytes = CAST(lqBytes as Blob) ENDIF IF EMPTY(lcMode) lcMode = "MD5" ENDIF

RETURN this.oBridge.InvokeStaticMethod(; "Westwind.WebConnection.EncryptionUtils",; "GetChecksumFromBytes",lqBytes, lcMode) ENDFUNC

************************************************************************ * SetEncryptionKey **************************************** *** Function: Sets the default Encryption key for the EncryptString *** DecryptString methods if the pass phrase is not passed. *** Assume: Set during application startup which makes the key *** global for the application. *** Pass: lcKey - The pass phrase key to use *** Return: nothing ************************************************************************ FUNCTION SetEncryptionKey(lcKey)

RETURN this.oBridge.SetStaticProperty(; "Westwind.WebConnection.EncryptionUtils",; "EncryptionKey",lckey) ENDFUNC

ENDDEFINE

Calling the Encryption Functions With this class in place it’s very easy to do encryption.

Encrypt Decrypt: lcOriginal = "Test String"

? lcOriginal lcEncrypted = o.Encryptstring(lcOriginal,lcSecret) ? lcEncrypted lcDecrypted = o.Decryptstring(lcEncrypted,lcSecret) ? lcDecrypted

? ? "Encryption using globally assigned key:" o.SetEncryptionKey(lcSecret + "_explicit")

? lcOriginal lcEncrypted = o.Encryptstring(lcOriginal) && no key ? lcEncrypted lcDecrypted = o.Decryptstring(lcEncrypted) && no key ? lcDecrypted

EncryptString and DecryptString take an optional encryption key parameter. If you don’t specify it a global static value is used. You can also set this global value using SetEncryptionKey() as shown in the second example. Ideally you either compile the Encryption key directly into your .NET class, or else you set the value once on startup in your application.

Keep in mind that the Encrypt and Decrypt functions only work as long as the key stays consistent. Change the encryption key and you will no longer be able to decrypt an encrypted value. So you can effectively not change the key, unless you run an explicit key update routine on all of the data affected.

Compute Hash: lcSecretSalt = "#@Sek|+!223"

? "Hash using no Salt (raw algo):" ? o.ComputeHash(lcOriginal,"MD5") ? o.ComputeHash(lcOriginal,"SHA256") ? o.ComputeHash(lcOriginal,"SHA512")

? ? "Hash using explicit Salt:" ? o.ComputeHash(lcOriginal2,"MD5",lcSecretSalt) ? o.ComputeHash(lcOriginal2,"SHA256",lcSecretSalt) ? o.ComputeHash(lcOriginal2,"SHA512",lcSecretSalt)

Hashes are computed using a one-way algorithm and are meant to be compared. Unlike two-encryption, you can’t retrieve the original value with a hash.

CheckSums: ? "Checksum From File:" lcFileName = LOWER(FULLPATH("wwdotnetbridge.prg")) ? lcFileName ? "SHA256: " + o.GetChecksumFromFile(lcFilename, "SHA256") ? "MD5: " + o.GetChecksumFromFile(lcFilename, "MD5")

? ? "Checksum From string (same file as above as string)" lcData = FILETOSTR(FULLPATH("wwDotnetBridge.prg")) ? "SHA256: " + o.GetChecksum(lcData, "SHA256") ? "MD5: " + o.GetChecksum(lcData, "MD5")

? ? "Checksum From Bytes:" lqBytes = CreateBinary("#UltraSecretText#") && CAST("xxx" as blob) ? "SHA256: " + o.GetChecksum(lqBytes, "SHA256") ? "MD5: " + o.GetChecksum(lqBytes, "MD5")

Checksums are really just hashes similar to hashes computed with ComputeHash, but you typically create them for files or binary data, so there are two functions GetCheckSumFromFile() and GetCheckSum() which works with binary or string data.

Base64 or BinHex Output By default the output is generated as a string in base 64 format. Alternately you can use BinHex formatting which packs hex values into a string two characters for each byte. Hashes are often represented as BinHex values rather than Base 64 so you can choose the format by using a global switch: o.SetBinHexMode() to switch on BinHex mode. To turn it off and go back to Base64: o.SetBinHexMode(.F.)

Markdown Parsing This example demonstrates: Calling a third party library, caching it for more efficient reuse

Required dependency: CommonMark.NET

Markdown has become wildly popular as an input format that can be used to represent HTML text. With its minimal syntax to represent common HTML expressions using simple punctuation characters it’s an ideal format for any light duty text input, social content like message boards and blogs, and of course Markdown is rampant when it comes to documentation on social programming sites like GitHub or BitBucket.

Here's an example of what Markdown text looks like using the standalone West Wind Markdown Monster Editor so you can see the rendered HTML output:

If you’re new to Markdown you can go to the following link to see a simple introduction of the most common formatting options.

 Markdown Editing

So if we want to use Markdown in your own applications as a text input format, you need a Markdown parser to turn that markdown text into HTML and we’re going to use a .NET component to do it. I’m going to use CommonMark.NET to handle the markdown conversion.

The first step is to get the component.

Using NuGet to get .NET Assemblies (dlls) These days most third party .NET components are distributed via NuGet which is a package manager for .NET that allows you to easily add any registered component to a .NET project. CommonMark.NET is no different and unless you want clone the entire project from GitHub and build it yourself into a DLL, NuGet is the way to get the package. Nuget installs all the dependencies required to run a component into the BIN folder. From there you can either reference the DLL(s) directly or copy them to your own projects for later distribution (recommended).

To use NuGet you need a create .NET project in Visual Studio. We’ll use this project for a few other things discussed later on as well. You can get the free Visual Studio Community Edition which is a full version of VS Professional.

 Open Visual Studio  Create a new Class Library Project (File | New | Class Library (Full Framework))  I’ll call it wwDotnetBridgeDemos  Go to the References Node and choose Manage NuGet Packages  Right Click and Choose

 In the NuGet Manager Select the Browse tab  Type CommonMark into the search box  Select CommonMark.NET and click Install

Once you’ve completed this step there will be a Packages folder in your project root that holds all the dependencies used by your project. In there should be a folder for the CommonMark.NET and within a version for the .NET platform you’re running on – in this case .NET 4.5 (NET4.5). You can find the actual .NET assembly DLL in that folder.

We can either reference the assembly in this path or – what I prefer – is just lift it out of that folder and copy it to my actual application’s folder. After all I have to eventually distribute the file so I need a local copy. There’s an easier way to do this if you build a custom assembly for your application anyway. We’ll do that with the next component. For now – lets copy the DLL into the sample folder.

Once you’ve copied the assembly we can access the Markdown parser using the following code: do wwDotNetBridge

LOCAL loBridge as wwDotNetBridge loBridge = CreateObject("wwDotNetBridge","V4") loBridge.LoadAssembly("CommonMark.dll")

TEXT TO lcMarkdown NOSHOW This is some sample Markdown text. This text is **bold** and *italic*.

* List Item 1 * List Item 2 * List Item 3

Great it works!

> ### @icon-info-circle Examples are great > This is a block quote with a header ENDTEXT lcHtml = loBridge.InvokeStaticMethod("CommonMark.CommonMarkConverter","Convert",lcMarkdown,null) ? lcHtml

Which produces the expected HTML:

This is some sample Markdown text. This text is bold and italic.

  • List Item 1
  • List Item 2
  • List Item 3

Great it works!

@icon-info-circle Examples are great

This is a block quote with a header

Cool -easy right?

So now we need to make this a bit more reusable and the easiest way is to put this into a class that you can store globally so you don’t have to reload the library each time.

Here’s a MarkdownParser class:

************************************************************* DEFINE CLASS MarkDownParser AS Custom ************************************************************* *: Author: Rick Strahl *: (c) West Wind Technologies, 2013-2016 *:Contact: http://www.west-wind.com ************************************************************* oMarkDown = null oBridge = null lEncodeScriptBlocks = .T.

************************************************************************ * Init **************************************** FUNCTION Init() LOCAL loBridge as wwDotNetBridge loBridge = GetwwDotnetBridge() this.oBridge = loBridge IF ISNULL(THIS.oBridge) RETURN .F. ENDIF

IF !loBridge.LoadAssembly("CommonMark.dll") RETURN .F. ENDIF

ENDFUNC * Init

************************************************************************ * Parse **************************************** FUNCTION Parse(lcMarkdown) LOCAL lcHtml, loScriptTokens

IF !this.lEncodeScriptBlocks loScriptTokens = TokenizeString(@lcMarkdown,"<%","%>","@@SCRIPT") ENDIF lcHtml = this.oBridge.InvokeStaticMethod("CommonMark.CommonMarkConverter","Convert",lcMarkdown,null)

IF !THIS.lEncodeScriptBlocks lcHtml = DetokenizeString(lcHtml,loScriptTokens,"@@SCRIPT") ENDIF lcHtml = TRIM(lcHtml,0," ",CHR(13),CHR(10),CHR(9))

RETURN lcHTML ENDFUNC * Parse

ENDDEFINE

So now we can make calling the markdown parsing much simpler: loParser = CREATEOBJECT("MarkdownParser") lcHtml = loParser.Parse(lcMarkdown)

Caching an Instance You’ll want to cache the parser instance globally somewhere so you don’t have to recreate it every time you want to parse Markdown as this affects performance. For example, I use the Markdown parsing in Web Connection on my Message Board for every message rendered in a thread. There can be 20 separate markdown documents to parse which is embedded into a script.

To make this work I want to have a simple function like so: <%= Markdown(TMessages.Message) %> To do this I create a Markdown() function that wraps the object reference in a public variable:

************************************************************************ * Markdown **************************************** *** Function: Converts Markdown to HTML *** Assume: Caches instance in __MarkdownParser *** Pass: lcMarkdown - text to convert to HTML from Markdown *** lnMode - 0/.F. - standard, 2 extended, 1 - standard, leave scripts, 3 - extended leave scripts *** Return: ************************************************************************ FUNCTION Markdown(lcMarkdown, lnMode, llReload) LOCAL loMarkdown, lcClass

IF llReload OR VARTYPE(__MarkdownParser) != "O" IF EMPTY(lnMode) lnMode = 0 ENDIF

lcClass = "MarkdownParser" IF lnMode = 2 lcClass = "MarkdownParserExtended" ENDIF

loMarkdown = CREATEOBJECT(lcClass) PUBLIC __MarkdownParser __MarkdownParser = loMarkdown

IF lnMode = 1 OR lnMode = 3 __MarkdownParser.lEncodeScriptBlocks = .F. ENDIF ELSE loMarkdown = __MarkdownParser ENDIF

RETURN loMarkdown.Parse(lcMarkdown) ENDFUNC * Markdown

There are a few new things in this standalone function. First the sample code provided with this article includes a MarkdownParserExtended class that includes special parsing to handle code snippets, font- awesome icons, and externalizing links. So this function accepts a lnMode parameter to reflect which parser to use.

The main point of this function is to cache the Markdown parser instance in a PUBLIC variable that is created if it doesn’t exist. If the variable exist the existing parser is used, otherwise a new instance is created and stored in the PUBLIC variable. FWIW, this is a very common pattern to use with .NET components that you use frequently. Caching instances can have dramatic performance improvements for frequently accessed components. In the messageboard for example the parser is frequently accessed hundreds of times a second (a few people on a large thread) so it really pays to cache the instance. The same approach is used by GetwwDotnetBridge() and you can use the same for your own components.

So with this code in place we now have a very simple way to parse our Markdown:

lcHtml = Markdown(lcMarkdown)

Or in side of a Web Connection application in a loop:

<% SCAN %> …

<%= Markdown(TThreadMessages.Body) %>
<% ENDSCAN %>

Markdown rocks and if you aren’t considering it for a text input format for any sort of output that needs to be presented nicely, I encourage you check it out. And now with a simple Markdown() function in place you can easily take advantage of it in your own applications.

Spell-check Words This example demonstrates: Calling a third party library and creating a wrapper FoxPro class.

You ever need to do spell checking in an application? I recently had a need in an editor style application that could iterate over all the words in the editor and spell check them. You could then pop up an a drop down list of words and select from a number of suggestions.

For .NET there’s a NHunspell library that makes it easy to do programmatic (but not visual control) spell checking, suggestions as well as adding new words to the dictionaries. Dictionaries use the standard Open Office format for dictionarkies, so you can use any Open Office compatible dictionary and different languages. The NHUnspell library is based on the HUnspell library which is a native library for many platforms. NHUnspell is simply a wrapper around the native component and exposes the native functions to .NET.

You can install the NuGet package into the project with:

PM> Install-Package NHunspell

The package contains both the .NET assembly (nhunspell.dll) and the two native assemblies (x86 and x64). The native assemblies have to be in the current application directory, otherwise they are not found on the FoxPro or System path.

Creating a FoxPro Wrapper As is usually the case I prefer to create a wrapper class for .NET components I use. Even if components are relatively simple, having a class can encapsulate all the logic of loading the required dependencies and default settings. This class is simple, but there are a few things to handle for suggestions so a wrapper is a good way to go.

************************************************************* DEFINE CLASS HunspellChecker AS Custom ************************************************************* #IF .F. *:Help Documentation *:Topic: Class HunspellChecker

*:Description: Checks against a dictionary to see if a work is valid. Can provide suggestions for misspelled words.

*:Example: loSpell = CREATEOBJECT("HunspellChecker","en_US",".\dictionaries")

? loSpell.Spell("Testing") && true ? loSpell.Spell("Tesdting") && false loSug = loSpell.Suggest("Tesdting") && collection of suggestions

*:Remarks: The Hunspellx86.dll *MUST* reside in the EXE startup folder. IF you're running under VFP9.exe the DLL must live in that folder or else it won't be found. It does not respect the FoxPro path unfortunately (limitation of the .NET interface of the third party provider).

*:ENDHELP #ENDIF oBridge = null oSpell = null cLanguage = "en_US" cDictionaryFolder = "" && root

************************************************************************ * init **************************************** FUNCTION init(lcLang, lcDictFolder)

IF EMPTY(lcLang) lcLang = this.cLanguage ENDIF IF EMPTY(lcDictFolder) lcDictFolder = this.cDictionaryFolder ENDIF this.oBridge = GetwwDotnetBridge() IF ISNULL(this.oBridge) ERROR "Failed to load HUnspell: " + this.oBridge.cErrorMsg ENDIF

IF !this.oBridge.LoadAssembly("NHunspell.dll") ERROR "Failed to load HUnspell: " + this.oBridge.cErrorMsg ENDIF

IF !EMPTY(lcDictFolder) lcDictFolder = ADDBS(lcDictFolder) ELSE lcDictFolder = "" ENDIF this.oSpell = this.oBridge.CreateInstance("NHunspell.Hunspell",; lcDictFolder + lcLang + ".aff",; lcDictFolder + lcLang + ".dic")

IF FILE(lcDictFolder + lcLang + "_custom.txt") lcFile = FILETOSTR(lcDictFolder + lcLang + "_custom.txt") lcFile = STRTRAN(lcFile,CHR(13) + CHR(10),CHR(10)) lcFile = STRTRAN(lcFile,CHR(13),CHR(10)) LOCAL ARRAY laLines[1] LOCAL lnX, lnLine lnLines = ALINES(laLines,lcFile,1 + 4,CHR(10)) FOR lnX = 1 TO lnLines this.oSpell.Add(laLines[lnx]) ENDFOR ENDIF

IF ISNULL(this.oSpell) ERROR "Failed to load HUnspell: " + this.oBridge.cErrorMsg ENDIF

ENDFUNC * init

************************************************************************ * Spell **************************************** *** Function: Checks to see if a word is a known word in the dictionary ************************************************************************ FUNCTION Spell(lcWord) LOCAL llResult

IF ISNULLOREMPTY(lcWord) OR LEN(lcWord) = 1 RETURN .T. ENDIF llResult = this.oSpell.Spell(lcWord)

RETURN llResult ENDFUNC * Spell

************************************************************************ * Suggest **************************************** *** Function: Gives back a collection of word suggestions for *** the passed in word ************************************************************************ FUNCTION Suggest(lcWord) LOCAL loWords, lnx loCol = CREATEOBJECT("collection") loWords = this.obridge.InvokeMethod(this.oSpell,"Suggest",lcWord) lnCount = this.oBridge.GetProperty(loWords,"Count")

FOR lnX = 0 TO lnCount -1 lcWord = this.oBridge.GetIndexedProperty(loWords,lnx) loCol.Add( lcWord ) ENDFOR

RETURN loCol ENDFUNC * Suggest

************************************************************************ * AddWordToDictionary **************************************** FUNCTION AddWordToDictionary(lcWord, lcLang) lcFile = "editors\" + lcLang + "_custom.txt" AppendToFile(lcWord + CHR(13) + CHR(10),lcFile) this.oSpell.Add(lcWord)

ENDFUNC * AddWordToDictionary

************************************************************************ * Destroy **************************************** FUNCTION Destroy()

*** MUST dispose to release memory for spell checker this.oSpell.Dispose() this.oSpell = null

ENDFUNC * Destroy

ENDDEFINE *EOC HunspellChecker

The Init function is responsible for setting up the NHunspell library by loading the assembly and loading up an oSpell instance with a specific dictionary. You basically load the library first, then make multiple calls for translation. Make sure you cache this instance on a form or some sort of global object as loading the dictionary initially is not super fast – you don’t want to load the dictionary for each spell check or suggestion but rather use a cached instance. The Spell() function is super easy as it’s just a pass through call to the underlying library. You pass in a string of the word to spell and get back a .T. or .F. if the value was found in the dictionary.

The Suggest() method is a bit more complex as it returns a generic collection. Generics in .NET are not directly accessible by COM Interop that FoxPro uses to retrieve values off an object so instead an indirect method of wwDotnetBridge has to be used. In this case I can use GetIndexedProperty() to return the index value at the specified index to get each of the suggestions for the misspelled word. The strings are then moved into a plain FoxPro collection that’s easy to consume in FoxPro.

You can also AddToDictionary() which allows you to add words to a dictionary. You can configure the name of the custom dictionary file which is used to hold the custom values. You specify this file at load time and NHUnspell automatically loads the custom entries from the custom file and writes to it when you add new dictionary entries.

Examples of calling the SpellChecking Library Let’s start by loading the library:

DO wwDotnetBridge SET PROCEDURE TO HunSpellChecker ADDITIVE loSpell = CREATEOBJECT("HunspellChecker","en_US",".\bin")

This loads the FoxPro class with an en_US dictionary from the .\bin folder relative to the current path. If you have additional dictionaries installed (.dic and .aff files) in the folder you can choose any of the dictionaries there. I’ve included English, German, French and Spanish dictionaries.

Check Spelling To check spelling you can do: ? loSpell.Spell("Testing") ? loSpell.Spell("Tesdting")

The first call returns .T. the second .F. as the second value is not a valid word.

Suggestions If you find a word that’s not spelled right you can bring up suggestions for it.

lcWord = "aren'tt" loSug = loSpell.Suggest(lcWord)

? loSug.Count FOR EACH lcWord in loSug ? lcWord ENDFOR

This shows you all the suggestions for word aren’tt. The suggestions aren’t, aren’t, weren’t.

Between these two functions you can easily prompt for a misspelling and then present a list to allow the user to pick a word to pick from a list.

Putting it together The following is a non-visual example that demonstrates how you can build the beginnings of a spell checker (minus the UI because that depends on how you track words in the UI). The following code takes a string of text, breaks it into words and spell checks each word. If a misspelled word is found it shows the word along with all of its suggested fixes.

? "*** Text to check:" TEXT TO lcText These ae somme of the worsd that are badly mispeled.

I can't imaggine that somebody can spel so bad.

ENDTEXT

loSpell = CREATEOBJECT("HunspellChecker","en_US",".\bin") loWords = GetWords(lcText)

LOCAL lnX ? "*** Mispelled words:" FOR lnX = 1 TO loWords.Count lcWord = loWords.Item(lnX) lcSuggest = ""

IF (!loSpell.Spell(lcWord)) loSug = loSpell.Suggest(lcWord) IF (loSug.Count > 0)

FOR lnY = 1 TO loSug.Count lcSuggest = lcSuggest + loSug.Item(lnY) + " " ENDFOR ENDIF

? lcWord + " - " + lcSuggest ENDIF

ENDFOR

FUNCTION GetWords(lcText) LOCAL loWords, lnCount, lnX, lcWord loWords = CREATEOBJECT("Collection") lnCount = ALINES(laWords,lcText,1," ",CHR(13),CHR(10)) FOR lnX = 1 TO lnCount lcWord = ALLTRIM(laWords[lnx]) IF EMPTY(lcWord) LOOP ENDIF lcWord = CHRTRAN(lcWord,".,:!?","") loWords.Add(lcWord) ENDFOR

RETURN loWords

This produces:

You can see some of the suggestions generated here and it should be easy to see how you can generate a UI that shows the mispelled word and its replacement after the user selects one.

How I use this Class This class has a practical use case for me – I use it in West Wind Html Help Builder which has a rich browser based Markdown editor embedded in a Web Browser control. The editor has a custom spell checking engine that allows checking each word for spelling errors which get underlined as well as a pop up that allows showing of suggestions. My custom JavaScript logic handles the word parsing in this case (which is one of the reasons I use it instead of a FoxPro textbox) and supports the styling to highlight misspelled words, but the actual spell checking calls into the FoxPro host application that uses the HUnspellChecker class to handle the spell checks (used for the underlining) and the suggestions (when the user right clicks on a misspelling, and the adding of a new word (when they click the add button):

Ironically the process of calling out into FoxPro and .NET from JavaScript is massively faster than using a JavaScript based version of Hunspell that exists, but is very, very slow in parsing the dictionary initially and very slow on the frequent spell checking operations that occur.

My FoxPro code that handles the spell checking looks like this:

LPARAMETERS lcWord, lcLang

IF EMPTY(lcLang) lcLang = "en_US" ENDIF

IF ISNULL(THIS.oSpell) this.oSpell = CREATEOBJECT("HunspellChecker",lcLang,"Editors") ENDIF

RETURN this.oSpell.Spell(lcWord)

This code lives on the Web Browser Editor control which has a lifetime of the editor so it stays loaded. Notice that I cache the spell checker and load it only when requested for the first and cache it after that.

Likewise to return suggestions when the user right clicks looks like this:

LPARAMETERS lcWord, lcLang LOCAL loWords as Collection, lcWords as string

IF EMPTY(lcLang) lcLang = "en_US" ENDIF

IF ISNULL(THIS.oSpell) this.oSpell = CREATEOBJECT("HunspellChecker",lcLang,"Editors") ENDIF loWords = this.oSpell.Suggest(lcWord) lcWords="" FOR lnx = 1 to loWords.Count lcWords = lcWords + loWords.Item(lnx) + "," ENDFOR

RETURN RTRIM(lcWords,",")

The Suggest() method is very simple to call and the collection is turned into a string to return to JavaScript. The string happens to be much faster and more lightweight and more easily readable by JavaScript than a FoxPro collection so this is the value of choice.

Using a SpellChecker is kind of an edge case for most applications, but it’s good to know that you can have access to the functionality very easily and very efficiently if you ever need it.

Upload and Download Files using SFTP This example demonstrates: Using a third party library and creating a .NET wrapper, then calling that from FoxPro. Also: Wrapping the .NET wrapper in a FoxPro class for easier consumption and error handling.

Required .NET Dependency: SSH.NET

Secure FTP (FTP over SSL) is a feature that is often requested, as it is something that’s not directly available in any of the native Windows WinInet or WinHTTP libraries provided by Microsoft.

However there are a number of .NET libraries available that allow you to use SFTP (and FTPS which is something different!) and you can utilize these libraries from FoxPro code using .NET as a bridge. So far I’ve shown how to directly call simple .NET methods directly using wwDotnetBridge. In both the Markdown() and String.Format() cases we called static functions directly.

In other cases however accessing the functionality is more complex or in some cases requires methods that accept parameters that are not so easy to create and manage in FoxPro – even with wwDotnetBridge. In those cases it makes sense to create a small wrapper library around base functionality in .NET and then call that component from FoxPro.

This has two advantages:

 Simplifies calling the .NET logic using ‘native’ features  Simplify the FoxPro code by calling the wrapper with ‘safe’ parameters

The following example uses the SSH.NET library to access SFTP content. As with the Markdown functionality you can use NuGet to pull down this library and then copy it into an application folder, which in the samples here is the BIN folder below the FoxPro samples.

From the Package Manager Console you can do:

PM> install-package SSH.NET

Or you can load the library from the interactive NuGet Package Manager as we did for the Markdown component.

Creating a .NET Wrapper Class

Once the component has been installed in the project, let’s start by creating a new class in the existing wwDontetBridgeDemos project in Visual Studio:

 Open the wwDotnetBridgeDemos Project in Visual Studio  Add a new Class to the project and call it SecureFtpClient

Let’s start with a DownloadFile() method that downloads a file from an SFTP web site. This method uses the SftpClient object from the SSH.NET library and its DownloadFile() method to download a file directly to disk: namespace wwDotnetBridgeDemos { public class SecureFtpClient { public string ErrorMessage { get; set; }

public bool DownloadFile(string remoteFilename, string localFilename, string host, int port, string username, string password) { try { using (var sftp = new SftpClient(host, port, username, password)) { sftp.Connect();

using (var file = File.OpenWrite(localFilename)) { sftp.DownloadFile(remoteFilename, file); }

sftp.Disconnect(); } } catch (Exception ex) { ErrorMessage = ex.Message; return false; } return true; } } }

This code needs to be compiled into an assembly so you can use it and then copied into our working folder for our sample application so we can access it. To do this:

 Build your project  Change the Build Output Options to point at the FoxPro project bin folder:

 Build the project (Ctrl-Shift-B)

(note the folder normalizes to the relative path of ..\..\bin after you save rather than the full folder name you entered which is normal and works as long as you don’t change the relative project structure)

This will compile the project and place the wwDotnetBridgeDemos.dll into the bin sub-folder below our FoxPro code.

Call the .NET Class from FoxPro

To call this code from FoxPro you can now use the following code:

DO wwDotNetBridge LOCAL loBridge as wwDotNetBridge loBridge = GetwwDotnetBridge()

*** Using local test server: http://labs.rebex.net/tiny-sftp-server lcHost = "127.0.0.1" lnPort = 22 lcUsername = "tester" lcPassword = "password" lcRemoteFile = "sailbig.jpg" lcLocalFile = "c:\temp\sailbig.jpg"

ERASE (lcLocalFile)

? loBridge.Loadassembly("bin\wwDotnetBridgeDemos.dll") loFtp = loBridge.CreateInstance("wwDotnetBridgeDemos.SecureFtpClient")

IF (!loFtp.DownloadFile(lcRemoteFile,lcLocalFile,lcHost,lnPort,lcUsername,lcPassword)) ? "Download failed: " + loFtp.ErrorMessage RETURN ENDIF

IF FILE(lcLocalFile) ? "File downloaded..." ELSE ? "File not downloaded: " + loFtp.ErrorMessage ENDIF

In this example I’m using a local SFTP server (http://labs.rebex.net/tiny-sftp-server) that you can use for local testing of SFTP traffic. You basically start up this server and tell it where it should find it’s ‘data’ directory and you can then up and download files to this folder. It’s a quick and easy way to see if your code works downloading and uploading against a working SFTP server.

As before you start by loading the wwDotnetBridge library and then loading an assembly. Here we’re loading our custom assembly. Not that when the code compiled it automatically also copied any dependent assemblies which includes the SSH.NET assembly into the bin folder. We don’t need to explicitly copy it, nor do we need to explicitly call LoadAssembly() on it as it is loaded automatically by wwDotnetBridgeDemos.dll simply because it is a dependency.

You then create an instance of the fully qualified classname which is the namespace + the class name separated by a ‘.’. You can then call the DownloadFile() method we created. Since this is a simple method with simple parameters we can simply call this method directly – no need to call loBridge.InvokeMethod().

If all goes well you should see the File downloaded message. Otherwise you should see an error message displayed.

Adding More Functionality in .NET

To round out the class let’s add a few more methods for UploadFile(), ListFiles() and DeleteFile() in the .NET class: public bool UploadFile(string localFilename, string remoteFilename, string host, int port = 22, string username = null, string password = null) { try { using (var sftp = new SftpClient(host, port, username, password)) { sftp.Connect();

using (var file = File.OpenRead(localFilename)) { sftp.UploadFile(file, remoteFilename); }

sftp.Disconnect(); } } catch (Exception ex) { ErrorMessage = ex.Message; return false; }

return true; } public SftpFile[] ListFiles(string filespec, string host, int port = 22, string username = null, string password = null) { if (string.IsNullOrEmpty(filespec)) filespec = string.Empty;

using (var sftp = new SftpClient(host, port, username, password)) { sftp.Connect();

var files = sftp.ListDirectory(filespec).ToArray();

sftp.Disconnect();

return files; } } public bool DeleteFile(string remoteFilename, string host, int port = 22, string username = null, string password = null) { using (var sftp = new SftpClient(host, port, username, password)) { try { sftp.Connect(); sftp.DeleteFile(remoteFilename); sftp.Disconnect(); } catch (Exception ex) { ErrorMessage = ex.Message; return false; } } return true; }

You can now call all of these methods in a small little program to download a file, upload a file, list files and then delete the uploaded file on the server from FoxPro:

*** Using local test server: http://labs.rebex.net/tiny-sftp-server lcHost = "127.0.0.1" lnPort = 22 lcUsername = "tester" lcPassword = "password" lcRemoteFile = "sailbig.jpg" lcLocalFile = "c:\temp\sailbig.jpg"

ERASE (lcLocalFile)

*** Download File ? loBridge.Loadassembly("bin\wwDotnetBridgeDemos.dll") ? loBridge.cErrorMsg loFtp = loBridge.CreateInstance("wwDotnetBridgeDemos.SecureFtpClient") ? loBridge.cErrorMsg

? loFtp ? loBridge.cErRORMSG

IF (!loFtp.DownloadFile(lcRemoteFile,lcLocalFile,lcHost,lnPort,lcUsername,lcPassword)) ? "Download failed: " + loFtp.ErrorMessage RETURN ENDIF

IF FILE(lcLocalFile) ? "File downloaded..." ELSE ? "File not downloaded..." ENDIF lcUploaded = STRTRAN(lcRemoteFile,".","_" + TRANSFORM(SECONDS())+ ".") IF (!loFtp.UploadFile(lcLocalFile,lcUploaded,lcHost,lnPort,lcUsername,lcPassword)) ? "Upload failed: " + loFtp.ErrorMessage RETURN ENDIF ? "File uploaded..."

*** List all the files (array returned as COM Array) loFiles = loBridge.InvokeMethod(loFtp,"ListFiles","",lcHost,lnPort,lcUsername,lcPassword) ? loFiles.Count FOR lnX = 1 TO loFiles.Count loFile = loFiles.Item(lnX) IF !ISNULL(loFile) ? loFile.Name ENDIF ENDFOR

IF loFtp.DeleteFile(lcUploaded,lcHost,lnPort,lcUsername,lcPassword) ? "File deleted..." ENDIF Calling a SOAP Web Service with .NET One very common use case for me – and a number of the customers I deal with – is using .NET to call SOAP Web Services. .NET includes a rich set of built-in for calling Web Services using two separate technologies – the classic .NET Web Service client and the Windows Communication Framework (WCF) client. The former is easier to use and works with standard SOAP 1.x services, while WCF can work with more complex WS* services that require message encryption, server certificates, message routing and asynchronous messages. Whenever possible I try to use the ‘old’ Web Service client, because it’s vastly easier to us and work with.

So let’s look at a simple example of calling a Web Service with .NET by creating a Web Service client and then calling it from FoxPro with wwDotnetBridge. I’m going to be using an existing AlbumViewer Web Service that I previously created on my local machine. It contains albums and artists that you can look up. The service lives at: http://localhost/albumviewerservices or if using IIS Express from Visual Studio (you have to manually launch it): http://localhost:5608/

The Web Service is locally implemented and uses FoxPro data which is stored in the FoxPro folder. For more info on how the service is implemented you can check out this document:

 Creating and Consuming FoxPro Web Services using .NET

This document also has more detail on what I describe here briefly.

Import a Web Service into a .NET Project To start we’ll import the Web Service into a new .NET Class Library Project in Visual Studio. You can also do this from the command line using WSDL.EXE which is a tool that ships with .NET.

First create a new .NET Class Library Project:

 On the Solution node in the project click Add | New Project  Select Visual C# | Class Library  Name it AlbumViewerProxy

This will create a new project in the existing solution. If you’re creating a new project just choose ClassLibrary as you top level project when you start out.

Once the project has been created go the References node, right click and select Add Service Reference.

Because WSDL based services are old they are a little tricky to find. You have to click through two dialogs to get to the actual Service import dialog.

On the first two dialogs just click the Add Web Reference buttons on the bottom:

Figure – Skip over the standard dialogs to get to the ‘classic’ Web services

You don’t need to fill in anything because this dialog and the next are simply ignored.

The final dialog that pops up is the one where we actually can specify the service URL by providing a link to the WSDL.

Figure – Adding a classic Web Service reference. Make sure you use a sensible Reference name which translates into the namespace used. Don’t use the same name as the service!

Once you’ve done this Visual Studio has added a new proxy class into your project. Compile the project first then you can look at the actual service proxy that was created in the Visual Studio Object browser to see what was generated.

Figure – The object browser shows you the generated .NET classes. It also provides you the info you need to instantiate these classes from FoxPro, namely the full namespace and class name.

The proxy import generates a service class (highlighted) as well as all of the related message objects that the service uses – so there are instances of Album, Artist, Track, and the query filter class in addition to the actual main service class. Remember the Object Browser in Visual Studio (or Reflector which I’ll get to shortly) – you can use it to discover the exact signatures required to create instances of these .NET classes.

As with the Demo project I recommend you compile the final output directly into a folder we can access from our FoxPro samples, which is the bin folder of our project. You can do this from the Project settings dialog (right click on the project | Properties):

In the dialog change the output folder to the root project’s bin folder where we already store all of our .NET dependencies for the demos.

Generate a Proxy without Visual Studio You can also remove Visual Studio from this equation if all you want to do is generate the proxy and create the .NET DLL. Frankly there’s not much value added in Visual Studio unless you plan on extending the functionality of the service or add it to other features you want to call (which is not uncommon though).

To use the command line, open a Windows Command prompt and go to the \WebServices\Tools folder in the samples for this article. You’ll find a CreateDll.bat file in this folder. You can run it with:

CreateDll.bat http://localhost/AlbumViewerServices/AlbumService.asmx Proxy AlbumService

You pass 3 parameters:

 The URL to the WSDL document  The namespace of the generated class  The name of the generated DLL (no extension)

Behind the scenes the batch file uses WSDL.exe (WSDL Code Generator) and CSC.exe (C# compiler) to produce a DLL of the proxy. This is essentially what Visual Studio does too.

This creates an AlbumService.dll that contains the generated proxy and you get a similar proxy to the one created in Visual Studio. Even better this proxy is generated without all the async methods as it uses a configuration file to only export what’s needed. Once you’re done copy the generated .dll file into your FoxPro project directory.

Calling the Proxy from FoxPro Once you’ve generated the AlbumService.dll into a folder you can access it from, we can use wwDotnetBridge to access the proxy.

Let’s start with the GetAlbum() method to load a single album by its name. I know there’s an Ace of Spades from Motorhead that we can search for and retrieve the single album: do wwDotNetBridge LOCAL loBridge as wwDotNetBridge loBridge = CreateObject("wwDotNetBridge","V4")

IF !loBridge.LoadAssembly("AlbumServiceProxy.dll") ? "Invalid library..." + loBridge.cErrorMsg return ENDIF

LOCAL loService as AlbumServiceProxy.Proxy.AlbumService loService = loBridge.CreateInstance("AlbumServiceProxy.Proxy.AlbumService")

*** Always check for errors! IF ISNULL(loService) ? "Unable to create service: " + loService.cErrorMsg RETURN ENDIF loAlbum = loService.GetAlbum("Ace of Spades")

*** Always check for errors IF ISNULL(loAlbum) ? "Couldn't get item: " + loService.cErrorMsg RETURN ENDIF

? loAlbum.Title ? "by " + loAlbum.Artist.ArtistName ? loAlbum.Descriptio ? loAlbum.Year

We load the AlbumServiceProxy.dll .NET assembly and then create an instance of the generated AlbumService type. How do you know the name of the type? The name will be the the rootnamespace of the project plus the proxy name you specified plus the name of the top level service class: AlbumServiceProxy.Proxy.AlbumService.

Instead of deducing or guessing the name you can also use the Visual Studio Object Browser or a Decompiler like Red Gate’s Reflector or Telerik’s JustDecompile to look at the signatures. In the \WebServices\Tools folder is an old, free copy of Reflector you can use to examine and browse the generated service definitions:

You use: loService = loBridge.CreateInstance("AlbumServiceProxy.Proxy.AlbumService") to create an instance, and after that you can go to town on the methods and properties. As is always the case, simple methods – like the GetAlbum() method we call first – can just be called directly. Any methods or properties that contain types COM doesn’t directly support (Value Types, Generic Types, Long, Guid, DbNull etc.) you have to use the indirect InvokeMethod(), GetProperty() or SetProperty() methods for. More on this in a bit.

For now you can just call the GetAlbum() method directly. loAlbum = loService.GetAlbum("Ace of Spades")

This retrieves a simple object instance and you can simply access the properties on it.

Changing the Service Url One important thing that you might have to do when you call a service is test the service against a test server and then switch the service to live production URL which requires changing the service endpoint URL. The WSDL document that you generate the service against has a service URL embedded in it and that’s the URL that is used by default.

If you need to switch to another URL you can specify it explicitly by using the URL property of the service proxy. Unfortunately you can’t do: loService.Url = "http://localhost/production/albumservice.asmx"

Rather you have to use wwDotnetBridge to access the URL property on the service instance: loBridge.SetProperty(loService,"Url","http://localhost/production/albumservice.asmx")

Ugly, but required because the Url property on the service class is inherited and COM Interop doesn’t pick up inherited members from abstract base classes. It’s another one of those COM Interop quirks that wwDotnetBridge allows you work around.

Arrays and Collections You may notice that I artfully neglected to access the Tracks property. That’s because the Tracks property is an array property and in order to effectively deal with arrays a little more work is required. While you can access array elements directly it’s better if you use some of the wwDotnetBridge helpers to provide an array wrapper. The wrapper makes it easy to iterate over the array, update elements and then also send them back up to the server.

Here’s the code to list the tracks using wwDotnetBridge and indirect referencing:

*** Get tracks Array as a COM Collection loTracks = loBridge.GetProperty(loAlbum,"Tracks")

IF loTracks.Count > 0 FOR lnX = 0 TO loTracks.Count - 1 loTrack = loTracks.Item(lnX) ? " " + loTrack.SongName + " " + loTrack.Length ENDFO ENDIF

Notice the call to GetProperty() to convert the .NET array into something that FoxPro can deal with more easily. This returns a ComArray object that wraps a .NET array and leaves that array in .NET – it is never marshalled into FoxPro directly. You can retrieve items in the array, update elements and add new ones, but the array instance never passes directly into FoxPro. This allows two-way editing and updates safely.

Once you have this ComArray instance you can ask for its Count property, then loop through each of its .Item() methods retrieve the individual items. wwDotnetBridge auto converts a number of types automatically when using GetProperty(), SetProperty() and InvokeMethod() to indirectly access .NET objects. If you have problems working with properties or methods directly try using these indirect methods instead.

Returning Collections From the Service So far so good. Let’s try calling the other two methods. For the most part the code for these methods will be similar. GetArtists() returns an array of artists and because we want to use the Array in FoxPro we can use InvokeMethod() to return a ComArray instance of the array for easy manipulation in FoxPro. loArtists = loBridge.InvokeMethod(loService,"GetArtists","") && all

FOR lnX = 0 to loArtists.Count - 1 loArtist = loArtists.Item(lnx) ? " " + loArtist.ArtistName ENDFOR

We can then loop through use the .Item() method to retrieve the individual artists and display them.

To get Albums works very similarily, but we can do some nested enumeration of the tracks: loAlbums = loBridge.InvokeMethod(loService,"GetAlbums")

*** Always check for errors IF ISNULL(loAlbums) ? "Couldn't get item: " + loService.cErrorMsg RETURN ENDIF

FOR lnX = 0 TO loAlbums.Count - 1

loAlbum = loAlbums.Item(lnx) ? loAlbum.Title ? "by " + loAlbum.Artist.ArtistName ? loAlbum.Descriptio ? loAlbum.Year

*** Get tracks Array as a COM Collection loTracks = loBridge.GetProperty(loAlbum,"Tracks")

IF loTracks.Count > 0 FOR lnY = 0 TO loTracks.Count - 1 loTrack = loTracks.Item(lnY) ? " " + loTrack.SongName + " " + loTrack.Length ENDFO ENDIF ENDFOR

At this point you can see it’s pretty straight forward to retrieve and display data from a service. Passing Data to a Service Next up we need to pass some data to the service. Let’s look at the GetAlbumsQuery() method which receives the filter class as a parameter. In order to pass an object to .NET we have to ensure that the object passed is of the proper type – it has to be the EXACT type that is specified in the parameter signature, which means we have to create a .NET object and pass it to .NET as a parameter.

The first thing required is the exact .NET typename we need to pass and to do that we can use the Object Browser in Visual Studio or a tool like .NET Reflector (in the Tools directory). I’m going to look up the AlbumFilterParms class and look at the type signature in the Object Browser in VS:

Figure 16 – Use the Object Browser (or Reflector) to find out the example name of a .NET type to instantiate. A full type name is namespace.classname.

If you look at the details of the class you see the name of the class and the namespace (Member of which is a little misleading). The full type name is AlbumServiceProxy.Proxy.AlbumFilterParms which is the namespace plus the classname combined by a dot.

The object browser is also very useful in showing what properties and methods are available on objects and what their exact type signatures are. It’s important to know what you need to pass to each method for example.

With that we can now create an instance of this type, set its properties and pass it to the GetAlbumsQuery() method. loFilter = loBridge.CreateInstance( "AlbumServiceProxy.Proxy.AlbumFilterParms") loFilter.AlbumName = "Ace of Spades" loAlbums = loBridge.InvokeMethod(loService,"GetAlbumsQuery",loFilter)

*** Always check for errors IF ISNULL(loAlbums) ? "Couldn't get item: " + loService.cErrorMsg RETURN ENDIF

FOR lnX = 0 TO loAlbums.Count - 1

loAlbum = loAlbums.Item(lnx) ? loAlbum.Title ? "by " + loAlbum.Artist.ArtistName ? loAlbum.Descriptio ? loAlbum.Year

*** Get tracks Array as a COM Collection loTracks = loBridge.GetProperty(loAlbum,"Tracks")

IF loTracks.Count > 0 FOR lnY = 0 TO loTracks.Count - 1 loTrack = loTracks.Item(lnY) ? " " + loTrack.SongName + " " + loTrack.Length ENDFO ENDIF ENDFOR

The key in this code is the loBridge.CreateInstance() call that effectively creates a .NET object and makes it available to your FoxPro code. You set properties and then pass that object to .NET which recognizes it as the proper type for the filter object. After that the code is the same as before.

So now we’ve come full circle – we’ve create services from FoxPro data and returned those results as SOAP results from the service. We can capture those results in FoxPro using .NET as a bridge to make the service calls and we can push data to .NET from FoxPro by using .NET objects created in FoxPro.

You can use the same approach to send an object to the server to update a record. For example to send and update an Artist you can do: loArtist = loService.GetArtist(2) ? loArtist.ArtistName

*** Update a value loArtist.ArtistName = "Accept " + TRANSFORM(SECONDS()) lnId = loService.SaveArtist(loArtist) IF lnID < 1 ? "Failed to update..." RETURN ENDIF

*!* *** Create a new Artist loArtist = loBridge.CreateInstance("AlbumServiceProxy.Proxy.Artist") loArtist.ArtistName = "New Artist " + TRANSFORM(SECONDS()) loArtist.Descriptio = "Description goes here" lnId = loService.SaveArtist(loArtist)

? "New Id: " + TRANSFORM(lnId)

This code first retrieves an existing artist, then changes a single property on the artist instance. It then pushes the updated record back to the server. The server then takes the instance and writes the updated data to the . To create a new record uses the same method – you simply create a new instance of the .NET Artist object using loBridge.CreateInstance(), populate it and then send it off. The server code then inserts the new artist. The server checks whether the artist exists already based on the Id passed (or 0) and if not found creates a new one and returns the generated id.

Web Service Interaction Summary As you can see, creating a .NET proxy from a Web service and then calling it from FoxPro is pretty straight forward. There are more things you can do to make service interfaces cleaner by wrapping the service into a FoxPro class wrapper which can handle errors for you. Our West Wind Web Service Proxy Generator tools can automate that entire process for from generating the .NET Proxy, to creating a FoxPro Proxy that calls the .NET proxy. Check it out. If you want more detailed info, on how the server side works for this example also check out the FoxPro/.NET Web Service article.

Humanizer This example demonstrates: Creating a wrapper .NET library of selected features around a third party library and exposing that library to FoxPro. This library uses a host of extension methods which are difficult to access with wwDotnetBridge.

When I build Web applications today there are lots of instances where you need to format text in ‘humanized` ways. Dates as friendly expressions, numbers as words, items with quantities, pluralized words, bytes expressed as words and so on. In .NET there’s a powerful library called Humanizer that can do all sorts of conversion for you.

In the previous example we looked calling a third party assembly – I’m doing it again with this example, but this time I’m going to have to do a little more work to utilize this library. Humanizer is very easy to use in .NET because it uses extension methods. Extension methods attach to existing objects and extend the attached object with the extension method.

Unfortunately, extension methods are not directly visible to FoxPro and COM interop as extension methods. They are actually static methods that take the target object as the first parameter. They are still callable using their native static method signatures.

For example, to use Humanizer in CSharp code you can do: date.Humanize(); to turn the DateTime value into something like yesterday.

But if you look at the actual .NET signature of the Humanize function you’ll find that it’s actually an extension method that looks like this: public static string Humanize(this DateTime input, bool utcDate = true, DateTime? dateToCompareAgainst = new DateTime?(), CultureInfo culture = null)

Which is the signature of an extension method (denoted by the this part in the first parameter). It’s still possible to use wwDotnetBridge to access this version of the method, but it’s a lot of work as you have to pass all the optional parameters as well as the base object and call the static method.

As a result, it’s a lot easier to simply create a wrapper class and provide some of the abstractions you are actually interested in. So rather than calling the raw static methods, we can create a custom .NET library and a class that provides a few methods that are easily callable from FoxPro.

Here’s the FoxHumanizer class: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Humanizer; namespace wwDotnetBridgeDemos {

///

/// Helper to create humanized words of numbers dates and other occasions /// /// Wrapper around the Humanizer library: /// https://github.com/Humanizr/Humanizer /// public class FoxHumanizer {

///

/// Humanizes a date as yesterday, two days ago, a year ago, next month etc. /// /// /// public string HumanizeDate(DateTime date) { return date.Humanize(); }

///

/// Turns integer numbers to words /// /// /// public string NumberToWords(int number) { return number.ToWords(); }

///

/// Returns a number like 1st, 2nd, 3rd /// /// /// public string NumberToOrdinal(int number) { return number.Ordinalize(); }

public string NumberToOrdinalWords(int number) { return number.ToOrdinalWords(); }

///

/// creates expression like one car or two bananas /// from a qty and a string that is pluralized as needed /// /// /// /// public string ToQuantity(string single, int qty) { return single.ToQuantity(qty, ShowQuantityAs.Words); }

public string ToCamelCase(string input) { return input.Camelize(); }

///

/// Truncates a string and adds elipses after length is exceeded /// /// /// /// public string TruncateString(string input, int length) { return input.Truncate(length); }

///

/// Takes a singular noun and pluralizes it /// /// /// public string Pluralize(string single) { return single.Pluralize(true); }

///

/// Takes a pluralized noun and turns it to singular /// /// /// public string Singularize(string pluralized) { return pluralized.Singularize(true); }

///

/// Returns a byte count as kilobytes /// /// /// public string ToByteSize(int byteSize) { return byteSize.Bytes().Humanize("#.##"); } } }

This class picks a few common humanizations and exposes them as a simple class. Because we’ve taken these extension methods and wrapped them into a simple object, it’s now super easy to consume this object in FoxPro code: do wwDotNetBridge loBridge = CreateObject("wwDotNetBridge") ? loBridge.LoadAssembly("bin\wwDotnetBridgeDemos.dll")

LOCAL loHuman as wwDotnetBridge.FoxHumanizer loHuman = loBridge.CreateInstance("wwDotnetBridgeDemos.FoxHumanizer")

? loHuman.HumanizeDate(DATE()-1) ? loHuman.NumberToWords(10) ? loHuman.ToQuantity("Car",3) ? loHuman.Pluralize("Building") ? loHUman.Pluralize("Mouse")

? loHuman.ToByteSize(13122) ? loHuman.ToByteSize(1221221)

Which produces:

Inter-application Messaging using Named Pipes Have you ever needed to communicate between two applications to send information, or to notify one application that something happened? Named pipes is a low level Windows feature that makes this possible in a very efficient way. .NET includes a very easy to use Named Pipe implementation that you can take advantage of from your FoxPro applications.

Names pipes are useful whenever you need to cross application communication. This can be for things as simple as notifying multiple instances of your own applications or multiple separate applications when some operation has occurred. A common use case where I’ve used Named Pipe communication is to build Singleton applications – applications that only run a single instance and simply activate an existing instance if another instance is already running. Other scenarios is a client server environment where a central server receives messages from multiple clients and takes action based on these operations. Named pipes can work on local machines or across multiple Windows servers.

Creating a Generic NamedPipeServer Implementation Let’s take a look at a generic NamedPipeServer implementation that allows you to create a Named Pipe Server that can run as a server to receive messages and also act as the client to send messages to the server. The server implementation should be able to run in the background, so it can receive messages and take action when a message arrives, while the main application can run as it normally would. When messages arrive an ‘event’ is fired to notify you that some action needs to be taken.

To make this happen we’ll create a class in .NET that represents both the pipe server and client with simple methods like StartServer()/StopServer() for the server part, and Send() for the client. Because we want to have the server run in the background, the server is created on a new thread that effectively runs in an endless loop, which is executed with a custom string directive. This way we have an easy way to shut down the inifinite loop, which exits the thread and effectively ends the server.

Here’s the .NET implementation of the NamedPipeManager class: using System; using System.IO; using System.IO.Pipes; using System.Threading; namespace wwDotnetBridgeDemos { ///

/// A very simple Named Pipe Server implementation that makes it /// easy to pass string messages between two applications. /// public class NamedPipeManager { public string NamedPipeName = Guid.NewGuid().ToString(); public event Action ReceiveString;

private const string EXIT_STRING = "__EXIT__"; private bool _isRunning = false; private Thread Thread;

public NamedPipeManager(string name) { NamedPipeName = name; }

///

/// Starts a new Pipe server on a new thread /// public void StartServer(dynamic messageCallbackHandler) { Thread = new Thread((pipeName) => { _isRunning = true;

while (true) {

try { string text; using (var server = new NamedPipeServerStream(pipeName as string)) { server.WaitForConnection();

using (StreamReader reader = new StreamReader(server)) { text = reader.ReadToEnd(); } }

if (text == EXIT_STRING) break;

if (messageCallbackHandler != null && !string.IsNullOrEmpty(text)) messageCallbackHandler.OnReceiveMessage(text);

if (_isRunning == false) break; } catch { // *Important* to capture exceptions on separate thread // or else crashes.

// pipe is already running so just exit this tread break; } } }); Thread.Start(NamedPipeName); }

///

/// Shuts down the pipe server /// public void StopServer() { _isRunning = false; Write(EXIT_STRING); Thread.Sleep(30); }

///

/// Write a client message to the pipe /// /// /// public bool Write(string text, int connectTimeout = 300) { using (var client = new NamedPipeClientStream(NamedPipeName)) { try { client.Connect(connectTimeout); } catch { return false; }

if (!client.IsConnected) return false;

using (StreamWriter writer = new StreamWriter(client)) { writer.Write(text); writer.Flush(); } }

return true; }

} }

In .NET Named Pipes are implemented as a NamedPipeStream which is opened and left open for the duration of the lifetime of the named pipe. Clients then write into this stream, which the server can detect and so read the content of the message.

In the server code above we create a new thread that runs the server in an infinite loop in the background and opens a stream and waits for someone to access that stream. When a message is sent to the stream the pipe server instance is signaled and can then read the stream to retrieve the message sent from the client. The message is first checked for a specific EXIT message, which if sent kills the loop and exits the thread and server. Otherwise the message is forwarded to an object that was passed to the server.

This object in this case will be a FoxPro object that has an OnReceiveMessage() method, which is fired when a message is received. This is the ‘event’ that is effectively fired in FoxPro and lets you intercept the message operation. Note that this message is fired asynchronously and can fire at any point in FoxPro, so whatever code you fire from this method should be very quick and not affect the FoxPro environment.

The StopServer() method then sets a flag that’s checked inside of the thread loop which in turn exits and releases the server.

The Write() method is used to send messages to the specified pipe and so acts as the client to the server. When you write to the pipe a Client Pipe stream is opened. If the pipe exists, the message is sent to it otherwise.

Bringing up the .NET Named Pipe Server from FoxPro To use this code from FoxPro we need to set up code for the server and the client. To demonstrate I’ll use a couple of small program files – one for the server, one for the client. Here’s the server code:

lcPipename = "MyPipe" loBridge = GetwwDotnetBridge() loCallbackHandler = CreateObject("NamedPipeCallbackHandler")

loBridge.LoadAssembly("bin\wwDotnetBridgeDemos.dll") ? loBridge.cErrorMsg loServer = loBridge.CreateInstance("wwDotnetBridgeDemos.NamedPipeManager",lcPipename)

? loBridge.cErrorMsg ? loServer

TRY loServer.StartServer(loCallbackHandler) WAIT WINDOW "Press any key to exit server" FINALLY *** Make sure you shut down otherwise you can't restart! loServer.StopServer() ENDTRY

RETURN

************************************************************* DEFINE CLASS NamedPipeCallbackHandler AS Custom ************************************************************* *: Author: Rick Strahl *: (c) West Wind Technologies, 2016 *:Contact: http://www.west-wind.com *:Created: 07/12/2016 ******************************

************************************************************************ * OnReceiveMessage **************************************** FUNCTION OnReceiveMessage(lcText)

WAIT WINDOW lcText nowait ACTIVATE SCREEN ? "Received: " + TRANSFORM(lcText)

ENDFUNC * OnReceiveMessage

ENDDEFINE *EOC NamedPipeCallbackHandler

This code starts by creating an instance of a callback object. If you recall from the .NET code you need to pass the server an object that has a OnReceiveMessage() method that can be used to notify the FoxPro code that a message has been sent. This method then can perform some action which in this case is simply write out a message to the FoxPro console window.

Next the code instantiates the .NET pipe server instance and calls the StartServer() method, passing the callbackhandler as a parameter. The important thing about the pipe server is that the object instance has to stay alive in order to keep running in the background. The code here uses a WAIT WINDOW, but in a regular application you’d store this object either as PUBLIC variable, or attach it to some top level object, like your application object. The call to StartServer() actually starts the server on the background thread.

Calling the .NET Pipe Server from FoxPro In order to call the server we need to run another FoxPro instance, since we’re testing cross application communication here after all.

Building a Singleton Application Implementation I’ll use the Singleton application implementation as an example here, because it’s something that can be quite useful. If you’ve ever built an application that needs to have just a single instance open.

The code to bring up the pipe client is simpler as you’re simply calling a method on the custom .NET object:

LPARAMETER lcMessage lcPipename = "MyPipe" loBridge = GetwwDotnetBridge() loBridge.LoadAssembly("bin\wwdotnetbridgedemos.dll") loServer = loBridge.CreateInstance("wwDotnetBridgeDemos.NamedPipeManager",lcPipename)

IF EMPTY(lcMessage) loServer.Write("Hello from Pipe Client " + TIME()) ELSE ? loServer.Write( lcMessage ) ENDIF

This code is pretty straight forward: You create an instance of the Pipe manager and all the Write() message to write a message into the pipe. When you run this client code you should immediately see the message displayed on the server application.

Voila – instant inter-process communication. Now mind you, you can pass much more than simple messages. You can pass commands or directions, command line parameters, or more complex serialized data in the form of JSON or XML to do something useful.

A practical example: Singleton FoxPro Applications A use case where I’ve used a named pipe implementation is to implement a Singleton application. Singletons are applications that only open a single instance and automatically defer to an existing running instance if you open another instance. The first instance acts as the master and all subsequently opened instances immediately bring up the existing running instance. In addition the secondary instances can send information – such as command line parameters – to the first instance. This is a common scenario for document centric applications that can open multiple documents – rather than opening multiple documents in new instances, they simply open a new tab or MDI window to show the information. Using the named pipe manager the relevant command line parameters can be forwarded to open these files in the primary instance instead.

To do this I created a custom SingletonApplication class which internally manages a Windows Mutex which can be used to detect whether an instance is already running, in combination with the named pipe manager to notify the primary instance to activate itself and – optionally receive parameter information.

The following FoxPro class demonstrates this implementation:

************************************************************* DEFINE CLASS SingletonApplication AS Custom ************************************************************* cInstanceName = "SingletonApplication" oBridge = null oMutex = null oPipeServer = null lIsServer = .F. oReceiveHandler = null cReceiveHandlerMethod = ""

************************************************************************ * Init **************************************** FUNCTION Init(lcInstanceName)

IF !EMPTY(lcInstanceName) THIS.cInstanceName = lcInstanceName ENDIF

THIS.oBridge = GetwwDotnetBridge() THIS.oBridge.LoadAssembly("bin\wwDotnetBridgeDemos.dll")

THIS.oPipeServer = THIS.oBridge.CreateInstance("wwDotnetBridgeDemos.NamedPipeManager",; this.cInstanceName)

ENDFUNC

************************************************************************ * SendMessage **************************************** FUNCTION SendMessage(lcMessage)

IF EMPTY(lcMessage) RETURN ENDIF

THIS.oPipeServer.Write( lcMessage )

ENDFUNC

************************************************************************ * ListenForNewInstance **************************************** FUNCTION ListenForNewInstance()

THIS.lIsServer = .T. THIS.oPipeServer.StartServer(THIS)

ENDFUNC

************************************************************************ * OnReceiveMessage **************************************** *** Function: Override class and implement this method to handle the *** message sent from secondary applications. *** Typically pass in command arguments to open documents *** or otherwise affect the primary instance ************************************************************************ FUNCTION OnReceiveMessage(lcMessage) ENDFUNC * OnReceiveMessage

************************************************************************ * IsOnlyInstance **************************************** *** Function: Checks to see if an instance of this app is already *** running. *** Assume: Uses a Win32 Mutex instance ************************************************************************ FUNCTION IsOnlyInstance() LOCAL llIsOnly llIsOnly = .F. THIS.oMutex = THIS.oBridge.CreateInstance("System.Threading.Mutex",.T.,THIS.cInstanceName) llIsOnly = THIS.oMutex.WaitOne(INT(10),.F.)

RETURN llIsOnly ENDFUNC * IsOnlyInstance

************************************************************************ * Destroy **************************************** FUNCTION Destroy()

IF VARTYPE(this.oMutex) = "O" THIS.oMutex.Dispose() THIS.oMutex = null ENDIF

IF VARTYPE(this.oPipeServer) = "O" *** Make sure you shut down otherwise you can't restart! IF (THIS.lIsServer) THIS.oPipeServer.StopServer() ENDIF THIS.oPipeServer = null ENDIF

ENDFUNC * Destroy

ENDDEFINE

To use this class in an application you can use code similar to the following in startup code. As before keep in mind that the class instance needs to be kept alive either as a PUBLIC var or on a global application level object that sticks around. In this example PUBLIC is used.

The code consists of a custom class that defines the name of the pipe and the implementation of the primirary instance’s behavior when a message is received:

PUBLIC loApp loApp = CREATEOBJECT("MySingletonApplication") IF !loApp.IsOnlyInstance() ? "App is already running..."

*** Send notification to primary app *** Typically you'd send command line parameters here loApp.SendMessage("Deferred to primary instance: " + TIME())

*** Quit this instance QUIT

RETURN ENDIF

? "First instance of " + loApp.cInstanceName loApp.ListenForNewInstance()

RETURN

************************************************************* DEFINE CLASS MySingletonApplication as SingletonApplication ************************************************************* * Create a subclass of SingletonApplication to customize * behavior. Specify the pipe name and what happens when a * secondary app (client) sends a message to the * primary application (server). * * Create a subclass and implement the OnReceiveMessage * executed when secondary instances are started. * Typically pass command line arguments to open new windows * or other values that affect the primary instance. *************************************************************

*** Specify the unique name for this pipe/application cInstanceName = "WestWindHtmlHelpBuilder"

************************************************************************ * OnReceiveMessage **************************************** *** Function: Method called when secondary app is fired ************************************************************************ FUNCTION OnReceiveMessage(lcMessage)

*** Activate this instance _SCREEN.AlwaysOnTop = .T. DOEVENTS

? "First Instance: " + lcMessage

_SCREEN.AlwaysOnTop = .F.

ENDFUNC

ENDDEFINE

If you run this code the first time, the pipe server starts. The call to .IsOnlyInstance() creates a new mutex and blocks it, and then the pipe server is started waiting for incoming messages.

If you now open another instance of FoxPro and run that very same code .IsOnlyInstance() will now fail because the Mutex is blocked. The code falls through and hits the .SendMessage() code to let the primary instance listening know that another instance has been activated. The secondary instance then quits. The primary instance now receives the notification in the OnReceiveMessage() event handler and can take action. Here the code activates the current application and prints a message to the FoxPro console. Of course you can do other things in this method like for example, receive command line parameters that might be asking to open additional files in an MDI application.

Using Named Pipe servers can be a good way to send interapplication messages because they are very lightweight and very efficient – calls made by the client are immediately picked up. As you can see it’s relatively easy to build a custom abstraction on top of the Named Pipe Manager to create custom solutions.

Hosting a Web Server inside of Visual FoxPro Have you ever wanted to create a FoxPro application that can serve simple HTTP internally without requiring a full Web Server like IIS? Maybe you have an application that occasionally needs to receive messages from other clients to synchronize data, receive messages when a task has been completed, or an application that simply receives and archives information sent by other apps across your network? Or perhaps you simply want to run a local HTML based application in your own Web Browser control, or preview some HTML content that requires AJAX interaction.

You could of course run a full blown Web Server like IIS and ASP.NET or a tool like Web Connection for this, but for simple scenarios running a full Web Server and maintaining it can be overkill.

Windows includes a built in Web host that is based on the http.sys kernel driver and an httplistener implementation. .NET exposes its own implementation of HttpListener and you can interact with this class using FoxPro. Essentially HttpListener is a multi-threaded HTTP client that listens for incoming HTTP requests on a specific port. You can pass in a FoxPro object to a custom listener implementation that is called back whenever a request arrives, at which point you can examine the request and decide whether you want to handle it, or – if you prefer let it do its default processing. In the following implementation I provide a Static Content Web server HttpListener implementation that pre-processes every request by passing it this FoxPro handler object.

Disclaimers Although it’s pretty cool to host your own Web Server in FoxPro there are a number of things you need to consider.

Not a replacement for a full blown Web Server Using HttpListener is meant for simple custom HTTP applications – they are not meant to replace a full blown Web server. HttpListener is a low level HTTP protocol handler and it doesn’t have much in the way of features. There’s no authentication, no caching, no routing and it doesn’t know about any Web Frameworks. Essentially you have to handle all interaction manually which works great for simple functionality.

Network Scope Keep in mind that in order to globally access your application it has to be reachable. The typical desktop application is not accessible through the internet directly unless you use port forwarding. So in most cases this solution is mainly suitable for internal networks or VFP unless your server is directly connected to the Internet.

Security In order for you to even use http.sys for a non-admin user, you have to explicitly assign the ports even to access specific ports locally. Additionally if you want to externally access those ports a firewall rule has to be set. This means there’s some configuration to make this work. There are two relevant commands you can issue from the command prompt (for port 16544 in this example):

Allow Interactive users to access port 16455 via http.sys: netsh http add urlacl url=http://*:16455/ user=Interactive listen=yes

Open port 16455 in the firewall: netsh advfirewall firewall add rule name="Open Port 16455" dir=in action=allow protocol=TCP localport=16455

If you’re building an application that relies on the HTTP features these commands should be handled as part of an installation process.

Implementing a HTTP Server using HttpListener HttpListener is a low level interface and it is essentially a hosting interface. While it might be possible to implement everything from FoxPro using wwDotnetBridge, that’s really not a good approach. Instead it’s best to create a custom HttpListener implementation in .NET code and then call that code from FoxPro.

The custom implementation below shows a base implementation that provides for:

 Server Hosting  Background Processing (so app can run while server processing in background)  Simple static Start/Stop methods easily callable from FoxPro  Static File Serving for any requests not handled by FoxPro  Simple request interface that allows FoxPro to process requests

Here’s the .NET implementation of this HttpListener implementation: public class SimpleHttpServer { public string[] DefaultDocuments = { "index.html", "index.htm", "default.html", "default.htm" };

public static bool StartHttpServerOnThread(string path, int port = 8080, object requestHandler = null) { // make sure we're not already running if (Current != null) Current.Stop();

try { // see if we can access the port TcpListener listener = new TcpListener(IPAddress.Loopback, port); listener.Start(); listener.Stop(); } catch { return false; }

var t = new Thread(StartHttpServerThread);

// STA Thread so VFP COM objects can work t.SetApartmentState(ApartmentState.STA);

t.Start(new ServerStartParameters { Path = path, Port = port, RequestHandler = requestHandler });

return true; }

///

/// Call this method to stop the Singleton instance of the server. /// public static void StopHttpServerOnThread() { Current.Stop(); Current = null; }

///

/// Internal method that instantiates the server instance /// /// private static void StartHttpServerThread(object parms) { try { if (Current != null) StopHttpServerOnThread();

var httpParms = parms as ServerStartParameters; Current = new SimpleHttpServer(httpParms.Path, httpParms.Port); Current.RequestHandler = httpParms.RequestHandler; } catch (Exception ex) { MessageBox.Show(ex.Message, "Html Help Builder: Error loading HTTP Service", MessageBoxButtons.OK, MessageBoxIcon.Error); } }

///

/// Mime Type conversion table /// private static IDictionary _mimeTypeMappings = new Dictionary(StringComparer.InvariantCultureIgnoreCase) { #region extension to MIME type list {".htm", "text/html"}, {".html", "text/html"}, {".ico", "image/x-icon"}, {".jpeg", "image/jpeg"}, {".jpg", "image/jpeg"}, {".js", "application/x-"}, … #endregion }; private Thread _serverThread; private string _rootDirectory; private HttpListener _listener; private int _port; public int Port { get { return _port; } }

///

/// Instance of an object whose Process() method is called on each request. /// Return true if the request is handled, fase if it's not. FoxPro object /// public object RequestHandler { get; set; }

///

/// Construct server with given port. /// /// Directory path to serve. /// Port of the server. public SimpleHttpServer(string path, int port = 8080) { Initialize(path, port); }

///

/// Construct server with an available port. /// /// Directory path to serve. public SimpleHttpServer(string path) { // Find an open port and bind to it TcpListener listener = new TcpListener(IPAddress.Loopback, 0); listener.Start(); int port = ((IPEndPoint)listener.LocalEndpoint).Port; listener.Stop();

Initialize(path, port); } private void Initialize(string path, int port) { _rootDirectory = path; _port = port; _serverThread = new Thread(Listen); _serverThread.SetApartmentState(ApartmentState.STA); _serverThread.Start(); }

///

/// Stop server and dispose all functions. /// public void Stop() { _serverThread.Abort(); _listener.Stop(); }

///

/// Internal Listener Handler that starts the listener and /// then waits for incoming requests (GetContext) /// private void Listen() { try { _listener = new HttpListener(); _listener.Prefixes.Add("http://*:" + _port + "/"); _listener.Start(); } catch (Exception ex) { MessageBox.Show("The HttpListener could not start. Most likely port " + _port + " has not been configured to allow access by non-admin users.\r\n\r\n" + "Use:\r\n\r\n" + $"netsh http add urlacl url=http://*:{_port}/ user=Interactive listen=yes\r\n\r\n" + "Error message:\r\n" + ex.Message, "HttpListener could not start",MessageBoxButtons.OK,MessageBoxIcon.Error);

Clipboard.SetText($"netsh http add urlacl url=http://*:{_port}/ user=Interactive listen=yes"); return; }

while (true) { try { HttpListenerContext context = _listener.GetContext(); Process(context); } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); } } }

///

/// Process an individual request. Handles only static file based requests /// /// private void Process(HttpListenerContext context) { string filename = context.Request.Url.AbsolutePath; Console.WriteLine(filename);

if (RequestHandler != null) { var response = new HttpResponse(context);

// process passed in Event handler and it's Process() method // if it returns true exist - otherwise let internal static files be served dynamic handler = RequestHandler;

bool result = false; try { result = (bool) handler.Process(context, response); } catch (Exception ex) { // foxpro exception so we don't crash response.WriteOutput("An error occurred during processing: " + ex.Message, "text/plain"); return; } if (result) return; // we handled it in FoxPro }

filename = filename.Substring(1);

if (string.IsNullOrEmpty(filename)) { foreach (string indexFile in DefaultDocuments) { if (File.Exists(Path.Combine(_rootDirectory, indexFile))) { filename = indexFile; break; } } }

filename = Path.Combine(_rootDirectory, filename);

if (File.Exists(filename)) { try { Stream input = new FileStream(filename, FileMode.Open);

//Adding permanent http response headers string mime; context.Response.ContentType = _mimeTypeMappings.TryGetValue(Path.GetExtension(filename), out mime) ? mime : "application/octet-stream"; context.Response.ContentLength64 = input.Length; context.Response.AddHeader("Date", DateTime.Now.ToString("r")); context.Response.AddHeader("Last-Modified", File.GetLastWriteTime(filename).ToString("r"));

byte[] buffer = new byte[1024 * 32]; int nbytes; while ((nbytes = input.Read(buffer, 0, buffer.Length)) > 0) context.Response.OutputStream.Write(buffer, 0, nbytes); input.Close(); context.Response.OutputStream.Flush();

context.Response.StatusCode = (int)HttpStatusCode.OK; } catch (Exception ex) { context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; }

} else { context.Response.StatusCode = (int)HttpStatusCode.NotFound; }

context.Response.OutputStream.Close(); } }

///

/// Parameters that are passed to the thread method /// public class ServerStartParameters { public string Path { get; set; } public int Port { get; set; }

///

/// Any object that implements a Process method /// method should return true (request is handled) /// or false (to fall through and handle as files) /// public object RequestHandler { get; set; } }

///

/// Response helper class that simplifies sending output /// to the server. /// public class HttpResponse { private HttpListenerContext Context;

public HttpResponse(HttpListenerContext context) { Context = context; }

public void WriteOutput(string output,string contentType) { if (string.IsNullOrEmpty(contentType)) contentType = "text/html";

Context.Response.ContentType = contentType;

var buf = Encoding.UTF8.GetBytes(output); Context.Response.ContentLength64 = buf.Length; Context.Response.OutputStream.Write(buf, 0, buf.Length); }

public void Write(string output) { var buf = Encoding.UTF8.GetBytes(output); Context.Response.OutputStream.Write(buf, 0, buf.Length); } }

That’s quite a bit of code but most of it is just boilerplate to get the server to run. For our FoxPro perspective there are two key components:

 StartHttpServerOnThread/StopHttpServer The methods that are used to bootstrap and start/stop the Http server from FoxPro. The static method creates a cached instance of the server on a new thread, so that the Web Server can run in the background while FoxPro keeps running

 RequestHandler Instance This is the instance of a FoxPro object that is passed that contains a Process(loContext, loResponse) method that is fired whenever a request comes in.

The remainder of the code mostly deals with setting up the server to launch on a specific port and checking whether the desired port is available.

Starting the server from FoxPro While the .NET code is lengthy, using it from FoxPro is not. Let’s start with a very simple way to set up the server and run it:

SET PROCEDURE TO wwUtils ADDITIVE do wwDotNetBridge lnPort = 8080 loBridge = CreateObject("wwDotNetBridge","V4") loBridge.LoadAssembly("wwDotnetBridgeDemos.dll") loHandler = CREATEOBJECT("WebRequestHandler") loBridge.InvokeStaticMethod("wwDotnetBridgeDemos.SimpleHttpServer",; "StartHttpServerOnThread",; FULLPATH("WebSite"),; INT(lnPort),loHandler)

GoUrl("http://localhost:" + TRANSFORM(lnPort) + "/index.htm")

WAIT WINDOW "press any key to exit..." loBridge.InvokeStaticMethod("wwDotnetBridgeDemos.SimpleHttpServer",; "StopHttpServerOnThread")

* FoxPro Handler Passed to .NET for Callbacks DEFINE CLASS WebRequestHandler as Custom

FUNCTION Process(loContext, loResponse) lcPath = LOWER(locontext.Request.Url.AbsolutePath) ? lcPath

IF lcPath = "/foxhello" loResponse.WriteOutput("Hello World from " + VERSION(),"text/plain") RETURN .T. ENDIF

RETURN .F. && Not handled ENDFUNC

ENDDEFINE

There are two parts to this code:

 The Start/Stop Code  The Handler Class Implementation

The Start/Stop code simply fires up the Web Server and gets to process requests. I point it at the \WebSite relative path in the samples, which includes a simple static Web site with HTML files. Once the server is started I can navigate to http://localhost:8080/ where 8080 is the port I used (remember you have to first enable that port and if you want remote access open the firewall).

In the Start method I pass in a WebRequestHandler instance. This class has a Process(loContext,loResponse) method implementation that is called whenever a request comes in through the server. In this example, we do two things here:

 Echo back the request path  Handle a special /foxhello request

As you can see in the Process() method implementation the method receives an HttpListenerContext object parameter, which in turn has subobjects for .Response .Request .Server etc that let you access the Http server functionality. Request holds individual request information like the Url for example, and Response contains properties for Http headers and an OutputStream object.

To simplify output generation I created a custom HttpResponse class in .NET that wraps writing to the output stream, and that object is passed as loResponse to the Process method. Using the WriteOutput() method makes it easy to write complete HTTP output in one go: loResponse.WriteOutput("Hello World from " + VERSION(),"text/plain")

So now if you open a browser and go to http://localhost:8080/index.htm you get the standard HTML content, but when you access http://localhost:8080/foxhello you get the FoxPro generated string message.

Making it cleaner The code above works and is easy enough, however by know you can see my pattern of creating wrapper classes for .NET code to make it easier to use without having to know all the details. So rather than manually instantiating the class I created a FoxPro SimpleHttpServer base class that wraps up the above code. Rather than passing a separate object you have to create, it simply includes a Process() and then passes itself to the StartHttpServerOnThread() method.

************************************************************* DEFINE CLASS SimpleHttpServer AS Custom ************************************************************* oBridge = null oHttpServer = null cErrorMsg = "" cIpAddress = "*" nPort = 16544

************************************************************************ * Init **************************************** FUNCTION Init(nPort) this.oBridge = GetwwDotnetBridge("V4")

IF !THIS.oBridge.LoadAssembly("wwDotnetBridgeDemos.dll") ERROR "Failed to load wwDotnetBridgeDemos: " + ; this.oBridge.cErrorMsg ENDIF

ENDFUNC

************************************************************************ * Custom :: Process **************************************** *** Function: Override this method to handle incoming requests *** Pass: loContext - HttpListenerContext Object *** loResponse - HttpResponse (custom) object (Write()) *** Return: void ************************************************************************ FUNCTION Process(loContext,loRespone) RETURN .F. && not handled ENDFUNC

************************************************************************ * Start **************************************** *** Function: Starts the server ************************************************************************ FUNCTION Start()

THIS.oBridge.InvokeStaticMethod("wwDotnetBridgeDemos.SimpleHttpServer",; "StartHttpServerOnThread",; FULLPATH("WebSite"),; INT(THIS.nPort),THIS)

ENDFUNC

************************************************************************ * Stop **************************************** FUNCTION Stop()

THIS.oBridge.InvokeStaticMethod("wwDotnetBridgeDemos.SimpleHttpServer",; "StopHttpServerOnThread")

ENDFUNC

ENDDEFINE *EOC HttpServer

This class takes care of the Start and Stop operations and simply passes this very instance to the .NET start method. To implement this class you can now simply subclass it and implement the Process() method:

************************************************************* DEFINE CLASS MyHttpServer AS SimpleHttpServer *************************************************************

************************************************************************ * Process **************************************** FUNCTION Process(loContext,loResponse) LOCAL lcPath lcPath = LOWER(locontext.Request.Url.AbsolutePath) ? lcPath

DO CASE CASE lcPath == "/foxhello" TEXT TO lcHtml NOSHOW TEXTMERGE

Response Generated by << VERSION() >>


Wooo Hoo!
Time is: << DATETIME() >>
ENDTEXT

loContext.Response.StatusCode = 200 loResponse.WriteOutput(lcHtml,"text/html") CASE lcPath == "/json" lcJson = [{"message":"Not an index.html document","count":1}] loResponse.WriteOutput(lcJson,"application/json;charset=utf-8") OTHERWISE *** Process as static file RETURN .F. ENDCASE

*** Handled return our response RETURN .T. ENDFUNC

ENDDEFINE

Notice that here I’m implementing two ‘routes’ called /foxhello and /json to demonstrate pushing back different kinds of data.

To use this class is now very easy:

SET PROCEDURE TO wwUtils ADDITIVE do wwDotNetBridge lnPort = 8080 loServer = CREATEOBJECT("MyHttpServer") loServer.nPort = lnPort loServer.Start()

GoUrl("http://localhost:" + TRANSFORM(lnPort) + "/index.htm")

WAIT WINDOW "press any key to exit..." loServer.Stop()

Instance Lifetime In order to run the server in your application, you need to make sure the server reference is kept alive, so you need to store it on some sort of global state object. You can use a PUBLIC variable or attach to form or other global application object like _Screen or your own Application object. Just make sure you start the server and save the reference and shut down the server by calling Stop() before you shut down the application.

Easy Multi-Threaded, Async .NET Calls The latest version of wwDotnetBridge includes a new InvokeMethodAsync() function, that allows you to make any .NET method calls asynchronously. What this means is that if you have a long running operation that you don’t want to wait and block your code on, you can call the .NET method, and then instead get called back when the .NET method has completed.

This is very useful for long running operations, so that your application can remain responsive. This feature is very easy to use – it’s basically the same syntax as invoking a method using .InvokeMethod() with the difference that you pass in a special FoxPro object that includes methods that receive the result or an error message on return. Although this feature works against .NET objects you can also use it with COM objects and therefore with FoxPro objects by creating a helper method that instantiates a COM object and calls it from .NET. Effectively this allows you execute FoxPro code asynchronously as well.

Running a Long Running .NET Method Asynchronously Lets create a new .NET class called Async Samples and add a new method that simulates an HTTP retrieval that takes a while to run. Imagine we’re calling a remote service to retrieve some data, but this process takes perhaps 10 or 20 seconds to run. Rather than tie up the FoxPro UI waiting for the HTTP request to complete, you can run it asynchronously. public string LongRunningOperation(string url) { Thread.Sleep(5000);

var http = new WebClient(); http.Headers.Add("cache", "no-cache"); return http.DownloadString(url); }

Obviously a bit contrived and 5 seconds isn’t massively long but it demonstrates the use case.

To call this code from FoxPro – asynchronously – you can now use code like the following: do wwDotNetBridge LOCAL loBridge as wwDotNetBridge loBridge = CreateObject("wwDotNetBridge","V4") loBridge.Loadassembly("bin\wwDotnetBridgeDemos.dll") loAsync = loBridge.CreateInstance("wwDotnetBridgeDemos.AsyncSamples")

*** Handler that is called back on completion or error loCallback = CREATEOBJECT("UrlCallbacks") loCallback.cUrl = "https://github.com/RickStrahl/wwDotnetBridge"

*** Start processing - returns immediately loBridge.InvokeMethodAsync(loCallback,loAsync,"LongRunningOperation",loCallback.cUrl)

? "*** Request submitted. Go on in your application..." ?

************************************************************* DEFINE CLASS UrlCallbacks as AsyncCallbackEvents ************************************************************* cUrl = ""

FUNCTION OnCompleted(lvResult,lcMethod) ? "Done: " + THIS.cUrl + ; " Length: " + TRANSFORM(LEN(lvResult),"9,999,999")

ENDFUNC

FUNCTION OnError(lcMessage,loException,lcMethod) ? "Error: " + this.cUrl ENDFUNC

ENDDEFINE

The key to make this work is the .InvokeMethodAsync() method which calls the requested .NET method on a new thread. It creates a new thread and executes the method on that new thread and waits for completion.

When you call .InvokeMethodAsync() you pass in a subclass of an AsyncCallbackEvents object. This object has two methods:

 FUNCTION OnCompleted(lvResult,lcMethod)  FUNCTION OnError(lcMessage, loException,lcMethod)

These methods are called when the method invocation completes passing back a result on success, or an error message and exception object if the method call fails.

You pass a subclass with your own custom handlers that fire on completion or error to .InvokeMethodAsync() which then calls you back when the operation completes. Voila – you essentially get multi-threaded Asynchronous processing.

Multiple Async Calls = Simultaneous Processing Because the async mechanism uses newly created threads in .NET you can use this mechanism to execute multiple operations simultaneously. Let’s look at another example, that retrieves a large number of URLs in parallel.

Let’s start with the .NET code which captures a URL as binary data and delays each request by up to a second. The delay is used to make the requests slow enough so you can see them completing at staggered times (because most downloads happen really fast) public byte[] GetUrl(string url) { ServicePointManager.DefaultConnectionLimit = 30;

Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);

var http = new WebClient(); http.Headers.Add("cache", "no-cache");

var rand = new Random();

var timeout = rand.Next(200, 1000); Thread.Sleep(timeout);

return http.DownloadData(url); }

Let’s set up some FoxPro code that fires of ~20 URL requests at the same time using .InvokeMethodAsync(). loBridge = CreateObject("wwDotNetBridge","V4") loBridge.Loadassembly("bin\wwDotnetBridgeDemos.dll")

CREATE CURSOR Urls (Url c(128), Content M, Length I, Started I, Updated I)

INSERT INTO Urls (url) VALUES ("http://Microsoft.com") INSERT INTO Urls (url) VALUES ("http://getbootstrap.com/components/") … 20 more

GO TOP SCAN loCallback = CREATEOBJECT("UrlCallbacks") loCallback.cUrl = Urls.Url

loAsync = loBridge.CreateInstance("wwDotnetBridgeDemos.AsyncSamples") REPLACE Started WITH SECONDS() loBridge.InvokeMethodAsync(loCallback,loAsync,"GetUrl",loCallback.cUrl) ENDSCAN

RETURN

DEFINE CLASS UrlCallbacks as AsyncCallbackEvents cUrl = ""

FUNCTION OnCompleted(lvResult,lcMethod) ? "Done: " + THIS.cUrl

UPDATE URLS SET Content = lvResult, Length=LEN(lvResult), Updated = SECONDS() - Started WHERE Url = this.cUrl

ENDFUNC

FUNCTION OnError(lcMessage,loException,lcMethod) ? "Error: " + this.cUrl ENDFUNC

ENDDEFINE

This code basically creates a cursor with a bunch of URLs. It then iterates over each URL and requests to retrieve each URL asynchronously. We have a custom UrlCallbacks object that has the callback handlers as well as a cURL property that holds the state for the callback object instance. Each URL gets its own callback object. We create the callback object and assign the URL and assign the start time.

We then call .InvokeMethodAsync() to start the processing. This happens in the SCAN loop for all the URLs in our list. The main program then ends. At this point your code your application is not blocking and the HTTP requests happen in the background – you can type at the console or run other Fox code.

As Urls get downloaded the Callback methods (OnCompleted() are fired). The implementation of this method captures the lvResult which is the binary data from the download and stores it in the database using an Update command agains the URL cursor. We basically write out the captured data, the length and the end time of the request. Update is very non-disruptive as it doesn’t move the record pointer or change work areas so it’s perfect for the task of ‘not interfering’ with the main program that could still be running and doing other things.

If you run this code a few times you’ll notice that everytime you run it the order of the URLs returned is slightly different. That’s because they are processing simultaneously and have different wait times and potentially different download times as each request runs on its own separate thread.

At this point you are effectively running multiple operations in parallel. And it’s very straight forward to do so. Running Async FoxPro Code? The .InvokeMethodAsync() method makes it easy to call .NET code, but can you use it to create multithreaded FoxPro code? Not directly, but indirectly yes! If you create a FoxPro COM object that performs whatever task you need it to perform, you can create a simple .NET wrapper method that calls and executes the COM object. You can then call that wrapper method with .InvokeMethodAsync() to effectively achieve multi-threaded FoxPro code. This can provide a super easy way to create asynchronous FoxPro code.

Bonus: Using SignalR to Broadcast Messages to many Clients Messaging is big these days – you have applications that are interacting with each other and if you need to push real time information from one application to one or more others, there are not a lot of choices. We looked at Named Pipes earlier in this article, but it’s limited to whatever you can access over the local network. Realistically Named Pipes make sense only for same machine messaging.

SignalR is Microsoft’s implementation of a Web based Publish and Subscribe model using Web technologies. Using SignalR you can use a Web server as a Hub that can receive and push message to one or more clients that are connected and listening. A SignalR client can both act as a Publisher that pushes out events and a Subscriber that can listen to events. Events can then be used to pass messages (data) to other clients.

In essence, SignalR is an easy to use .NET wrapped around Web Sockets. Web Sockets – like TCP/IP sockets – support the ability to create persistent connections over HTTP and keep that connection open to allow sending messages back and forth. Unlike regular Web applications which tend to be transactional and stateless, SignalR/WebSockets leave connections open and provide stateful interactions between the publisher and subscriber. What this means is that as soon as a publisher sends a message, the message is immediately sent to all connected subscribers. You can publish messages to all listeners or specific groups.

SignalR uses a Hub and Spoke model which uses a central server as a relay. Clients push messages to a Web server which forwards the message to subscribed clients. The Web Server holds connections to clients open using WebSockets so there’s more overhead and resource usage in this scenario compared to traditional Web applications. IIS supports Web Sockets only on Server 2012, but SignalR has fallback support for Long Polling (Long delay HTTP requests that wait for timeouts or actions), Forever Frame. This makes SignalR work on older browsers but it’s recommended that you use IIS 2012 and use Web Sockets as it’s much quicker and less resource intensive.

Hubs and Clients The key players in SignalR are Hubs which is the server ‘relay’ and clients that send messages to the hub and receive callback messages from the hub.

The Hub is a that can either run in IIS or be self-hosted in a .NET (or FoxPro) application – see the earlier Hosting a Web Server in FoxPro section. Messaging apps tend to be asynchronous, so typically you don’t return values from service calls but rather you send messages to the hub which then distributes the messages. If a recipient of a message needs to send a result back, you send another separate message that can then be trapped by the original message sender.

The client can act both as a publisher sending messages to the hub and a subscriber listening to messages coming back from the hub. It’s your choice whether you implement both in the same class or you break up the logic into separate .NET classes.

In this example, I’ll create a new SignalR hub in one .NET project, and a Proxy that can send messages and consume the messages from the Hub in another. I’ll then create a FoxPro wrapper around the Proxy and allow passing in a messaging object to capture responses in FoxPro

Creating a Hub The first step is to create a Hub and to do this you’ll need a Web application to host the Hub. Let’s create one in Visual Studio.

 Create a New Project  Choose Visual C# | Web Application  Add an Empty Web Application and call it SignalRHub  Use Add | New Template | OWIN Startup Class to the project

Open up Startup.cs and change it to: using Microsoft.Owin; using Owin;

[assembly: OwinStartup(typeof(SignalRHub.Startup))] namespace SignalRHub { public class Startup { public void Configuration(IAppBuilder app) { app.MapSignalR("/signalr",new Microsoft.AspNet.SignalR.HubConfiguration()); } } }

Which configures the application to listen for hub requests on the above URL. If you are using IIS (as I recommend so that the app is running) create a virtual directory for your application:

 Open Project Properties  Choose the Web tab  Under Servers choose Local IIS  Set the Project Url to http://localhost/SignalRHub  Click on Create Virtual

If you don’t want to use IIS, you can also use IISEXpress, but you have to make sure it’s running by starting your project in Debug mode (or View in Browser on Index.html) which is a pain. Hence I like to use a full version of IIS so the app is always available.

Next let’s create the actual Hub class with a few sample methods: using System; using Microsoft.AspNet.SignalR; namespace SignalRHub { public class SignalRHub : Hub { public void Hello(string message) { Clients.All.hello("Hello from server: " + message); }

public void GroupHello(string message, string group) { Clients.Group(group).grouphello("GroupHello from server: " + message); }

public void SendPerson(Person person, string group) { //Clients.Group(group).sendperson(person); Clients.Group(group).sendperson(person); }

public void JoinGroup(string groupName) { Groups.Add(Context.ConnectionId, groupName); }

public void RemoveGroup(string groupName) { Groups.Remove(Context.ConnectionId, groupName); } }

public class Person { public string Name { get; set; } public string Company { get; set; } public string Email { get; set; }

public DateTime Entered { get; set; } } }

The class is a subclass of Hub which provides all of the functionality. You simply implement methods on the server for the operations you want to support. Add any parameters for values you want to pass to the subscribers. These are the methods that the client can call to Publish messages.

The methods all return void – no results returned because they are asynchronous and the method implementations really do nothing more than relaying the method call to the subscribers.

For example: public void Hello(string message) { Clients.All.hello("Hello from server: " + message); }

Forwards the Hello message to All clients that are connected. All in this case is a property that provides access to all connected clients. There’s also Others which is all but the current connection, and Group(name) which allows targeting of messages to smaller groups or individuals.

Most commonly you’ll want to send to specific groups to send messages only to targeted listeners. Groups can be an individual (ie. “Rick” and “Markus”) or a group of multiple people (ie. “Sales Team”). public void GroupHello(string message, string group) { Clients.Group(group).grouphello("GroupHello from server: " + message); }

There’s no easy way to test Hubs without connecting through SignalR. However generally there’s not a lot of code in Hubs typically – in most cases Hubs simply forward the inbound messages to the connected clients just like I’m doing above.

Creating a SignalR Client Next we need a .NET class that implements a client. Essentially we end up mapping the server’s Hub API into the proxy – twice actually. Once for the publishing to those methods and once for receiving.  Create a new .NET Class Library Project and name it SignalRClient  Add a new Class using Add | New From Template  Add Class

Use the following code: using System; using System.Threading.Tasks; using Microsoft.AspNet.SignalR.Client; namespace SignalRClient { public class SignalRClient :IDisposable { const string SERVER_NAME = "http://localhost/signalrhub/";

private HubConnection Server; private IHubProxy Proxy;

// FoxPro instance public dynamic Fox;

public SignalRClient() {

}

#region Lifetime management public void Start(dynamic foxHandler) { Server = new HubConnection(SERVER_NAME);

// Specify the name of server Hub Class Proxy = Server.CreateHubProxy("SignalRHub");

Proxy.On("hello", OnHello); Proxy.On("grouphello", OnGroupHello); Proxy.On("sendperson", OnSendPerson);

Server.Start().Wait();

Fox = foxHandler; }

///

/// Shutdown the SignalR connection /// public void Stop() { Server.Stop(); Server = null; } #endregion

#region Invoke Methods on the Proxy

public void Hello(string message) { Proxy.Invoke("hello", message); }

public void GroupHello(string message, string group) { Proxy.Invoke("grouphello", message, group); }

public void SendPerson(Person person, string group) { Proxy.Invoke("sendperson",person,group); } public void SendUpdatedPerson(Person person, string group) { Proxy.Invoke("sendupdatedperson", person, group); }

public void JoinGroup(string group) { Proxy.Invoke("JoinGroup", group); }

public void RemoveGroup(string group) { Proxy.Invoke("RemoveGroup",group); } #endregion

#region Handle callbacks from the Hub Server (ie. handle broadcasts)

public void OnHello(string message) { if (Fox != null) Fox.Hello(message); }

public void OnGroupHello(string message) { if (Fox != null) Fox.GroupHello(message);

//return "Ok it worked " + DateTime.Now; }

public void OnSendPerson(Person person) { Console.WriteLine("person received on client."); if (Fox != null) Fox.SendPerson(person); } #endregion

public void Dispose() { Server?.Stop(); } }

public class Person { public string Name { get; set; } public string Company { get; set; } public string Email { get; set; } public DateTime Entered { get; set; } } }

The class implements Start/Stop methods to manage the lifetime of the connection. Start() opens the connection and Stop() shuts it down. Typically you’ll want to create an instance of the class, call Start() and then leave it running to receive events. In FoxPro this means storing it in a persistent property like a form or top level application object that sticks around.

The start method first creates a Hub Connection to the server and creates a Proxy from the specific server Hub class you want to access.

Server = new HubConnection(SERVER_NAME); Proxy = Server.CreateHubProxy("SignalRHub");

You then map all the server methods to local callback handles that match the signature of the server methods. By convention the name of the callbacks start with OnXXXX to signify these methods are the subscriber methods.

Proxy.On("hello", OnHello); Proxy.On("grouphello", OnGroupHello); Proxy.On("sendperson", OnSendPerson); Proxy.On("sendupdatedperson", OnSendUpdatedPerson);

The method also passes a dynamic instance of an object which in our case can be a FoxPro object. This object should provide matching methods from the Hub. The FoxPro object is called as part of the OnXXX methods and passed all the parameters that were passed to the base method. We will pass the FoxPro instance when we call start. public void Start(dynamic foxHandler) { Handler = foxHander; … }

Finally we actually start the connection with:

Server.Start().Wait();

The following set of methods provide both the Publish methods and the OnXXXX Subscriber method implementations.

Publish Methods These methods are used to publish events and are called when a client wants to share data. public void Hello(string message) { Proxy.Invoke("hello", message); }

public void GroupHello(string message, string group) { Proxy.Invoke("grouphello", message, group); }

public void SendPerson(Person person, string group) { Proxy.Invoke("sendperson",person,group); }

Again these methods don’t really have any logic – they simply forward the method call to the Hub server. When Each method is called, the call is forward to the Web server which in turn pushes the message to any connected clients which handle them in he Subscriber methods.

There are two special Publish methods that are not forwarded to any clients – these basically establish a spefic group connection on the server. public void JoinGroup(string group) { Proxy.Invoke("JoinGroup", group); } public void RemoveGroup(string group) { Proxy.Invoke("RemoveGroup",group); }

Join groups adds the current connection to the specific group so when you send to that group you receive Subscription messages.

Subscription Methods The Subscription methods then duplicate once again the Hub interface with OnXXXX methods that map the Hub interface. In this case again we’re simply forwarding the call this time to the passed FoxPro Callback handler object. public void OnHello(string message) { if (Fox != null) Fox.Hello(message); } public void OnGroupHello(string message) { if (Fox != null) Fox.GroupHello(message); } public void OnSendPerson(Person person) { Console.WriteLine("person received on client."); if (Fox != null) Fox.SendPerson(person); } public void OnSendUpdatedPerson(Person person) { if (Fox != null) Fox.SendPerson(person); }

As you might expect the FoxPro object is called when an event occurs – if the object is attached. Let’s create it.

Using the Proxy in FoxPro In order to use the Proxy in FoxPro I’ll use wwDotnetBridge to instantiate the SignalRClient and then create a custom CallbackHandler class that implements the methods we want to handle.

We’ll need two things:  A bit of code to act as a listener  Code to fire a message

Let’s start by creating a SignalRListener that can listen to events fired by the SignalRClient:

LPARAMETERS lcGroup

IF EMPTY(lcGroup) lcGroup = "Rick" ENDIF

DO wwDotNetBridge LOCAL loBridge as wwDotNetBridge loBridge = CreateObject("wwDotNetBridge","V4") loBridge.LoadAssembly("signalrclient.dll") loHandler = CREATEOBJECT("SignalRCallbackHandler") loProxy = loBridge.CreateInstance("SignalRClient.SignalRClient") loProxy.Start(loHandler) loProxy.JoinGroup(lcGroup)

CLEAR ? "*** Listening for messages for: " + lcGroup

WAIT WINDOW loProxy.Stop()

RETURN

DEFINE CLASS SignalRCallbackHandler as Custom

FUNCTION Hello(lcMsg) ? "Simple Hello: " + lcMsg ENDFUNC

FUNCTION GroupHello(lcMsg) ? "Group Hello: " + lcMsg ENDFUNC

FUNCTION SendPerson(loPerson) ? "SendPerson Returned from server: Name: " + loPerson.Name ENDFUNC

ENDDEFINE

The code starts out by loading up the Proxy .NET class and starting the server. Note the SignalRCallbackHandler class below that is used to handle the events we’re interested in. We create an instance of this class and pass it to the Start() method which allows our Fox code to get notified when an event callback occurs. The class should map all the Client and Hub methods with the same input parameters.

We also join a group so we can listen for events meant for a specific group. The listener now will receive events for global (non group) events and specific group events.

Then we wait – the SignalR instance quietly sits in the background and waits for incoming connections, but the class needs to stay alive to do that. Here I use WAIT WINDOW but in an application you’d use a form, _Screen or some other long lived component to keep the reference alive.

Calling the Proxy Now we’re ready to send messages. Let’s start with the Hello() method which is sent to all.

LPARAMETERS lcMessage

IF EMPTY(lcMessage) lcMessage = "Hello Southwest Fox. Time is " + TIME() ENDIF do wwDotNetBridge LOCAL loBridge as wwDotNetBridge loBridge = CreateObject("wwDotNetBridge","V4") loBridge.LoadAssembly("signalrclient.dll")

loProxy = loBridge.CreateInstance("SignalRClient.SignalRClient") loProxy.Start(null) loProxy.Hello(lcMessage) loProxy.Stop()

To get the full effect of this we need to run two instances of FoxPro.

In the first instance run SignalRListener.prg and the in the second run SignlarRHello.prg

If you run this program as is you get:

Simple Hello: Hello Southwest Fox. Time is: 1:02 echoed to the screen in the Listener instance. If you do the request has made it to the server hub, forwarded to the proxy and then pushed into our Fox object that receives this message. It works.

Now fire up another instance of FoxPro and run SignalRListener.prg in it again. Then go back to the SignalRHello.prg instance and run it again. Both listeners are now receiving the messages. You can keep adding listeners and they all will receive and respond to the messages. Nice!

Using Groups to focus Messages Sending messages to all is generally not very useful. Depending on how your application works you’ll want to be have specific messages sent to specific groups. For example, if you build a chat application you probably would want to capture all messages for a specific chat room in a group.

If two clients are communicating privately you might have a group with a unique id.

Notice that the Listener started with a specific group name. We joined a group and are listening to messages that come from that group. So lets say I want to send all messages to a group called Markus. I’ll use the SignalRGroupHello.prg to do this: DO SignalRGroupHello with "Hello Markus…","Markus"

The code here just calls a separate method: loProxy.Start(null) loProxy = loBridge.CreateInstance("SignalRClient.SignalRClient") loProxy.Start(null) loProxy.GroupHello(lcMessage,lcGroup) loProxy.Stop() that passes along the group as a parameter.

When I run this against the existing listeners there will be – crickets. Nothing happens because our listener isn’t listening to the ‘Markus’ group.

To do this I now have to change the way I launch the SignalRListener:

DO SignalRListener with "Markus"

Now if a run the previous client code, the message arrives but only in the Markus listener instance. You run multiple instances and each can listen to a different group. It’s very useful to managing users in an application.

Pass Objects So far I’ve only passed strings – you can also pass objects around the system. Let’s look at the following SendPerson() function that sends a person object to be published: loProxy.Start(null) loPerson = loBridge.CreateInstance("SignalRClient.Person") loPerson.Name = lcName loPerson.Company = "West Wind" loPerson.Email = "[email protected]" loPerson.Entered = DATETIME() loProxy.SendPerson(loPerson,lcGroup) loProxy.Stop()

In order to pass an object, we have to create a .NET object instance and populate it with values using loBridge.CreateInstance(). Other than that the behavior is the same as the HelloWorld method. The event callback is handled in the callback handler object of the listener:

FUNCTION SendPerson(loPerson) ? "SendPerson Returned from server: Name: " + loPerson.Name ENDFUNC

Summary And there you have it – a powerful way to pass information over the Web to other applications.

There are lots applications for this. Remember this is two-way communication so anything can be both a sender and a receiver of messages. I’ve used this mechanism in a number of applications where we captured incoming data from a Web server and pushed it into desktop applications running in different locations. It’s immensely powerful because it removes the barrier between computers.

It’s pretty powerful technology and I encourage you to learn more about SignalR or WebSockets more generally and distributed asynchronous application development.

Summary wwDotnetBridge is a powerful tool to extend the functionality of Visual FoxPro by allowing you to take advantage of just about any .NET code in your FoxPro applications. You can call native .NET features which are fairly vast and cover many Windows System services in easy to use APIs. You can take advantage of the ever growing wealth of open source .NET libraries that can be integrated into your applications. And finally you can create your own .NET code to handle more complex .NET interactions or creating your own .NET libraries and then call that code from FoxPro.

There’s a lot you can do, so think outside of the box and take a look at what’s available, or reconsider things that you thought you could not do with FoxPro and see if you can do it in .NET – often times there are solutions that you can then adapt to integrate into your FoxPro code with wwDotnetBridge.

Happy bridging.

Resources  Source Code, Slides, Notes for this Sample on BitBucket  wwDonetBridge on GitHub

About Rick Strahl Rick Strahl is the Big Kahuna and janitor at West Wind Technologies located on the beautiful island of Maui, Hawaii. Between windsurf sessions and spikey haired adventures, Rick has been a software developer for over 25 years, developing business and Web applications since the very early days of the Web when you needed a hand crank (or a pair of wire splicers) to get online. Today Rick builds client centric Web applications and services for customers with HTML5, JavaScript and mobile Web technologies, using AngularJS on the front end, and the ASP.NET stack and Visual FoxPro on the back end. Rick’s company West Wind Technologies also produces a number of developer related tools including West Wind WebSurge, Html Help Builder and West Wind Web Monitor. Rick is also available for consulting and mentoring services and maintains a host of open source libraries at http://github.com/RickStrahl. You can find Rick’s blog at weblog.west-wind.com or contact him directly at http://west-wind.com/contact.