Using IronPython for Application Testing

Understanding Why You Want to Use IronPython for Testing

Every testing technique you’ve ever used has some drawback. For example, if you include debug statements in your code, you must ensure that you perform a release build to remove the statements before you release the code. Otherwise, the application will run slowly. In addition, using debug statements can cause the application to perform differently from the way it performs when you use it in a production environment, which makes it possible that the very tests that you depend on to check the application will actually hide problems from view.

Using IronPython for testing has a considerable number of benefits over other testing tools. The biggest benefit is that you don’t have to do anything special to the application. The test harness you create exists outside the application and doesn’t affect the application in any way. All the test harness does is monitor application behavior and report on it to you. As a result, if the test harness reviews every aspect of the application and verifies that it runs correctly, the application will run correctly in the production environment, too, because nothing will have changed.

As you’ve seen throughout the book, IronPython is an interpreted environment. That means you don’t have to create the test harness in one piece — you can create it a little at a time as you try things out with the application. In fact, the very nature of IronPython makes it possible for you to play “what if” analysis on your application. You can see just how bad you can make the application environment and try things that users do, such as create unreasonable execution conditions.

Using an IronPython script for testing means that all the testing code is in one place. If you decide that you need to add another test, you don’t have to delve into the inner workings of the application to add it and then create another build. Instead of using this time-consuming process, you simply add a few more lines to an external script using any text editor that you like. There’s nothing complicated about the process — anyone knowledgeable about your application should be able to do it without any problem.

The external nature of IronPython also makes it impossible for your test code to add problems (such as errors, performance issues, or reliability concerns) to the application. In some cases, adding test code actually introduces an application error, making it hard to know whether the error is in the test harness or the application. If there’s a problem in the IronPython test harness, you’ll see an IronPython error telling you about it. In short, you have separation between the test harness and the application, which ensures one won’t affect the other.

There are a few downsides to working with IronPython as a testing tool. The most important of these issues is that IronPython treats your application like a series of black boxes. It provides input to a method and expects a certain output. However, IronPython can’t see into the method to test individual elements within it.

IronPython also can’t see private members of your application, so it can’t test absolutely every aspect of your application. If a private member is causing a problem, you need to use some other tools to find it. Of course, you can use IronPython to infer certain issues in private methods based on their effect on public methods, but this kind of logic can prove more troublesome than direct testing.

Considering the Test Environment

Before you begin writing your test harness, you need to consider the test environment. The test environment determines how you test the application, be it a DLL or a desktop application with user access. The following list provides some criteria you need to consider as part of the test environment.

  • Code access: You must define how the test harness will access the code. It’s important to determine whether the harness will test absolutely every method, property, event, and other application element individually, whether it will test elements in combination, or whether it will use a combination of individual and combined tests.
  • Test ranges: A test harness must test both the possible and the impossible. For example, you might design a method to accept positive numbers from 0 through 5. However, the test harness must also test numbers greater than 5 and less than 0. In addition, it must test unexpected input, such as a string.
  • User emulation: When working with some applications, you must determine how to emulate user activity. For example, you might write down a series of steps that the user will take to perform a certain activity and then execute those steps in your test harness. Of course, users are unpredictable; your script must also perform some haphazard and unpredictable steps and provide unexpected input. If you find that users are doing something you never expected, you must add it to the test harness.
  • Security testing: If you don’t try to break down the walls you erected for your application, someone else will most certainly sign up for the job. Because IronPython tends to treat everything as public, it actually makes a great tool for testing security. You’ll find no artificial walls to keep things neat and tidy. Security is never neat or tidy — it’s all about someone ripping away the veneer of the façade you called security when you put the application together. IronPython lets you test your application brutally, the same way someone else will.
  • System characteristics: Even though you can’t write code to ensure that your application will run on every machine in the solar system, you can do things such as add random pauses in your code to mimic activity on an overloaded system. You can also execute your application and its test harness on a number of different machine configurations to verify that the application will run as expected.

There are probably other criteria that you need to consider for your individual testing scenario. Take time to brainstorm scenarios, worst-case situations, and truly horrifying events, and then test for them. The following sections provide some additional insights about the test environment and the issues you must consider.

Defining Access

The matter of access is an essential part of testing. The word “access” has all kinds of meanings and connotations. Of course, there’s the access of your test harness to the code within the application. The black box nature of IronPython prevents access in depth, but careful programming can provide access to unprecedented amounts of information within your application and make testing relatively complete.

You must also consider the access the user has to the application as part of the test harness. For example, if you use external configuration files, you can count on some number of users accessing them. Even if you work out methods that are seemingly impossible to overcome, a user or two will find a way to overcome them. Anything you can devise will be overcome by someone (it’s always easier to destroy than to create). Consequently, you must consider all forms of user access as part of your test harness — if for no other reason than to determine how bad things can get when a user meddles.

It’s also important to consider external access. Whenever a system has access to the network or the Internet, you must consider the potential for outside sources to access your application (even if your application isn’t designed for such access). Many vendors of shrink-wrapped software have gained notoriety for not taking this kind of access into consideration. The thought was that the application didn’t access the outside source, so there wasn’t any need to consider the outside source during testing. It turns out that any outside access opens avenues of influence and attack for all the applications on a system, so you must test this kind of access.

Access is a two-way street. As part of your testing harness, you must consider application access to external resources. For example, you must consider what happens when an application attempts to access a particular file on disk and can’t find it. Even more important, you need to consider resources on the network or on the Internet. There are many forms of access that your test harness must consider as it tests the various methods inside the application. It isn’t always possible to test simply for strict inputs or outputs; you must test inputs and outputs within the confines of an environment defined by various kinds of access.

Considering a Few Things IronPython Can’t Test

Earlier, you learned that IronPython tests application elements using a black box approach — given a particular input, what should the element provide as output? However, there are other limitations you need to consider in the way IronPython performs testing. For example, IronPython can’t perform stress testing. If you want to test your application in a memory-starved environment, then you need to combine IronPython with another tool. For example, you might want to read the article at http:// msdn.microsoft.com/magazine/cc163983.aspx about a load-generating tool you can build yourself. Web application load testing requires other techniques that you can learn about at http:// support.microsoft.com/kb/231282. If you need to stress test applications in combination with a particular server, check out the site at http://blogs.msdn.com/nickmac/archive/2004/10/06/ server-stress-tools.aspx.

IronPython can perform diagnostic testing of your application with ease, but it doesn’t make a good environment for performance testing. As with stress testing, you need to combine IronPython with another tool to check application performance in various conditions. In fact, you may very well need to combine IronPython, your stress testing tool, and your performance testing tool to obtain statistics for a range of test scenarios and environments.

The point of this section is that while IronPython is a good scripting tool or a good diagnostic tool, it can’t do everything. In many cases, you must combine IronPython with one or more additional tools to obtain the desired information about your application. Your test plan should include all of these contingencies, and you should consider them before you create your test harness.

Creating the Test Harness

An advantage to working with IronPython is that you need not create the test harness in one sitting. You can use an iterative technique to create the test harness. It’s possible to start with a small nugget of tests that you know you must perform, and then add to that nugget as other issues come to light. Eventually, you end up with a full-blown test suite.

Most .NET developers won’t initially understand the benefits of using an interpreter for testing, but the realization will grow with time that interpreters make things easy. If you get an idea, you don’t have to run a complete test or compile anything. All you need to do is open up the IronPython console, load the assembly you want to test, and then try out various tests until you come up with a perfect combination of items to use. At this point, you can click the system menu in the IronPython console, choose Edit ➪ Mark, highlight the text you want to copy from your experiments, and press Enter to copy it to the clipboard. Now you can paste the text you’ve created into your test harness and comment it. In fact, the IronPython console (and all consoles for that matter) provides a number of commands, as shown in Figure 18-1.

As an alternative, if you already have the beginnings of a test-harness check, but want to add to it, you can always paste the text directly into the IronPython console using the Paste command shown in Figure 18-1. The interpreter will automatically execute any statements that you paste into it, so you’ll be ready to start typing new code after you paste it.

Modularity is the name of the game when it comes to a test harness. Try to place the individual tests into separate files so that you can reuse the code later. Simply have a centralized file where you call each of the tests in turn. The tests will output the information you need to screen, so the developer using the test harness need not even know that there are multiple files involved.

Use the text-editing tools to copy and paste text as needed.
Figure 18-1: Use the text-editing tools to copy and paste text as needed.

Testing DLLs

DLLs present one of the easier ways to begin using IronPython to test applications. In fact, you’ve already performed a kind of testing in Chapters 16 and 17 when you created the extensions and then used their content as part of an IronPython application. All that a test harness will do is formalize the testing process so that the output you receive speaks directly about the functionality of the DLL under test, rather than its use in an application. The following sections describe how to perform a test on a DLL using IronPython.

Creating the Test DLL

The DLL used for testing purposes is extremely simple so that the section doesn’t focus more on an interesting DLL than it does on testing techniques. All that this DLL provides is an account where you make an initial deposit to create the account and then make deposits, withdrawals, and transfers. The DLL includes a number of features so that you can try things out, but the code definitely isn’t production quality. For one thing, most of the error-checking code is left out to keep the code clear so you can easily see what will happen next. Listing 18-1 shows the DLL code used for this example.

Listin g 18-1: Defining a DLL to test

[code]
public class Accounts
{
// Contains the current account amount.
private Int32 Total;
public Accounts()
{
// Sets a default acccount amount.
Total = 5000;
}
public Accounts(Int32 Initial)
{
// Set a user supplied initial amount.
Total = Initial;
}
// Provides access to the account total.
public Int32 GetTotal
{
get { return Total; }
}
// Adds a deposit to the account.
public Int32 Deposit
{
set { Total += value; }
}
// Subtracts a withdrawal.
public Int32 Withdrawal
{
set { Total -= value; }
}
public void Transfer(Accounts Account2)
{
// Place the money in the second account in the first account.
this.Total += Account2.Total;
// Withdraw the money from the second account.
Account2.Total = 0;
}
}
[/code]

The example includes two constructors (something you didn’t try in Chapters 16 or 17). The developer can create an account with a default value of 5000 or provide some other initial amount. In either case, you end up with a new Accounts object that has Total defined.

The GetTotal property is read-only and lets the developer obtain the total in the count from Total. Using a property enables you to perform checks before allowing people to have the information. For example, you could place a security code in this property to ensure that only authorized personnel received the information. If a developer were to take this approach, you’d need to write a test to check the GetTotal property using an account other than the developer account.

The Deposit and Withdrawal properties are write-only. The caller doesn’t receive anything back from them. You could use a method to perform the task as well. Using a property makes the test code easier to read, but that’s the only advantage. In both cases, the properties change the value of Total. Of course, you can perform checks in the properties, such as verifying that a withdrawal won’t result in an account with a value less than 0.

The Transfer() method moves all the money from one account to the other. Typically, you’d provide some type of transaction support in a method of this type, but the example doesn’t include it. This is one situation where IronPython can test the method’s inputs and outputs, but can’t test the internal workings of the method. You’d need another tool to test issues such as whether the transaction support actually worked as intended.

Creating the DLL Test Script

It’s time to build an IronPython script to test the DLL shown in Listing 18-1. In this case, the test script is a bit short and doesn’t test every contingency (such as starting with a negative amount in the account), but it demonstrates how you’d create a test script for a DLL. Listing 18-2 contains the code needed for this example.

Listin g 18-2: Developing a DLL test harness

[code]
# Creates a new heading.
def CreateHeading(Test, Title):
print ‘n########################################‘
print ‘Test ID = ‘, Test
print ‘Test Title = ‘, Title
# Displays the values.
def ShowValues(Expected, Received):
print ‘Expected Value = ‘, Expected
print ‘Received Value = ‘, Received
if Expected == Received:
print ‘Test Passed’
else:
print ‘Test Failed’
# Ends the test.
def CreateFooter():
print ‘########################################‘
# Print out statements of everything the test is doing.
print ‘Beginning Test’
print ‘Loading clr’
import clr
print ‘Loading test module’
clr.AddReferenceToFile(‘TestDLL.DLL’)
from TestDLL import *
CreateHeading(‘0001’, ‘Creating Account1’)
Account1 = Accounts()
ShowValues(5000, Account1.GetTotal)
CreateFooter()
CreateHeading(‘0002’, ‘Making a Deposit’)
Account1.Deposit = 1000
ShowValues(6000, Account1.GetTotal)
CreateFooter()
CreateHeading(‘0003’, ‘Making a Withdrawal’)
Account1.Withdrawal = 500
ShowValues(5500, Account1.GetTotal)
CreateFooter()
CreateHeading(‘0004’, ‘Creating Account2’)
Account2 = Accounts(3000)
ShowValues(3000, Account2.GetTotal)
CreateFooter()
CreateHeading(‘0005’, ‘Transferring Money’)
Account1.Transfer(Account2)
print ‘nAccount1 = 8500’
ShowValues(8500, Account1.GetTotal)
print ‘nAccount2 = 0’
ShowValues(0, Account2.GetTotal)
CreateFooter()
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

Let’s begin with the three functions at the beginning of the script: CreateHeading(), ShowValues(), and CreateFooter(). It may seem a bit silly at first to create these functions, but they provide a method for changing the output of the tests quickly, should you need to do so. In addition, you don’t want to write the same print statements hundreds of times as you create your script. It’s far easier to simply call the functions.

The CreateHeading() and CreateFooter() functions don’t have much logic in them — they simply display information onscreen. The ShowValues() function does have a bit of logic. In this case, it simply compares the expected value to the result and displays the appropriate output text. However, you could perform any number of checks required by your application. For example, if you’re working with strings, you might need to check the string length and determine precisely how it differs from another string.

Notice that the __main__() code begins with print ‘Loading clr‘. It’s important to describe every event that occurs in the test script. Otherwise, you won’t know where a script has failed during testing. Make sure you describe the mundane acts of loading and unloading modules, as well as the actual tests.

The first test begins with a call to CreateHeading() with the test number and title. The code then performs a test, Account1 = Accounts() in this case, calls ShowValues() to test the result, and finishes with CreateFooter(). Almost all of the tests follow the same pattern.

The final test is a little different than the rest. To perform the test correctly, you must evaluate the content of both Account1 and Account2. This is a case where you can infer what is happening inside a method with the test code. The method, Transfer(), could perform the task correctly with Account1, but not with Account2, which would tell you something about the content of the method and where to look for the problem.

This final bit of script also shows the flexibility of using the three functions presented earlier. By separating the individual tasks into three parts, you can call the ShowValues() function multiple times as needed. You might also consider creating a second form of ShowValues() to accept a comparison string for output (the print ‘nAccount1 = 8500‘ part of the script).

Performing the DLL Test

It’s time to run the DLL test. If you configured your project using the techniques in Chapters 16 and 17, you should be able to click Start Debugging (or press F5) to start the build process. During the build process, the compiler checks your DLL for major errors.

After the DLL is built, the IronPython script runs. Remember that this script is running outside of the IDE, so nothing it does will actually affect the performance of your code. The diagnostic tests will run and provide the information shown in Figure 18-2.

Notice that the use of formatting, test numbers, titles, comparison values, and so on makes the test results extremely easy to read. Of course, a large DLL could overwhelm the capacity of the console to display information. In this case, you could just as easily send the output to a text file, HTML page, or an XML file. The point is that the script makes it possible to view diagnostics about your application almost immediately after you build it.

Testing Applications

You can use IronPython for more than DLL testing — you can also use it to test your applications. Applications are more of a challenge than DLLs because you have to find a way to emulate user input. Of course, many developers just aren’t as creative as users. A developer would never think about putting text where a number is expected. Many developers discover, to their chagrin, that users will also try implanting scripts and doing other weird things to the application that aren’t easy to test. Some users will even try odd character combinations looking for hidden application features or just to see what will happen. Tests will only work as well as your ability to outguess the user. The following sections show how to test a simple Windows Forms application.

The output shows a list of all of the tests run by the IronPython script on the DLL.
Figure 18-2: The output shows a list of all of the tests run by the IronPython script on the DLL.

Creating the Test Application

The test application is very simple, but it does include some internal code you can use for testing purposes. The following sections describe the test application.

Defining the Form

A Windows Forms application need not be complex to test it using IronPython. All you really need are a few controls and some buttons with code for their event handlers. Figure 18-3 shows the simple form used for this example.

As with Windows Forms you use in a DLL, you must make an important change to test an application using IronPython. All the controls you want to access must have their Modifiers property set to Public. The default setting of Private prevents you from accessing them directly in IronPython.

Building the Code

You can see that the form in Figure 18-3 has three Button controls in it. Each of the controls has a Click() event handler associated with it, as shown in Listing 18-3.

The simple form used for this example provides enough inputs to test.
Fi gure 18-3: The simple form used for this example provides enough inputs to test.

Listin g 18-3: Defining an application to test

[code]
private void btnQuit_Click(object sender, EventArgs e)
{
Close();
}
public void btnAdd_Click(object sender, EventArgs e)
{
txtResult.Text = (Int32.Parse(txtValue1.Text) +
Int32.Parse(txtValue2.Text)).ToString();
}
public void btnSubtract_Click(object sender, EventArgs e)
{
txtResult.Text = (Int32.Parse(txtValue1.Text) –
Int32.Parse(txtValue2.Text)).ToString();
}
[/code]

The btnQuit_Click() event handler is as you might expect. It simply closes the form using the Close() method. You won’t test this functionality using the IronPython script.

The btnAdd_Click() event handler converts the values of txtValue1.Text and txtValue2.Text to Int32 values using Int32.Parse(). It then adds the numbers together, converts the result to a string using ToString(), and places it into txtResult.Text. Because IronPython needs to test this event handler, the visibility is set to public. If you don’t change the visibility of the event handler, IronPython won’t be able to access it. The btnSubtract_Click() event handler works the same as the btnAdd_Click() event handler, except that it subtracts the two numbers.

Creating the Application Test Script

As long as you’re willing to make the required visibility changes to your application, you can use IronPython to test it. Creating a test project for an application works precisely the same as creating a test project for a DLL. Here’s the short list of changes you must perform:

  1. Change the build output location for both the Debug and Release builds to the solution folder.
  2. Add IPY.EXE as an existing project to your solution.
  3. Set the ipy project as the startup project so that the IDE executes it instead of the Windows Forms application.
  4. Configure the ipy project to start your script and to use the appropriate working directory.
  5. Add a new IronPython script to the solution folder.

This test script uses the three functions described in Listing 18-2 to provide output. It also adds the following two output functions:

[code]
# Verify the type.
def CheckType(Object, Type):
if Object.GetType().__str__() == Type:
print ‘Test Passed’
else:
print ‘Test Failed’
# Show initial values.
def ShowInit(Value1, Value2):
print ‘Value1: ‘, Value1
print ‘Value2: ‘, Value2
[/code]

The CheckType() function compares the type of an object you create against an expected type. If the type is incorrect, then it displays a failed message. You can use this function when creating a form or other object that could fail for any number of reasons.

The ShowInit() function displays the initial values for a binary operation or perhaps just two values used for some other task. You could probably create a version of the function that accepts any number of arguments in the form of an array. The point is that you can create some specialized functions to display data for a particular test and then find that you can use it for other purposes later.

As previously mentioned, this test script also uses the three functions found in Listing 18-2. Listing 18-4 shows the actual test script for this application. It doesn’t provide a complete test but does provide enough information that you could easily complete it if you wanted.

Listin g 18-4: Developing an application test harness

[code]
# Print out statements of everything the test is doing.
print ‘Beginning Test’
print ‘Loading clr’
import clr
print ‘Loading System assembly support’
import System
print ‘Creating a blank event argument.’
EventArg = System.EventArgs()
print ‘Loading test module’
clr.AddReferenceToFile(‘TestApplication.EXE’)
from TestApplication import *
CreateHeading(‘0001’, ‘Creating a test form’)
MyForm = Form1()
CheckType(MyForm, ‘TestApplication.Form1’)
CreateFooter()
CreateHeading(‘0002’, ‘Testing a default add’)
MyForm.btnAdd_Click(object, EventArg)
ShowInit(MyForm.txtValue1.Text, MyForm.txtValue2.Text)
ShowValues(‘2’, MyForm.txtResult.Text)
CreateFooter()
CreateHeading(‘0003’, ‘Testing a default subtract’)
MyForm.btnSubtract_Click(object, EventArg)
ShowInit(MyForm.txtValue1.Text, MyForm.txtValue2.Text)
ShowValues(‘0’, MyForm.txtResult.Text)
CreateFooter()
CreateHeading(‘0004’, ‘Testing add with one change’)
MyForm.txtValue1.Text = ‘5’
MyForm.btnAdd_Click(object, EventArg)
ShowInit(MyForm.txtValue1.Text, MyForm.txtValue2.Text)
ShowValues(‘6’, MyForm.txtResult.Text)
CreateFooter()
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

The test script begins by loading the required code for the test, beginning with clr. Because this test has to work with event handlers, it needs to load the System assembly and create a System.EventArgs object, EventArg. Because the event handlers in this application don’t actually use the event arguments, EventArg is actually a default object with no content. The call simply won’t succeed without it, however, so you must create it.

After the script finishes the prerequisites, it performs the first test, which is to create the Windows Forms object, Form1, as MyForm. The creation process could fail; you want to verify that MyForm isn’t null, so that’s the first test that relies on the CheckType() function. You don’t have to show the form to test it, so the code doesn’t call ShowDialog(). If you do decide to show the form, you’ll actually need someone to work with it. The script is suspended during the time the form appears onscreen.

The next step is to perform some tasks with the form. The code performs a default add and subtract. The two value fields, MyForm.txtValue1.Text and MyForm.txtValue2.Text, contain default values that you can use for testing. Actually, it’s good application design to always include default values for the user so that the user has some idea of what kind of content to provide.

The MyForm.btnAdd_Click() and MyForm.btnSubtract_Click() event handlers perform the actual addition and subtraction. In order to call these two methods, you must supply both a sender object and event arguments. The sender object can simply be an object because the code doesn’t use it.

The final test in the example is to change one of the values and perform another addition. To perform this task, the script changes the value of MyForm.txtValue1.Text and calls MyForm.btnAdd_Click(). Normally, you’d provide a wealth of additional tests to check various values and see how they react with the code. For example, you might provide some negative values to ensure that the event handlers work properly with them. You might also test incorrect input, such as providing a string. The point is that you can completely automate any level of testing using this IronPython script technique.

Performing the Application Test

At this point, you have an application to test and the script to test it. It’s time to run the application. One of the problems you could encounter is not making something public (such as an object, control, or property) that you need to test (the default is to create private objects, controls, and properties). Unfortunately, the need to make class members public is one of the problems of using IronPython for desktop application testing. It’s not a big problem, but you need to consider it. When working with an extremely large application, changing the required member visibility could prove problematic. In addition, making some members public could pose security risks.

Let’s hope everything works as anticipated when you run the test. Figure 18-4 shows typical output from this application.

As with the DLL testing script, this script outputs text that’s easy to read and results that are easy to decipher. You know immediately whether certain tests have failed and precisely what inputs were used to conduct the test. As with DLL testing, you may need to use some other form of output, such as an XML file, when performing testing on complex applications because the content won’t fit entirely onscreen.

Desktop applications can prove difficult to test, but the results are worth it.
Fi gure 18-4: Desktop applications can prove difficult to test, but the results are worth it.

Performing Command Line Tests

For many developers, testing must be formal or it really isn’t testing. Actually, ad hoc testing is sometimes better because you get to play with the application while you test it. Testing in an ad hoc manner at the command line is possible in IronPython because it’s an interpreted environment. In fact, we’ve been performing ad hoc testing throughout the book. Every time you reviewed the content of an application, no matter what type it was, using the dir() function, you were performing a kind of ad hoc testing because you were reviewing the content of the application.

The test demonstrated that the DLL had added the overrides correctly and that you should be able to access them from an application. In addition, you discovered that IronPython views the content of the DLL in a slightly different manner than another environment might view them.

Let’s look at a specific test example, the TestDLL.DLL file. For the purposes of this example, you want to use the dir() function to determine whether the Accounts class contains everything it should (and nothing it shouldn’t), as shown in Figure 18-5. Notice that there’s no mention of Total in this list, but you can see all of the properties and methods described in Listing 18-1.

Make sure you check the actual content of the DLL against the expectations you have for it.
Fi gure 18-5: Make sure you check the actual content of the DLL against the expectations you have for it.

If you remember from Chapters 16 and 17, the __doc__() function is undefined for an assembly that you import into IronPython, but the help() function does produce a result. One of the next checks you should perform manually is to verify that the assembly provides the kind of information you expect from help. Figure 18-6 shows the output of the help() function for the Accounts class. Notice that it contains all of the information you expect, including the fact that there are two forms of __new__(), the constructor, and the read/write state of the various properties.

Of course, you’ll want to perform other sorts of manual testing that could eventually appear in your test script. For example, you might decide to check whether the Accounts class will let you create an account with a negative starting amount (it will).

It would be also helpful to know whether someone could circumvent some of the properties in the Accounts class. You wouldn’t want someone to use code such as Account2 = Account2 + 20 to overcome the protections in the Deposit property. In this case, the test displays an error. Another check might include adding two accounts together, such as Acccount3 = Account1 + Account2.

By now, you should have the point of using manual testing. You can creatively think of ways that someone might try to overcome protections in your code. It probably isn’t possible to find every avenue of entry into a DLL, but testing in this way helps you think through more potential problems that other forms of testing allow. Interactively probing your code is a unique method of testing the impossible.

Verify that the help() function doesn’t show any surprises about your assembly.
Fi gure 18-6: Verify that the help() function doesn’t show any surprises about your assembly.

Using IronPython from Other .NET Languages

Understanding the Relationship between Dynamic and Static Languages

Something that most developers fail to consider is that, at some point, all languages generate the same thing — machine code. Without machine code, the software doesn’t execute. Your computer cares nothing at all about the idiosyncrasies of human language and it doesn’t care about communicating with you at all. Computers are quite selfish when you think about it. The circuitry that makes up your computer relies on software to change the position of switches — trillions of them in some cases. So computers use machine code and only machine code; languages are for humans.

When it comes to dynamic and static languages, it’s the way that humans view the languages that make them useful. A dynamic language offers the developer freedom of choice, call it the creative solution. A static language offers a reliable and stable paradigm — call it the comfort solution, the one that everyone’s used. How you feel about the languages partly affects your use of them. In the end, both dynamic and static language output ends up as machine code. Dynamic and static languages end up being tools that help you create applications faster and with fewer errors. If you really wanted to do so, you could write any application today using assembler (a low-level language just above machine code, see http://www.bing.com/reference/semhtml/Assembly_language for more information), but assembler is hardly the correct tool any longer — humans need a better tool to put applications together. The point is that you should use the tool that works best for a particular development process and not think that the tool is doing anything for your computer.

Anytime you use multiple languages, you must consider issues that have nothing to do with the dynamic or static nature of that language. For example, you must consider the data types that the languages support and provide a method for marshaling data from one language to the other. In fact, marshaling data is an important element in many areas of coding. If you want to communicate with the Win32 API from a .NET-managed language such as C# or Visual Basic.NET, you must marshal the data between the two environments. It’s important not to confuse communication and infrastructure requirements with differences between dynamic and static languages. Many resources you find do confuse these issues, which makes it hard for anyone to truly understand how dynamic and static languages differ.

Before you can use IronPython from other languages, it’s important to consider the way in which IronPython performs tasks. When an IronPython session starts, nothing exists — the environment begins with an empty slate. You’ve discovered throughout this book that IronPython calls upon certain script files as it starts to configure the environment automatically. These configuration tasks aren’t part of the startup; they are part of the configuration — something that occurs after the startup. The dynamic nature of IronPython means that all activity begins and ends with adding, changing, and removing environment features. There aren’t any compiled bits that you can examine statically. Everything in IronPython is dynamic.

When a static language such as C# or Visual Basic.NET attempts to access IronPython, it must accommodate the constant change. If you got nothing else out of Chapter 14 but this one fact, then the chapter was worth reading. In order to do this, C# and Visual Basic.NET rely upon events because they can’t actually accommodate change as part of the language. An event signals a change — an IronPython application has modified a class to contain a new method or property. It isn’t just the idea that the output or value has changed, but the method or property itself is new. In some cases, C# or Visual Basic.NET will also need to deal with the situation where a method or property simply goes away as well. The underlying mechanism of events, delegates, and caches is inspired and all but invisible, but to be successful at using the languages together, you must know they’re present.

The differences between dynamic and static languages go further than simply not knowing what code will execute next in a dynamic language. There’s also the matter of data typing. A static language assigns a type to the data it manages, which means that the compiler can make assumptions about the data and optimize access to it. A dynamic language also assigns types to the data it manages, but only does so at run time and even then the data type can change. Now, consider how this changeability complicates the matter of marshaling data from one language to the other. Because the data no longer has a stable type, the marshaling code can’t assume anything about it and must constantly check type to ensure the data it marshals appears in the right form in the target language.

The difference between dynamic and static languages, at least from a programming perspective, comes down to flexible coding and data typing. Everything else you may have heard either relates to differences between any two languages (such as the need to marshal data) or the political drama of which tool works best. This book won’t endeavor to tell you what tool to use. Certainly, I don’t tell anyone that a hammer works best for driving screws or that screwdrivers make wonderful ice picks (not that I believe either of these statements myself). The tool you use for a particular task is the one you can use best or the one called for by a particular job requirement. The point of this chapter and the rest of the book is to demonstrate that dynamic and static languages can work together successfully and in more than one way. The tool you use is up to you.

Creating an Externally Accessible IronPython Module

The first requirement for building an application that allows external access is to create the IronPython script you want to use. Ideally, this script will contain code that is fully debugged. You also want to test the code before you try to use it within C# or Visual Basic.NET. The following sections provide you with the techniques you use to create an IronPython script that you access from C# or Visual Basic .NET.

Considering Requirements for Externally Accessible Modules

The mistake that many developers will make is to think they must do something special in IronPython to make the code accessible. What you really need to do is create an IronPython script using the same techniques as always, and then test it directly. After you test the script using IronPython code, work with the target static language to gain the required access. This pretesting process is important to ensure that you aren’t fighting with a bad script in addition to potential problems marshaling data or interacting with methods that change.

Creating the IronPython Script

The IronPython script used for this example is quite simple in approach. All that the example call really does is add two numbers together. You could perform the task with far less code, but the point of this class is to demonstrate access techniques, so it’s purposely simple. Listing 15-1 shows the external module code and the code used to test it. As previously mentioned, testing your IronPython script is essential if you want the application to work properly.

Listin g 15-1: A test IronPython class for use in the examples

[code]
# The class you want to access externally.
class DoCalculations():
# A method within the class that adds two numbers.
def DoAdd(self, First, Second):
# Provide a result.
return First + Second
# A test suite in IronPython.
def __test__():
# Create the object.
MyCalc = DoCalculations()
# Perform the test.
print MyCalc.DoAdd(5, 10)
# Pause after the test session.
raw_input(‘nPress any key to continue…’)
# Execute the test.
# Comment this call out when you finish testing the code.
__test__()
[/code]

The class used for this example is DoCalculations(). It contains a single method, DoAdd(), that returns the sum of two numbers, First and Second. Overall, the class is simple.

The TestClass.py file also contains a __test__() function. This function creates an instance of DoCalculations(), MyCalc. It then prints the result of calling the DoAdd() method with values of 5 and 10. The example waits until you press Enter to exit.

In __main__(), you see a call to __test__(). You can execute the example at the command line, as shown in Figure 15-1. Make sure you use the –D command line switch to place the interpreter in debug mode. You could also open IPY.EXE interactively, load the file, and execute it inside the interpreter. When you know that the code works properly, be sure to comment out the call to __test__() in __main__().

Test the external module before you use it with your application.
Figure 15-1: Test the external module before you use it with your application.

Accessing the Module from C#

Now that you have an external module to use, you’ll probably want to access from an application. This section considers the requirements for accessing IronPython from C#. Don’t worry; the section “Accessing the Module from Visual Basic.NET” later in this chapter discusses access from Visual Basic. NET as well. If you follow these steps, you’ll find that access is relatively straightforward, even if it does get a bit convoluted at times. Microsoft promises the future versions of C# will make dynamic language access even easier.

Adding the Required C# References

Any application you create requires access to the dynamic language assemblies. The IronPython assemblies appear in the Program FilesIronPython 2.6 folder on your machine. Right-click References and choose Add Reference from the content menu to display the Add Reference dialog box. Select the Browse tab. In most cases, you only need the three DLLs shown in Figure 15-2 to access any IronPython script. (You may also need to add the IronPython .Modules.DLL file to the list in some cases.)

Add the required references from your IronPython setup.
Figure 15-2: Add the required references from your IronPython setup.

Select the assemblies you require by Ctrlclicking them in the Add Reference dialog box. Click OK when you’re finished. You’ll see the assemblies added to the References folder in Solution Explorer.

Adding the Required References to the Host Language

You can perform a multitude of tasks with IronPython. In fact, later chapters in the book show how to perform tasks such as testing your static application code. IronPython really is quite flexible. However, most people will start by executing external scripts and only need a few of the namespaces in the IronPython assemblies to do it. The following using statements provide everything needed to execute and manage most IronPython scripts.

[code]
using System;
using IronPython.Hosting;
using IronPython.Runtime;
using Microsoft.Scripting.Hosting;
[/code]

Understanding the Use of ScriptEngine

You have many options for working with IronPython scripts. This first example takes an approach that works fine for Visual Studio 2008 developers, as well as those using Visual Studio 2010. It doesn’t require anything fancy and it works reliably for most scripts. Ease and flexibility concerns aside, this isn’t the shortest technique for working with IronPython scripts. This is the Method1 approach to working with IronPython scripts — the technique that nearly everyone can use and it appears in Listing 15-2.

Listin g 15-2: Using the script engine to access the script

[code]
static void Main(string[] args)
{
// Create an engine to access IronPython.
ScriptEngine Eng = Python.CreateEngine();
// Describe where to load the script.
ScriptSource Source = Eng.CreateScriptSourceFromFile(“TestClass.py”);
// Obtain the default scope for executing the script.
ScriptScope Scope = Eng.CreateScope();
// Create an object for performing tasks with the script.
ObjectOperations Ops = Eng.CreateOperations();
// Create the class object.
Source.Execute(Scope);
// Obtain the class object.
Object CalcClass = Scope.GetVariable(“DoCalculations”);
// Create an instance of the class.
Object CalcObj = Ops.Invoke(CalcClass);
// Get the method you want to use from the class instance.
Object AddMe = Ops.GetMember(CalcObj, “DoAdd”);
// Perform the add.
Int32 Result = (Int32)Ops.Invoke(AddMe, 5, 10);
// Display the result.
Console.WriteLine(“5 + 10 = {0}“, Result);
// Pause after running the test.
Console.WriteLine(“rnPress any key when ready…”);
Console.ReadKey();
}
[/code]

Now that you have access to Eng, you can use it to perform various tasks. For example, you must tell Eng what scope to use when executing code, so the example creates a ScriptScope object, Scope. In order to perform tasks, you must also have an ObjectOperations object, Ops. The example uses the defaults provided for each of these objects. However, in a production application, you might decide to change some properties to make the application execute faster or with better security.

At this point, you can execute the script. The act of executing the script using Source.Execute() loads the script into memory and compiles it in a form that the static application can use. The Source.Execute() method associates Scope with the execution environment. At this point, the parameters for executing the script are set in stone — you can’t change them.

The script is in memory, but you can’t access any of its features just yet. The script contains a DoCalculations class that you access by calling Scope.GetVariable() to create CalcObj. The code gains access to the class by creating an instance of it, CalcObj, using Ops.Invoke(). At this point, CalcObj contains an instance of DoCalculations() in the IronPython module, but you can’t use it directly. Remember that you must marshal data between C# and IronPython. In addition, C# has to have a way to deal with the potential changes in the IronPython script.

This seems like a lot of work just to gain access to DoAdd, but you can finally use AddMe to perform the addition. A call to Ops.Invoke() with AddMe and the arguments you want to use performs all of the required marshaling for you. You must coerce the output to an Int32 (something that C# understands). Finally, the application outputs the result, as shown in Figure 15-3.

FThe example application calls the DoAdd() method and displays the result onscreen.
Figure 15-3: The example application calls the DoAdd() method and displays the result onscreen.

Using the dynamic Keyword

One of the new ways in which you can access IronPython in C# 4.0 is to use the dynamic keyword. This keyword makes it possible for you to cut out a lot of the code shown in Listing 15-2 to perform tasks with IronPython. It’s still not perfect, but you’ll do a lot less work. Listing 15-3 shows a short example that accesses the __test__() function found in Listing 15-1.

Listin g 15-3: Accessing IronPython using the dynamic keyword

[code]
static void Main(string[] args)
{
// Obtain the runtime.
var IPY = Python.CreateRuntime();
// Create a dynamic object containing the script.
dynamic TestPy = IPY.UseFile(“TestClass.py”);
// Execute the __test__() method.
TestPy.__test__();
}
[/code]

The next step is to load the script. The dynamic type, TestPy, contains all the features of the TestClass.py script after you load it using IPY.UseFile(). Figure 15-4 shows how TestPy appears after the script loads. Notice that the Locals window correctly identifies all the IronPython types in the file.  (Visual Basic.NET developers will have to wait for an update).

In this case, the example calls the __test__() function. This function outputs the same information shown in Figure 15-1.

Loading the script provides access to all of the features it contains.
Figure 15-4: Loading the script provides access to all of the features it contains.

Working with the App.CONFIG File

In some cases, you might want to configure your application using an App.CONFIG file. Using the App.CONFIG file tends to ensure that your application works better between development machines. In addition, using the App.CONFIG file can make it easier to work with DLR using older versions of Visual Studio. Most important of all, using the App.CONFIG file ensures that anyone working with the application uses the correct version of the DLLs so that any DLL differences aren’t a problem.

Your project won’t contain an App.CONFIG file at the outset. To add this file, right-click the project entry in Solution Explorer and choose Add ➪ New Item from the context menu. You see the Add New Item dialog box shown in Figure 15-5. Highlight the Application Configuration File entry as shown and click Add. Visual Studio automatically opens the file for you.

The App.CONFIG file contains entries that describe the Microsoft scripting configuration. In most cases, you begin by defining a <section> element, which describes a <microsoft.scripting> element. The <microsoft.scripting> element contains a list of languages you want to use in a <languages> element, as shown in Listing 15-4.

Listin g 15-4: Defining the App.CONFIG file content

[code]
<?xml version=”1.0” encoding=”utf-8” ?>
<configuration>
<configSections>
<section name=”microsoft.scripting”
type=”Microsoft.Scripting.Hosting.Configuration.Section,
Microsoft.Scripting, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35”
requirePermission=”false” />
</configSections>
<microsoft.scripting>
<languages>
<language names=”IronPython,Python,py”
extensions=”.py”
displayName=”IronPython 2.0 Beta”
type=”IronPython.Runtime.PythonContext,IronPython,
Version=2.6.10920.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35” />
</languages>
</microsoft.scripting>
</configuration>
[/code]

Use an App.CONFIG file to hold DLR configuration information.
Figure 15-5: Use an App.CONFIG file to hold DLR configuration information.

The <section> element includes attributes for name, type, and requirePermission. The type attribute should appear on one line, even though it appears on multiple lines in the book. This attribute describes the Microsoft.Scripting.DLL attributes. Especially important is the Version and PublicKeyToken entries.

The <microsoft.scripting> element contains a <languages> element at a minimum. Within the <languages> element you find individual <language> elements that are descriptions of the languages you want to use in your application.

For this example, you create a <language> element for IronPython that starts with a names attribute. It’s important to define all the names you plan to use to access the language — the example defines three of them. The extensions attribute describes the file extensions associated with the language, which is .py in this case. The displayName attribute simply tells how to display the language. Finally, the type attribute contains a description of the IronPython.DLL file. As with the type element for Microsoft.Scripting.DLL. Again, you need to exercise special care with the Version and PublicKeyToken entries.

Now that you have the App.CONFIG file created, it’s time to look at the application code. Listing 15-5 contains the source for this example.

Listin g 15-5: Using the App.CONFIG file in an application

[code]
static void Main(string[] args)
{
// Read the configuration information from App.CONFIG.
ScriptRuntimeSetup srs = ScriptRuntimeSetup.ReadConfiguration();
// Create a ScriptRuntime object from the configuration
// information.
ScriptRuntime runtime = new ScriptRuntime(srs);
// Create an engine to access IronPython.
ScriptEngine Eng = runtime.GetEngine(“Python”);
// Describe where to load the script.
ScriptSource Source = Eng.CreateScriptSourceFromFile(“TestClass.py”);
// Obtain the default scope for executing the script.
ScriptScope Scope = Eng.CreateScope();
// Create an object for performing tasks with the script.
ObjectOperations Ops = Eng.CreateOperations();
// Create the class object.
Source.Execute(Scope);
// Obtain the class object.
Object CalcClass = Scope.GetVariable(“DoCalculations”);
// Create an instance of the class.
Object CalcObj = Ops.Invoke(CalcClass);
// Get the method you want to use from the class instance.
Object AddMe = Ops.GetMember(CalcObj, “DoAdd”);
// Perform the add.
Int32 Result = (Int32)Ops.Invoke(AddMe, 5, 10);
// Display the result.
Console.WriteLine(“5 + 10 = {0}“, Result);
// Pause after running the test.
Console.WriteLine(“rnPress any key when ready…”);
Console.ReadKey();
}
[/code]

The biggest difference between this example and the one shown in Listing 15-2 is that you don’t create the script engine immediately. Rather, the code begins by reading the configuration from the App.CONFIG file using ScriptRuntimeSetup.ReadConfiguration(). This information appears in srs and is used to create a ScriptRuntime object, runtime.

At this point, the code finally creates the ScriptEngine, Eng, as in the previous example. However, instead of using Python.CreateEngine(), this example relies on the runtime.GetEngine() method. For this example, the result is the same, except that you’ve had better control over how the ScriptEngine is created, which is the entire point of the example — exercising control over the IronPython environment. The rest of the example works the same as the example shown in Listing 15-2. The output is the same, as shown in Figure 15-3.

Accessing the Module from Visual Basic.NET

You might get the idea from the lack of Visual Basic.NET examples online that Microsoft has somehow forgotten Visual Basic.NET when it comes to DLR. Surprise! Just because the examples are nowhere to be seen (send me an e‑mail at [email protected] if you find a stash of Visual Basic.NET examples somewhere) doesn’t mean that you can’t work with IronPython from Visual Basic. In fact, the requirements for working with Visual Basic.NET are much the same as those for working with C#, as shown in the following sections.

Adding the Required Visual Basic.NET References

Visual Basic requires the same DLL references as C# does to work with IronPython. Figure 15-2 shows the assemblies you should add to your application to make it work properly. In this case, you right-click the project entry and choose Add Reference from the context menu to display an Add Reference dialog box similar to the one shown in Figure 15-2. Select the Browse tab and add the IronPython assemblies shown in Figure 15-2 by Ctrl-clicking on each of the assembly entries. Click OK. Visual Basic will add the references, but you won’t see them in Solution Explorer unless you click Show All Files at the top of the Solution Explorer window.

As with C#, you need to add some Imports statements to your code to access the various IronPython assemblies with ease. Most applications will require the following Imports statements at a minimum.

[code]
Imports System
Imports IronPython.Hosting
Imports IronPython.Runtime
Imports Microsoft.Scripting.Hosting
[/code]

Creating the Visual Basic.NET Code

As with all the other examples, you shouldn’t let the IronPython example dictate what you do in your own applications. You can obtain full access to any IronPython script from Visual Basic.NET and fully use every feature it provides.

Accessing IronPython scripts from Visual Basic.NET is much the same as accessing them from C# using the ScriptEngine object. Listing 15-6 shows the code you need to access the IronPython script used for all the examples

Listin g 15-6: Accessing IronPython from Visual Basic.NET

[code]
Sub Main()
‘ Create an engine to access IronPython.
Dim Eng As ScriptEngine = Python.CreateEngine()
‘ Describe where to load the script.
Dim Source As ScriptSource = Eng.CreateScriptSourceFromFile(“TestClass.py”)
‘ Obtain the default scope for executing the script.
Dim Scope As ScriptScope = Eng.CreateScope()
‘ Create an object for performing tasks with the script.
Dim Ops As ObjectOperations = Eng.CreateOperations()
‘ Create the class object.
Source.Execute(Scope)
‘ Obtain the class object.
Dim CalcClass As Object = Scope.GetVariable(“DoCalculations”)
‘ Create an instance of the class.
Dim CalcObj As Object = Ops.Invoke(CalcClass)
‘ Get the method you want to use from the class instance.
Dim AddMe As Object = Ops.GetMember(CalcObj, “DoAdd”)
‘ Perform the add.
Dim Result As Int32 = Ops.Invoke(AddMe, 5, 10)
‘ Display the result.
Console.WriteLine(“5 + 10 = {0}“, Result)
‘ Pause after running the test.
Console.WriteLine(vbCrLf + “Press any key when ready…”)
Console.ReadKey()
End Sub
[/code]

As you can see from the listing, Visual Basic.NET code uses precisely the same process as C# does to access IronPython scripts. In fact, you should compare this listing to the content of Listing 15-2. The two examples are similar so that you can compare them. The output is also precisely the same. You’ll see the output shown in Figure 15-3 when you execute this example.

Developing Test Procedures for External Modules

Many developers are beginning to realize the benefits of extensive application testing. There are entire product categories devoted to the testing process now because testing is so important. Most, if not all, developer tools now include some idea of application testing with them. In short, you should have all the testing tools you need to test the static portion of your IronPython application.

Unfortunately, the testing tools might not work particularly well with the dynamic portion of the application. Creating a test that goes from the static portion of the application to the dynamic portion of the application is hard. (Consequently, you need to include a test harness with your dynamic code and perform thorough testing of the dynamic code before you use it with the static application. (When you think about a test harness, think about a horse, your application that has a harness added externally for testing purposes. You add the harness for testing and remove it for production work without modifying the application.) Listing 15-1 shows an example of how you might perform this task.

The test harness you create has to test everything, which is a daunting task to say the least. In addition, you need to expend extra effort to make the test harness error free — nothing would be worse than to chase an error through your code, only to find out that the error is in the test harness. At a minimum, your test harness should perform the following checks on your dynamic code:

  • Outputs with good inputs
  • Outputs with erroneous inputs
  • Exception handling within methods
  • Property value handling
  • Exceptions that occur on public members that would normally be private

Of course, you want to check every method and property of every class within the dynamic code. To ensure you actually test everything, make sure you create a checklist to use to verify your test harness. Because IronPython isn’t compiled, you’ll find that you must manually perform some checks to ensure the code works precisely as planned, but use as much automation as possible.

Debugging the External Module

Debugging isn’t hard, but it also isn’t as straightforward as you might think when working with IronPython. The debugger won’t take you directly to an error. You can’t test variables using the debugger from within the static language. In short, you have to poke and prod the external script to discover what ails it. Fortunately, you do have three tools at your disposal for discovering errors.

  • Exceptions
  • print Statements
  • An ErrorListener object

Let’s begin with the easiest of the three tools. The static language application won’t ignore outright errors in the script code. For example, you might have the following error in the script:

[code]
# Introduce an error.
print 1/0
[/code]

If your code has this error (and it really shouldn’t), you’ll see an exception dialog box like the one shown in Figure 15-6. Unfortunately, when you click View Detail, the content of the View Detail dialog box is nearly useless. The exception information won’t tell you where to find the error in your script. In fact, it may very well lead you on a wild goose chase that ends in frustration.

The static language application displays exceptions for your script.
Figure 15-6: The static language application displays exceptions for your script.

The name of the exception will provide clues as to where the error might exist, but you can’t confirm your suspicions without help. The only tool, besides vigorous script testing, is to include print statements such as these in your code.

[code]
# Display the values of First and Second.
print ‘Values in IronPython Script’
print ‘First = ‘, First
print ‘Second = ‘, Second
[/code]

When you run the script, you see the output shown in Figure 15-7. Most developers view print statements as a bit old school, but they do work if you use them correctly. Make sure you provide enough information to know where the script is failing to perform as expected. Even so, using print statements may feel a bit like wandering around in the dark, so you should place an emphasis on testing the script before you use it and after each change you make.

Using print statements may seem old school, but they work.
Figure 15-7: Using print statements may seem old school, but they work.

In some cases, you might make a small change to a script and it stops running completely — you might not see a script exception, just an indicator that something’s wrong because the application raises an unrelated exception. Syntax errors and other problems where the interpreter simply fails can cause the developer a lot of woe. For example, your application might have the following syntax error:

[code]
# Create a syntax error.
while True print ‘This is an error!’
[/code]

This code obviously won’t run. Because of the nature of the error, you might even pass it by while looking through your code. The answer to this problem is to create an ErrorListener class like the one shown in Listing 15-7.

Listin g 15-7: Create an ErrorListener to hear script semantic errors

[code]
class MyListener : ErrorListener
{
public override void ErrorReported(ScriptSource source,
string message,
SourceSpan span,
int errorCode,
Severity severity)
{
Console.WriteLine(“Script Error {0}: {1}“, errorCode, message);
Console.WriteLine(“Source: {0}“, source.GetCodeLine(span.Start.Line));
Console.WriteLine(“Severity: {0}“, severity.ToString());
}
}
[/code]

The ErrorListener contains just one method, ErrorReported(). This method can contain anything you need to diagnose errors. The example provides an adequate amount of information for most needs. However, you might decide to provide additional information based on the kind of script you’re using.

In order to use this approach, you must compile the script before you execute it. The compilation process must include the ErrorListener, as shown here.

[code]
// Compile the script.
Source.Compile(new MyListener());
[/code]

When you run the application now, you get some useful information about the syntax error, as shown in Figure 15-8.

The ErrorListener provides useful output on syntax errors.
Figure 15-8: The ErrorListener provides useful output on syntax errors.

 

Interacting with the DLR

The Dynamic Language Runtime (DLR) is a new feature of the .NET platform. Its intended purpose is to support dynamic languages, such as Python (through IronPython) and Ruby (through IronRuby). Without DLR, the .NET Framework can’t really run dynamic languages. In addition, DLR provides interoperability between dynamic languages, the .NET Framework, and static languages such as C# and Visual Basic.NET. Without DLR, dynamic and static languages can’t communicate. In order to meet these goals, DLR must provide basic functionality that marshals data and code calls between the dynamic and static environments. This functionality comes in a number of forms that are discussed in this chapter. You might be surprised to find that you’ve already used many of these features throughout the book. Here’s the list of features that DLR supports in order to accomplish its goals.

  • Hosting Application Programming Interfaces (APIs): In order to run dynamic language scripts, the host language must have access to the scripting engine. The Hosting APIs provide the support needed to host the dynamic language within the host environment through the scripting engine. This marshaling of code and data makes it possible to seamlessly integrate static and dynamic languages.
  • Extensions to Language Integrated Query (LINQ) ExpressionTree: Normally, a language would need to convert data, objects, and code into Microsoft Intermediate Language (MSIL) before it could translate anything into another language. Because all .NET languages eventually end up as MSIL, MSIL is the common language for all higher-level .NET languages. These extensions make it possible for language compilers to create higher-level constructs for communication purposes, rather than always relying on MSIL. The result is that the marshaling process takes less time and the application runs faster.
  • DynamicSite: This feature provides a call-site cache that dynamic languages use in place of making constant calls to other .NET languages. Because the call-site cache is already in a form that the dynamic language can use, the overall speed of the dynamic language application improves.
  • IDynamicObject: An interface used to interact with dynamic objects directly. If you create a class that implements IDynamicObject, DLR lets the class perform the required conversions, rather than rely on the built-in functionality. Essentially, you create an object that can have methods, properties, and events added dynamically during run time. You use IDynamicObject when you want to implement custom behaviors in your class.
  • ActionBinder: The ActionBinder is a utility that helps support .NET interoperability. The ActionBinder is language specific. It ensures that conversions of variable data, return values, and arguments all follow language-specific behaviors so that the host language sees the data in a form it understands.

These are the main tasks that DLR performs. Of course, it also provides other compiler utilities that you need to know about. The final section in this chapter provides an overview of these other features.

DLR is a constantly changing technology today, so you’ll want to keep up with the additions and changes to DLR. One of the better places to find general DLR resources online is at http://blogs.msdn.com/ironpython/ archive/2008/03/16/dlr-resources.aspx. This chapter also provides a number of specific resources you can use to discover more about DLR. The point is to keep track of what’s going on with this exciting technology and review your code as things change.

Obtaining DLR

It’s important to remember that IronPython relies on DLR to perform just about every task that IronPython executes. Therefore, you already have access to a certain level of DLR, even if you don’t install anything or do anything special. In fact, you’re using DLR in the background every time you use IronPython. However, you’re using DLR without really knowing it exists and without understanding what DLR itself can do for your application. So while you can use the direct approach to DLR, it can prove frustrating and less than friendly.

In order to truly understand DLR, you at least need documentation. Better yet, you can download the entire DLR package and begin to understand the true impact of this product. If nothing else, spend some time viewing the available components at http://www.codeplex.com/dlr. The following sections describe various methods of gaining access to DLR so you can use it to perform some custom tasks.

Using the Direct Method

The direct method is the easiest way to obtain the benefits of DLR, but it’s also the most limited. You simply add a reference to the IronPython.DLL file located in the Program FilesIronPython 2.6 folder of your hard drive. This technique works fine for embedding IronPython scripts in your C# or Visual Basic.NET application. In fact, you gain access to the following classes:

  • IronPython
  • IronPython.Compiler
  • IronPython.Compiler.Ast
  • IronPython.Hosting
  • IronPython.Modules
  • IronPython.Runtime
  • IronPython.Runtime.Binding
  • IronPython.Runtime.Exceptions
  • IronPython.Runtime.Operations
  • IronPython.Runtime.Types

For many developers, this is all the DLR support you need, especially if your application only requires cross-language support through the Hosting APIs. (You’ll still want to download the documentation that’s available on the main DLR Web site — the section “Downloading the Documentation” later in this chapter explains how to perform this task.) The following steps describe how to add the required reference to gain access to these classes.

  1. Create the .NET project.
  2. Right-click References in Solution Explorer and choose Add Reference from the context menu. You see the Add Reference dialog box.
  3. Select the Browse tab and locate the IronPython.DLL file, as shown in Figure 14-1.
  4. Click OK. Visual Studio adds the required reference to your project.
Add the IronPython.DLL file to your project.
Figure 14-1: Add the IronPython.DLL file to your project.

You make use of IronPython.DLL as you would any other .NET assembly. Simply add the required use or Imports statement to your code. The examples throughout the book tell you about these requirements for the individual example.

Downloading the Full DLR

If you really want to experience DLR, you need the complete package. The full DLR consists of a number of components and even the source code weighs in at a hefty 10.5 MB.

Before you begin the download, check out the release notes at http://dlr.codeplex.com/ wikipage?title=0.92_release_notes for additional information about DLR. For example, you might decide to get an IronPython- or IronRuby-specific download. The full release includes both language products (which can be helpful, even if you use only one of them).

You obtain the full DLR from http://dlr.codeplex.com/Release/ProjectReleases .aspx?ReleaseId=34834. When you click the DLR-0.92-Src.zip link, you see a licensing dialog box. Click I Agree to begin the download process.

After the download completes, extract the resulting DLR-0.92-Src.ZIP file into its own folder. The resulting Codeplex-DLR-0.92 folder contains the following items.

  • License.HTML and License.RTF: You can read the same licensing information in two different formats. Use whichever form works best for you.
  • Docs: A folder containing the complete documentation for DLR. The best place to begin is the DLR-Overview.DOC file.
  • Samples: A folder containing a number of sample applications that demonstrate DLR features. There’s only one IronPython sample in the whole batch — you’ll find it in the Codeplex-DLR-0.92SamplesSilverlightAppPythonpython folder.
  • Source: A folder that contains the complete DLR source code that you need to compile in order to use DLR to create applications. This folder should be your first stop after you read the DLR-Overview.DOC file.

Building the Full DLR

Before you can use DLR, you must build it. The previous section explains how to download a copy of the DLR source. The following sections describe three methods you can use to build DLR. For most developers, the easiest and fastest method is the command line build. However, if you want to review the code before you use it, you might want to load the solution in Visual Studio and take a peek.

Performing a Command Line Build

The command line build option requires that you use the Visual Studio command line, not a standard command line (which doesn’t contain a path to the utilities you need). The following steps describe how to perform the command line build:

  1. Choose Start ➪ Programs ➪ Microsoft Visual Studio 2008 ➪ Visual Studio Tools ➪ Visual Studio 2008 Command Prompt or Start ➪ Programs ➪ Microsoft Visual Studio 2010 ➪ ➤ Visual Studio Tools ➪ Visual Studio Command Prompt (2010). You’ll see a command prompt.
  2. Type CD Codeplex-DLR-0.92Src and press Enter. This command places you in the DLR source code directory.
  3. Type MSBuild Codeplex-DLR.SLN (when using Visual Studio 2008) or MSBuild Codeplex-DLR-Dev10.SLN (when using Visual Studio 2010) and press Enter. By default, you get a debug build. Use the /p:Configuration=Release command line switch (as in MSBuild Codeplex-DLR.SLN /p:Configuration=Release or MSBuild Codeplex-DLR-Dev10.SLN /p:Configuration=Release) to obtain a release build. You see a lot of text appear onscreen as MSBuild creates the DLR DLLs for you. Some of the text will appear unreadable (Microsoft uses some odd color combinations). When the build process is complete, you should see 0 Error(s) as the output, along with a build time, as shown in Figure 14-2. (If you don’t see a 0 error output, you should probably download the files again because there is probably an error in the files you downloaded.)
The build process should show 0 Error(s) as the output message.
Figure 14-2: The build process should show 0 Error(s) as the output message.

Don’t look for the output in the source code folders. The output from the build process appears in the Codeplex-DLR-0.92Bin40 folder when working with Visual Studio 2010, no matter which technique you use to build DLR. Visual Studio 2008 developers will find their output in the Codeplex-DLR-0.92 BinDebug or Codeplex-DLR-0.92BinRelease folders, depending on the kind of build created. Visual Studio 2008 developers will also find a separate Codeplex-DLR-0.92BinSilverlight Debug or Codeplex-DLR-0.92Bin Silverlight Release folder for Silverlight use.

Performing a Visual Studio 2008 Build

Some developers will want to perform a build from within Visual Studio 2008. To perform this task, simply double-click the Codeplex-DLR.SLN icon in the Codeplex-DLR-0.92Src folder. Choose Build ➪ Build Solution or press Ctrl+Shift+B. You’ll see a series of messages in the Output window. When the process is complete, you should see, “Build: 23 succeeded or up-to-date, 0 failed, 1 skipped” as the output.

You must select each of the options in the Solution Configurations combo box in turn and perform a build to create a complete setup. Otherwise, you’ll end up with just the Release build or just the Debug build. If you need Silverlight or FxCop support, you must also create these builds individually.

Don’t worry if you see a number of messages stating

[code]
Project file contains ToolsVersion=”4.0”, which is not supported by this
version of MSBuild. Treating the project as if it had ToolsVersion=”3.5”.
[/code]

because this is normal when using Visual Studio 2008. You’ll also see a number of warning messages (a total of 59 for the current DLR build) in the Errors window, which you can ignore when using the current release.

Performing a Visual Studio 2010 Build

A release version of DLR will build better if you have a copy of Visual Studio 2010 on your system. To perform this task, simply double-click the Codeplex-DLR-Dev10.SLN icon in the Codeplex-DLR-0.92Src folder. Set the Solution Configurations option to Release or Debug as needed (there aren’t any options to build Silverlight or FxCop output). Choose Build ➪ Build Solution or press Ctrl+Shift+B. You’ll see a series of messages in the Output window. When the process is complete, you should see, “Build: 15 succeeded or up-to-date, 0 failed, 2 skipped” as the output. The Warnings tab of the Error List window should show 24 warnings.

Downloading the Documentation

The download you performed earlier provides code and documentation, but you might find that the documentation is outdated. As with everything else about DLR, the documentation is in a constant state of flux. If you want to use DLR directly, then you need the documentation found at http://dlr.codeplex.com/wikipage?title=Docs and specs&referringTitle=Home. Unfortunately, you have to download each document separately

Reporting Bugs and Other Issues

At some point, you’ll run into something that doesn’t work as expected. Of course, this problem even occurs with production code, but you’ll definitely run into problems when using the current release of DLR. In this case, check out the listing of issues at http://www.codeplex.com/dlr/WorkItem/List.aspx. If you don’t find an issue entry that matches the problem you’re experiencing, make sure you report the bug online so it gets fixed. Of course, reporting applies equally to code and documentation. Documentation errors are often harder to find and fix than coding errors — at least where developers are concerned — because it’s easier to see the coding error in many cases.

Working with Hosting APIs

In fact, you may have even wondered whether it’s possible to use IronPython as a scripting language for your next application. Fortunately, you can use IronPython as the scripting language for your next application by relying on the Hosting APIs. It turns out that a lot of people have considered IronPython an optimal language for the task. The following sections consider a number of Hosting API questions, such as how you can use it in an actual application, what the host application needs in order to use the Hosting APIs, and what you’d need to do to embed IronPython as a scripting language in an application.

Using the Hosting APIs

The DLR specification lists a number of hosting scenarios, such as operating on dynamic objects you create within C# or Visual Basic.NET applications. (See the section “Working with IDynamicObject” later in this chapter for details on dynamic objects in C# and Visual Basic.NET.) It’s also possible to use the Hosting APIs to create a scripting environment within Silverlight or other types of Web applications.

Whatever sort of host environment you create, you can use it to execute code snippets or entire applications found in files. The script run time can appear locally or within a remote application so you can use this functionality to create agent applications or scripting that relies on server support. The Hosting APIs make it possible to choose a specific scripting engine to execute the code or to let DLR choose the most appropriate scripting engine for the task. This second option might seem foolhardy, but it can actually let your code use the most recent scripting engine, even if that engine wasn’t available at the time you wrote the host environment code.

Chaos could result if you couldn’t control the extent (range) of the script execution in some way. For example, two developers could create variables with the same name in different areas of the application. The Hosting APIs make it possible to add scope to script execution. The scope acts much like a namespace does when writing code. Just as a namespace eliminates naming conflicts in assemblies, scoping eliminates them in the scripting environment. Executing the code within a scope also provides it with an execution context (controlled using a ScriptScope). Scopes are either public or private, with private scopes providing a measure of protection for the scripting environment. A script can also import scopes for use within the environment or require the host to support a certain scope to execute the script.

The Hosting APIs also provide support for other functionality. For example, you can employ reflection to obtain information about object members, obtain parameter information, and view documentation. You can also control how the scripting engine resolves file content when dynamic languages import code files.

Understanding the Hosting APIs Usage Levels

The DLR documentation specifies that most developers will use the Hosting APIs at one of three levels that are dictated by application requirements. Here are the three basic levels.

  • Basic code: The basic code level (Level 1 in the documentation) relies on a few basic types to execute code within scopes. The code can interact with variable bindings within those scopes.
  • Advanced code execution: The next level (Level 2 in the documentation) adds intermediate types that provide additional control over how code executes. In addition, this level supports using compiled code in various scopes and permits use of various code sources.
  • Support overrides: The final level (Level 3 in the documentation) provides methods to override how DLR resolves filenames. The application can also use custom source content readers, reflect over objects for design-time tool support, provide late bound variable values from the host, and use remote ScriptRuntime objects.

The concept of a ScriptRuntime object is central to working with the Hosting APIs. A host always begins a session by creating the ScriptRuntime object and then using that object to perform tasks. You can create a ScriptRuntime object using several methods. Of course, the easiest method is to use the standard constructor, which requires a ScriptRuntimeSetup object as input. It’s also possible to create a ScriptRuntime object using these methods

  • ScriptRuntime.CreateFromConfiguration(): A factory method that lets you use a preconfigured scope to create the ScriptRuntime object. In fact, this factor method is just short for new ScriptRuntime(ScriptRuntimeSetup.ReadConfiguration()).
  • ScriptRuntime.CreateRemote(): A factory method that helps you to create a ScriptRuntime object in another domain. The code must meet strict requirements to perform remote execution. See Section 4.1.3, “Create* Methods,” in the Hosting APIs specification for details.

At its name implies, a ScriptRuntimeSetup object gives a host full control over the ScriptRuntime object configuration. The ScriptRuntimeSetup object contains settings for debug mode, private execution, the host type, host arguments, and other setup features. Simply creating a ScriptRuntimeSetup object sets the defaults for executing a script. Once you use a ScriptRuntimeSetup object to create a ScriptRuntime object, you can’t change the settings — doing so will raise an exception.

The Hosting APIs actually support a number of objects that you use to create a scripting environment, load the code you want to execute, and control the execution process. The figure at http:// www.flickr.com/photos/john_lam/2220796647/ provides an overview of these objects and how you normally use them within the hosting session.

It’s important to isolate code during execution. The Hosting APIs provide three levels of isolation.

  • AppDomain: The highest isolation level, which affects the entire application. The AppDomain lets you execute code at different trust levels, and load and unload code as needed.
  • ScriptRuntime: Every AppDomain can have multiple ScriptRuntimes within it. Each ScriptRuntime object can have different name bindings, use different .NET assemblies, have different settings (one can be in debug mode, while another might not), and provide other settings and options support.
  • ScriptScope: Every ScriptRuntime can contain multiple ScriptScopes. A ScriptScope can provide variable binding isolation. In addition, you can use a ScriptScope to give executable code specific permissions.

Now that you have a better idea of how the pieces fit together, it’s important to consider which pieces you use to embed scripting support within an application. Generally, if you want basic code (Level 1) support, all you need are the objects shown in green at http://www.flickr.com/photos/ john_lam/2220796647/. In fact, if you want to use the default ScriptScope settings, all you really need to do is create the ScriptRuntime and then use the default ScriptScope.

Considering the Host Application

A host has to meet specific requirements before it can run IronPython as a scripting language. Chapter 15 discusses more of the details for C# and Visual Basic.NET developers. You’ll find that C# and Visual Basic.NET provide everything you need. However, it’s interesting to see just what the requirements are, especially if you’re using an older version of these languages. Section 3 of the DLR-Spec-Hosting.DOC file found in the Codeplex-DLR-0.92Docs folder contains complete information about the hosting requirements. Section 3.3 (and associated subsections) are especially important for most developers to read if they plan to use the Hosting APIs for anything special.

Embedding IronPython as a Scripting Language

Imagine that you’ve created a custom editor in your application where users can write IronPython scripts. They then save the script to disk (or you could read it from memory), and then they assign the script to a button or menu in your application. When the user selects the button or menu, your application executes the script. Creating this scenario isn’t as hard as you might imagine. DLR comes with most of the functionality you need built in.

Of course, you need a test script to start. Listing 14-1 shows the test script for this example. The example is purposely simple so that the example doesn’t become more focused on the IronPython code than the code that executes it. However, you could easily use any script you want as long as it’s a legitimate IronPython script.

Listin g 14-1: A simple IronPython script to execute

[code]
# A simple function call.
def mult(a, b):
return a * b
# Create a variable to hold the output.
Output = mult(5,10)
# Display the output.
print(‘5 * 10 =’),
print(Output)
# Pause after the debug session.
raw_input(‘nPress any key to continue…’)
[/code]

In this case, the example has a simple function, mult(), that multiplies two numbers together. The __main__() part of the script multiplies two numbers and displays the result using the print() function. In short, the script isn’t very complicated.

Now that you have a script, you need to create an application to execute it. The example is a simple console application. In order to create the IronPython ScriptRuntime object, you need access to some of the IronPython assemblies. Right-click References in Solution Explorer and choose Add Reference from the context menu. You see the Add Reference dialog box shown in Figure 14-3. Ctrl+click each of the entries shown in Figure 14-3, then click OK to add them to your project.

Add the required references from your IronPython setup.
Figure 14-3: Add the required references from your IronPython setup.

The example also requires that you add using statements for a number of the assemblies. Here are the using statements that you must add for this example.

[code]
using System;
using IronPython.Hosting;
using IronPython.Runtime;
using Microsoft.Scripting.Hosting;
[/code]

Now that the console project is set up, you can begin coding it. This example is very simple, but it actually works. You can execute an IronPython script using this technique. Of course, you can’t interact with it much. Chapter 15 provides more detailed examples, but this example is a good starting place. Listing 14-2 shows the minimum code you need to execute an IronPython script and display the result of executing it onscreen.

Listin g 14-2: Executing the IronPython script

[code]
static void Main(string[] args)
{
// Create an IronPython ScriptRuntime.
ScriptRuntime Runtime = IronPython.Hosting.Python.CreateRuntime();
// Execute the script file and return scope information about
// the task.
ScriptScope Scope = Runtime.ExecuteFile(“Test.py”);
// Display the name of the file executed.
Console.WriteLine(“rnExecuted {0}“,
Scope.GetVariable<string>(“__name__“));
// Keep the output visible.
Console.WriteLine(“rnPress any key…”);
Console.ReadLine();
}
[/code]

The code begins by creating the ScriptRuntime object, Runtime. Notice that you create this object by directly accessing the IronPython assemblies, rather than the DLR assemblies. There are many ways to accomplish this task, but using the technique shown is the simplest. The Runtime object contains default settings for everything. For example, this ScriptRuntime doesn’t provide debugging capability. Consequently, this technique is only useful when you have a debugged script to work with and may not do everything needed in a production environment where you let users execute their own scripts as part of an application.

The Runtime.ExecuteFile() method is just one of several ways to execute a script. You use it when a script appears in a file on disk, as is the case for this example. When you call the Runtime .ExecuteFile() method, your application actually calls on the IronPython interpreter to execute the code. The output from the script appears in Figure 14-4. As you can see, the code executes as you expect without any interference from the host. In fact, you can’t even tell that the application has a host.

The script output appears as you might expect.
Figure 14-4: The script output appears as you might expect.

When the Runtime.ExecuteFile() method call returns, the C# application that executed the script receives a ScriptScope object that it can use to interact with the application in various ways. This ScriptScope object, like the ScriptRuntime object, contains all the usual defaults. It’s a good idea to examine both Runtime and Scope in the debugger to see what these objects contain because you’ll find useful information in both.

The script is running in a host application. In fact, they share the same console window. To show how this works, the example writes output to the console window. It retrieves the __name__ property from Scope and displays it onscreen with the message, as shown in Figure 14-5. The point of this example is that the IronPython script truly is hosted and not running on its own. The technique shown here lets you perform simple interactions between C# or Visual Basic.NET and IronPython.

The output shows that the host and the IronPython environment share the same console.
Figure 14-5: The output shows that the host and the IronPython environment share the same console.

Understanding the Extensions to LINQ Expression Tree

Part of the premise behind DLR is that every .NET language eventually ends up in Microsoft Intermediate Language (MSIL) form. Whether you use C# or Visual Basic.NET, or even managed C++, the output from the compiler is MSIL. That’s how the various languages can get along. They rely on MSIL as an intermediary so that managed languages can work together.

The problem with compiling everything to MSIL is that MSIL doesn’t necessarily perform tasks quickly or easily when working with dynamic languages such as IronPython. It would be far easier if there were a mechanism for translating the code directly into something that C# or Visual Basic .NET could use. That’s where the LINQ Expression Tree (ET) comes into play. A LINQ ET can represent IronPython or other code (such as JavaScript) in a tree form that DLR can then translate into something that C# or Visual Basic.NET can understand. The result is a DLR tree that presents the code in an easily analyzable and mutable form. The example at http://blogs.msdn.com/hugunin/ archive/2007/05/15/dlr-trees-part-1.aspx explains how DRL trees work graphically. In this case, the author explains how a DLR tree can represent a JavaScript application — the same technique also applies to IronPython.

The LINQ ET originally appeared in the .NET Framework 3.5. In its original form, Microsoft used the LINQ ET to model LINQ expressions written in C# and Visual Basic.NET. In the .NET Framework 4.0, Microsoft added extensions for a number of reasons. For the purposes of this book, the most important reason to extend LINQ ETs is to accommodate the DLR semantics used to translate IronPython code into something that C# and Visual Basic.NET can understand.

DLR trees work in the background. It’s helpful to know they exist, but you generally won’t worry about them when working with IronPython so this section is short. However, let’s say you want to create a scripting language for your application that isn’t as complex as IronPython. Perhaps you want to implement an editor and everything that goes with it in your application. In this case, you may very well want to work with DLR trees. The examples found at http://weblogs.asp.net/ podwysocki/archive/2008/02/08/adventures-in-compilers-building-on-the-dlr.aspx show what you need to do to create your own language compiler. Once you have a compiler like this built, you could execute the code using a technique similar to the one shown in Listing 14-2.

It’s important to consider one word of warning, however, when working with the current version of DLR trees. As you scan through the specification, you’ll find that the authors have left behind copious notes about issues that aren’t resolved now or features that were left out of the current implementation due to a lack of time. The result is conversations such as the one at http://stackoverflow.com/ questions/250377/are-linq-expression-trees-turing-complete. If you look at section 2.4.1 of the specification, you find that a higher-level looping mechanism was indeed cut, but Microsoft is aware of the problem and plans to implement the feature in the future. In short, DLR trees have limits that you need to consider before implementing them in your application.

Considering DynamicSite

When working with a static language such as C# or Visual Basic.NET, the compiler knows what to emit in the form of MSIL based on the code the developer provides. However, dynamic code isn’t static — it can change based on any of a number of factors. One problem with dynamic languages is that DLR doesn’t always know what to emit during compile time because the real time event hasn’t occurred yet. Of course, the static language still needs some code in place because static languages need to know what to do at compile time. This seeming conundrum is handled by invoking a DynamicSite object. Using a DynamicSite object means that the static language knows what to call at compile time and DLR can fill the DynamicSite object with executable code during run time.

As with many parts of DLR, the action takes place behind the scenes — you don’t even know it occurs. However, it’s useful to know what happens so you at least know what to suspect when an error occurs. The act of invoking the DynamicSite method creates an operation to perform and a delegate. The delegate contains caching logic that is updated every time the arguments change. In short, as the dynamic language changes, DLR generates events that change the content of the cache as well.

At the center of working with DynamicSite is the UpdateBindingAndInvoke() method. The first time that application code calls the DynamicSite object, the UpdateBindingAndInvoke() method queries the arguments for the specified code. For example, the code might be something simple such as x + y, so the query would request the types of x and y. At this point, UpdateBindingAndInvoke() generates a delegate that contains the implementation of the code.

The next time the application invokes the DynamicSite object, the delegate checks the arguments in the call against those in the cache. If the argument types match, then the delegate simply uses the current implementation of the code. However, if the arguments are different, then the delegate calls UpdateBindingAndInvoke(), which creates a new delegate that contains a definition of the new code with the updated arguments. The new delegate contains checks for both sets of argument types and calls the appropriate implementation based on the arguments it receives. Of course, if none of the argument sets match the call, then the process starts over again with a call to UpdateBindingAndInvoke().

Working with IDynamicObject

This section discusses the IDynamicObject interface provided as part of DLR, which doesn’t affect IronPython directly, but could affect how you use other languages to interact with IronPython. You can easily skip this section and leave it for later reading if you plan to work exclusively with IronPython for the time being. This is a very short discussion of the topic that is meant to fill in the information you have about DLR and its use with IronPython.

As mentioned throughout the book, C# and Visual Basic.NET are both static languages. Microsoft doesn’t appear to have any desire to change this situation in upcoming versions of either language. Consequently, you can’t create dynamic types using C# or Visual Basic. There isn’t any technique for defining missing methods or dynamic classes using either language. However, you can consume dynamic types defined using a new interface, IDynamicObject.

The IDynamicObject interface tells DLR that the class knows how to dispatch operations on itself. In some respects, IDynamicInterface is a kind of managed form of the IQueryable interface that C++ developers use when creating COM objects. The concept isn’t new, but the implementation of it in the .NET environment is new.

There are many levels of complexity that you can build into your dynamic implementation. The example in this section is a very simple shell that you can build on when creating a fullfledged application. It’s designed to show a common implementation that you might use in an application. You can see another simple example at http://blogs.msdn.com/csharpfaq/ archive/2009/10/19/dynamic-in-c-4-0-creating-wrappers-with-dynamicobject.aspx.

The starting point for this example is a class that implements DynamicObject. In order to create such a class, you need to include the following using statements:

[code]
using System;
using System.Dynamic;
[/code]

The class is called ADynamicObject and appears in Listing 14-3.

Listin g 14-3: Creating a class to handle dynamic objects

[code]
// Any dynamic object you create must implement IDynamicObject.
public class ADynamicObject : DynamicObject
{
// Calls a method provided with the dynamic object.
public override bool TryInvokeMember(InvokeMemberBinder binder,
object[] args, out object result)
{
Console.WriteLine(“InvokeMember of method {0}.”, binder.Name);
if (args.Length > 0)
{
Console.WriteLine(“tMethod call has {0} arguments.”, args.Length);
for (int i = 0; i < args.Length; i++)
Console.WriteLine(“ttArgument {0} is {1}.”, i, args[i]);
}
result = binder.Name;
return true;
}
// Gets the property value.
public override bool TryGetMember(GetMemberBinder binder,
out object result)
{
Console.WriteLine(“GetMember of property {0}.”, binder.Name);
result = binder.Name;
return true;
}
// Sets the property value.
public override bool TrySetMember(SetMemberBinder binder, object value)
Console.WriteLine(“SetMember of property {0} to {1}.”,
binder.Name, value);
return true;
}
}
[/code]

In this case, the code provides the ability to call methods, get property values, and set property values. Amazingly, DLR automatically calls the correct method without any hints from you.

Notice that each of the methods uses a different binder class: InvokeMemberBinder, GetMemberBinder, or SetMemberBinder as needed. The binder provides you with information about the member of interest. In most cases, you use the member name to locate the member within the dynamic object. In this case, the code simply displays the member name onscreen so you can see that the code called the correct member.

Two of these methods, TryInvokeMember() and TryGetMember(), return something to the caller. It’s important to remember that the data is marshaled, so you must use the out keyword for the argument that returns a value or the application will complain later (the compiler may very well accept the error without comment). In both cases, the code simply returns the binder.Name value. If you were building this dynamic object class for an application, you’d use the binder.Name value to access the actual property or method.

When invoking a method, the TryInvokeMember() method receives an array of arguments to use with the method call. The code shows how you detect the presence of arguments and then displays them onscreen for this example. In an actual application, you’d need to compare the arguments provided by the caller against those required by the method to ensure the caller has supplied enough arguments of the right type.

All three methods return true. If the code were to return false instead, you’d see a RuntimeBinderException in the caller code. This exception tells the caller that the requested method or property doesn’t exist.

When a C# application desires to create a dynamic object, it simply creates an instance of the dynamic class. The instance can create properties, methods, or other constructs as needed. Listing 14-4 shows an example of how a test application might appear.

Listin g 14-4: Using the ADynamicObject class

[code]
class Test
{
static void Main()
{
// Create a new dynamic object.
dynamic DynObject = new ADynamicObject();
// Set a property to a specific value.
Console.WriteLine(“Setting a Property to a Value”);
DynObject.AProp = 5;
// Use one property to set another property.
// You would see a property get, followed by a property set.
Console.WriteLine(“rnSetting a Property to another Property”);
DynObject.Prop1 = DynObject.AProp;
// Call a method and set its output to a property.
// You would see a method call, followed by a property set.
Console.WriteLine(“rnSetting a Property to a Method Output”);
DynObject.Prop2 = DynObject.AMethod();
// Call a method with a property argument and set a new property.
// You would see a property get, a method call, and finally a
// property set.
Console.WriteLine(“rnSetting a Property to Method Output with Args”);
DynObject.Prop3 = DynObject.AMethod(DynObject.AProp);
// Wait to see the results.
Console.WriteLine(“rnPress any key when ready…”);
Console.ReadLine();
}
}
[/code]

Notice that the code begins by creating a new dynamic object using the dynamic keyword. At this point, you can begin adding properties and methods to the resulting DynObject. Properties can receive values directly, from other properties, or from methods. Methods can use arguments to change their output. Figure 14-6 shows the output from this example. The path that the code takes through the various objects helps you understand how dynamic objects work.

The DynamicObject class actually provides support for a number of members. You can use these members to provide a complete dynamic implementation for your application. Here’s a list of the DynamicObject members you can override.

  • GetDynamicMemberNames()
  • GetMetaObject()
  • TryBinaryOperation()
  • TryConvert()
  • TryDeleteIndex()
  • TryDeleteMember()
  • TryGetIndex()
  • TryGetMember()
  • TryInvoke()
  • TryInvokeMember()
  • TrySetIndex()
  • TrySetMember()
  • TryUnaryOperation()
The output shows the process used to work with dynamic objects.
Figure 14-6: The output shows the process used to work with dynamic objects.

The point of all this is that you can implement a kind of dynamic object strategy for static languages, but it’s cumbersome compared to IronPython. You might use this approach when you need to provide a dynamic strategy for something small within C# or Visual Basic. This technique is also useful for understanding how IronPython works, at a very basic level. IronPython is far more robust than the code shown in this example, but the theory is the same.

Understanding the ActionBinder

DLR makes it possible to invoke dynamic code from within a static environment using a DynamicSite object. The actual process for creating the method invocation call is to create an Abstract Syntax Tree (AST). The AST has functions assigned to it using an Assign() method. When DLR wants to assign a new function to AST, it supplies a function name and provides a calling syntax using the Call() method. The Call() method accepts four arguments.

  • An object used to hold the function. Normally, the code calls the Create() method of the host class using GetMethod(“Create”).
  • A constant containing the name of the function as it appears within the host object.
  • The array of arguments supplied to the function.
  • A delegate instance used to invoke the code later. It’s this argument that you consider when working with an ActionBinder.

At this point, you have an object that holds the parameters of the function call, as well as a delegate used to execute the function. The problem now is one of determining how to call the function. After all, the rest of your code knows nothing about the delegate if you create it during run time, as is the case when working with dynamic languages. If none of the code knows about the delegate, there must be some way to call it other than directly.

To make rules work, your code has to include a GetRule() method that returns a StandardRule object. Inside GetRule() is a switch that selects an action based on the kind of action that DLR requests, such as a call (DynamicActionKind.Call). When DLR makes this request, the code creates a StandardRule object that contains an ActionBinder. The ActionBinder determines what kind of action the call performs. For example, you might decide that the ActionBinder should be LanguageContext.Binder, which defines a language context for the function. The language context is a definition of the language’s properties, such as its name, identifier, version, and specialized features. (You can learn more about how a language context works at http://www.dotnetguru .org/us/dlrus/DLR2.htm.) The code then calls SetCallRule() with the StandardRule object, the ActionBinder, and a list of arguments for the function.

Now, here’s the important consideration for this section. The ActionBinder is actually part of the language design. If you wanted to create a new language, then part of the design process is to design an ActionBinder for it. The ActionBinder performs an immense amount of work. For example, a call to ActionBinder.ConvertExpression() provides conversion information about the data types that the language supports. Of course, IronPython already performs this task for you, but it’s important to know how things work under the hood in case you encounter problems.

Understanding the Other DLR Features

DLR is a moving target at the time of this writing. The latest release, 0.92, isn’t even considered production code as of yet. Consequently, you might find that the version of DLR that you use has features not described in this chapter because they weren’t available at the time of this writing.

An ExpandoObject is a dynamic property bag. Essentially, you fill it with data you want to move from one language to another. It works just like any other property bag you’ve used in the past. Because the ExpandoObject class implements IDynamicMetaObjectProvider, you can use it with dynamic languages such as IronPython. You use this object when moving data from C# or Visual Basic.NET to IronPython.

Developing ASP.NET Applications

Considering IronPyth on Web Application Compatibility

IronPython (and Python for that matter) have huge potential for making Web sites significantly easier to build and manage. In fact, there are a lot of options for using Python (and IronPython) to perform Web development tasks that you can see at http://wiki.python.org/ moin/WebProgramming and http://www.fredshack.com/docs/pythonweb.html. The problem with IronPython is that it can’t use the C/C++ libraries used by Python, which means that some of these solutions won’t work. For example, it’s unlikely that you could use IronPython in a Common Gateway Interface (CGI) application because CGI relies on modules written in C/ C++. Consequently, the first rule for Web development with IronPython is to make sure that IronPython can actually interact with the desired modules.

Unfortunately, IronPython isn’t integrated into Visual Studio. Because Visual Studio relies on some level of integration to provide full Web development support, you’ll almost certainly find that some projects are out of reach. It’s not that you can’t create them using IronPython; it’s that the process would be so horribly time consuming and error prone that using another language would be a better idea. You do have full .NET capability when working with IronPython, as demonstrated by the example in this chapter. All you really need is to add Dynamic Language Runtime (DLR) support to the picture and things will work out just fine.

The picture isn’t all gloom. IronPython actually proves to be a good development language for some tasks. As you progress through this chapter, you’ll find that IronPython actually makes code behind tasks exceptionally easy. It’s conceivable that you can build and test simple Web projects using IronPython considerably faster than using another language such as C# or Visual Basic.NET.

It’s important to remember that you don’t have to work with IronPython alone. Your Web application can include other technologies, such as Silverlight. You can also rely on other languages, such as C# or Visual Basic.NET, to fill in the gaps in IronPython coverage. Having another language at your disposal is all about flexibility, and IronPython is a great add-on language for any Web application project.

Obtaining ASP.NET Dynamic Language Support

Microsoft built ASP.NET with extensibility in mind, but the native support tends to focus more on static languages such as Visual Basic.NET and C#, rather than dynamic languages such as IronPython. The Dynamic Language Runtime (DLR) is an add-on for ASP.NET that makes it possible to use languages, such as IronPython, that make typing decisions at run time, rather than compile time. You must download and install this support before you can use IronPython to create a Web application. The following sections describe DLR in more detail.

DLR Limitations

The DLR is currently a work in progress. The overall feel is of an alpha product that shows promise, but still has more than a few warts. In addition, the product currently lacks these features (in order of their importance to you as a developer):

  • IntelliSense: Most developers depend on IntelliSense to provide clues as to what works and what doesn’t — in essence, what to write next. Without IntelliSense support, most developers find themselves peaking at the documentation and spending hours being frustrated with the development environment. Because a default relies so heavily on IntelliSense during the entire development process, its lack is keenly felt. Let’s hope that Microsoft will choose to add this feature sooner rather than later.
  • Limited designer support: If you worked through the examples in Chapter 8, you probably have a good idea of why designer support is so important. Sure, you can create a perfectly usable interface without a designer, but doing so becomes time consuming and many developers give up before they get their user interface completely right. The better the designer support, the faster you can work.
  • Project templates: The lack of templates isn’t anything new. You’ve created examples throughout this book without them, so not having them now isn’t that big a deal. However, it will be nice to have the convenience of project templates when Microsoft creates them.
  • ASP.NET Model-View-Controller (MVC) pattern: MVC is a development pattern that Microsoft is pushing very hard because it provides better control over the development process. You can learn more about ASP.NET MVC at http://www.asp.net/mvc/. Microsoft eventually plans to add MVC to DLR by extending MVC for IronRuby (see http:// github.com/jschementi/ironrubymvc for additional details).
  • Language Services Support: A new language feature that Microsoft plans to provide sometime in the future. Details about this feature are unavailable as of this writing, but it’s likely that Language Services Support will somehow make DLR more flexible and able to support a myriad of languages.

If you read the information at http://www.asp.net/DynamicLanguages/ carefully, you notice that it contains a wealth of caveats. The DLR is essentially an alpha version of a product that may not even appear as part of ASP .NET. Consequently, you need to use DLR to see what’s possible, rather than as a production tool for needs you have today. Anything you create using DLR today is likely to require updates and changes tomorrow (assuming you can use DLR at all). The lack of solid DLR commitment by Microsoft is one reason this chapter provides an overview of ASP.NET application development, rather than in-depth information.

Getting DLR

Before you can use DLR, you must download and install it. The files you need for the installation appear at http://aspnet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=17613. Download both the documentation and binaries files so that you have a complete setup. The documentation file is very small, so you might not think you even received the download at first.

The files are simple ZIP files, so you can extract them to your root directory. The examples in this chapter use a source folder of C:ironpython-2.6-beta1-aspnet-200905 for the DLR-specific examples. You need to change the path in the examples to match the location you used to extract the files on your machine.

The ironpython-2.6-beta1-aspnet-200905.ZIP file contains two folders: bin and examples. The bin folder contains a complete set of IronPython files, including the files required to make an IronPython script work as an ASP.NET application.

Using the Sample Applications

The ironpython-2.6-beta1-aspnet-200905examples folder contains two example applications. You should run at least one of these examples to ensure you have a good installation (simple as it is, sometimes there’s a configuration on your machine that prevents the examples from working as intended). To start as simply as possible, use the following steps to run the basic Hello Web forms example:

  1. Open a command prompt. It doesn’t have to be a VS2010 command prompt — any command prompt will do. If you’re using Vista or above, make sure you open the command prompt with administrator rights by right-clicking the Command Prompt icon in the Start menu and choosing Run As Administrator from the context menu.
  2. Type CD WINDOWSMicrosoft.NETFrameworkv2.0.50727 and press Enter to change directories to the .NET Framework 2.0 folder on your system. If you don’t have the .NET Framework 2.0 installed on your system, then type CD Program FilesCommon Files Microsoft SharedDevServer10.0 and press Enter to gain access to the ASP.NET Development Server folder on your system.
  3. Type WebDev.WebServer /port:85 /path:C:ironpython-2.6-beta1-aspnet-200905 exampleshello-webforms and press Enter (change your path information if you need to do so). You may need to type WebDev.WebServer20 if you’re using an alternate folder location. This action will start the ASP.NET Development Server on port 85. You’ll see an ASP.NET Development Server icon appear in the Notification Area.
  4. Right-click the ASP.NET Development Server icon and choose Open in Web Browser from the context menu. Your Web browser should open up and you should see a simple Web page like the one shown in Figure 11-1.

    This simple Web page relies on IronPython for support.
    Figure 11-1: This simple Web page relies on IronPython for support.
  5. Test the Web page to see if it works. Type your name in the Enter Your Name field and click Submit. You should see your name appear in place of the Your Name Here label shown in Figure 11-1. It’s a really simple example, but it will tell you whether you‘re getting the right results.
  6. Right-click the ASP.NET Development Server icon and choose Stop from the context menu. The server is no longer available.

The DLR package includes a second example. To use it, simply type WebDev.WebServer /port:85 / path:C:ironpython-2.6-beta1-aspnet-200905examplesalbum-handler and press Enter at the command prompt (again, make sure you use a path that matches your machine setup). This example is a little more complicated. When you initially display the browser, you see a list of filenames, which isn’t particularly helpful. Click the album-ipy.aspx entry and wait a few seconds. Eventually, you’ll see the Test icon shown in Figure 11-2.

The second example is a little more interesting
Figure 11-2: The second example is a little more interesting — at least it has graphics.

Click the Test icon and you’ll see more graphics. You can click the La Flore icon to see some flowers, or click one of the scenic images, as shown in Figure 11-3. Spend a bit of time with this application and you’ll find that it really is pretty interesting. Now, consider that this application is written in IronPython. Even though DLR isn’t a fully supported technology yet, it does have some amazing capabilities.

Creating a Web Site

You might think that creating an IronPython Web site is going to be complex at first — especially with a lack of IDE support. However, an IronPython Web site doesn’t really contain many parts that you have to worry about. A basic Web site starts with the root folder, which contains three items:

  • An Active Server Page Framework (.ASPX) file containing the user interface
  • The code behind file for the .ASPX file containing IronPython code
  • The Web.CONFIG file that contains all of the required configuration entries (such as the location of the special binaries used to interpret the IronPython script)

In addition to these files, you need a bin folder that contains the executables for working with IronPython. These files won’t vary between projects, so you may as well copy the bin folder whenever you create a new project. The following list is the files found in the bin folder.

  • IronPython.DLL
  • IronPython.Modules.DLL
  • Microsoft.Dynamic.DLL
  • Microsoft.Scripting.Core.DLL
  • Microsoft.Scripting.DLL
  • Microsoft.Scripting.ExtensionAttribute.DLL
  • Microsoft.Web.Scripting.DLL

It’s important to remember that IronPython is scripted. As a consequence, much of the compiled language baggage that you might have had to consider in the past isn’t an issue when working with IronPython. Don’t let the seeming simplicity of the Web site fool you, however; IronPython is just as capable as any other language.

Building a Basic ASP.NET Site Using IronPython

You’ve seen someone else’s examples for working with IronPython on a Web site. Now it’s time to create an IronPython Web site of your own. The example in this section isn’t meant to do anything too impressive. This section focuses on a process you can use to build ASP.NET applications of your own instead. The following sections describe every step you need to take to create any ASP.NET application using IronPython.

Creating the Project

Neither Visual Studio nor DLR provides a project template for you to use. In addition, you have more work to do when creating an ASP.NET application, so creating a project isn’t quite as easy as it should be. The DLR instructions suggest copying the hello-webforms folder to another location and using it as a starting point, which will work but will prove cumbersome. The following steps create a different kind of setup, one that will prove better in the long run, but require more work on your part now.

  1. Create a folder for your application. The example uses Calculator, but you can use any name you want.
  2. Copy the hello-webformsbin folder to your project folder.
  3. Create a .BAT file (the example uses Start.BAT) with the following content:
    [code]
    @Echo Off
    REM Change this directory to the location of WebDev.Webserver
    CD WINDOWSMicrosoft.NETFrameworkv2.0.50727
    REM Start the Web server.
    Start /B WebDev.WebServer /port:%1 /path:%2
    REM Open the Web browser to the right Web site.
    Start “C:Program FilesMozilla FirefoxFirefox” http://localhost:%1/
    @Echo On
    [/code]
    This batch file changes directories to the correct location for the WebDev.Webserver utility. It then starts the WebDev.Webserver utility with the port and path information you provide as part of the project arguments. Finally, it starts your browser so you can see the results.
  4. Start Visual Studio and choose File ➪ Open ➪ Project/Solution. Locate CMD.EXE found in the WindowsSystem32 folder and click Open. This step creates a project based on the command processor.
  5. Right-click cmd in Solution Explorer and choose Properties from the context menu. You see the General tab of the cmd Properties window, as shown in Figure 11-4.

    Set the properties for your project.
    Figure 11-4: Set the properties for your project.
  6. Type /C Start.BAT 85 “C:255 – Source CodeChapter11Calculator“ in the Arguments field. The arguments start with the /C command line switch, which tells the command processor to process all of the commands you’ve requested, and then terminate itself. The Start.BAT argument is the file you created in Step 3. This batch file requires two input arguments, the port number you want to use and the location of the example application.
  7. Type the location of your project, such as C:255 – Source CodeChapter11 Calculator, in the Working Directory folder.
  8. Right-click the Solution entry in Solution Explorer and choose Add ➪ New Item from the context menu. You’ll see the Add New Item dialog box shown in Figure 11-5.
  9. Highlight the HTML Page template and type Default.ASPX in the Name field. Click Add. Visual Studio adds a new ASPX file to your project.
  10. Right-click the Solution entry in Solution Explorer and choose Add ➪ New Item from the context menu to display the Add New Item dialog box again.
  11. Highlight the Text File template and type Default.ASPX.py in the Name field. Click Add. Visual Studio adds the code behind file for the Default.ASPX page.
  12. Copy the Web.CONFIG file from the hello-webforms folder to your project folder.
  13. Right-click the Solution entry in Solution Explorer and choose Add ➪ Existing Item from the context menu to display the Add Existing Item dialog box shown in Figure 11-6.
    Select items to add from the Add New Item dialog box.
    Figure 11-5: Select items to add from the Add New Item dialog box.

    Copy the Web.CONFIG file from an existing project.
    Figure 11-6: Copy the Web.CONFIG file from an existing project.
  14. Locate and highlight the Web.CONFIG file as shown in Figure 11-6. Click Add. Your project is now ready to go.

Defining the User Interface

The template used for this example is actually an .HTM file so it doesn’t contain a few essential entries; you need to use it as an .ASPX file. First, you must tell the Web service which language to use and where to find the code behind file. Add the following code as the first line in the .ASPX file.

[code]
<%@ Page Language=”IronPython” CodeFile=”Default.aspx.py” %>
[/code]

When you complete this step, close and then reopen the Default.ASPX file. Otherwise, the IDE is going to spend a lot of time complaining. The next step is to change where the code runs. You want it to run at the server so you change the <head> tag, as shown in the following code:

[code]
<head runat=”server”>
[/code]

Now you need to create content for the Web page. In this case, the user interface provides a simple four-function calculator. Listing 11-1 shows the code needed to perform this task.

Listin g 11-1: Defining a user interface for the example application

[code]
<form ID=”form1” runat=”server”>
<div>
<asp:Label ID=”lblInput” runat=”server”
text=”Type an input value:”/>
<asp:TextBox ID=”txtInput” runat=”server” />
<asp:Label ID=”lblError” runat=”server”
text=”Type a number in the input field!”
style=”color:red”
visible=”false” />
</div>
<div>
<asp:Button ID=”btnAdd” runat=”server” text=”+”
OnClick=”btnAdd_Click” />
<asp:Button ID=”btnSub” runat=”server” text=”-“
OnClick=”btnSub_Click” />
<asp:Button ID=”btnMul” runat=”server” text=”*“
OnClick=”btnMul_Click” />
<asp:Button ID=”btnDiv” runat=”server” text=”/“
OnClick=”btnDiv_Click” />
</div>
<div>
<asp:Label ID=”lblResult” runat=”server”
text=”Current Value:” />
<asp:TextBox ID=”txtResult” runat=”server” text=”0”
readonly=”true” />
</div>
<div>
<asp:Button ID=”btnClear” runat=”server” text=”Clear”
OnClick=”btnClear_Click” />
</div>
</form>
[/code]

As the listing shows, you work with IronPython code in the same way that you work with any .ASPX file. Unfortunately, the designer has not a clue as to what to do with your code, so you have to write it all by hand. Theoretically, you could start a Web project and then simply move the .ASPX file from that project to your IronPython project, but that seems like a lot of work unless your interface is relatively complex. If you find that you can’t quite remember all the ASP.NET controls at your disposal, you can find a complete list at http://www.w3schools.com/ASPNET/aspnet_refwebcontrols.asp in an easily accessible form.

In this case, the controls appear in four separate groups: input, control, result, and clearing. Figure 11-7 shows a typical view of the example form. Some common errors that developers make are not including the runat=”server” attribute and not providing the proper connectivity to events in the code behind, such as OnClick=”btnClear_Click”. Notice that you can use styles, just as you normally do, with the style attribute. One of the attributes that developers can forget about is visible=”false”, which makes the control invisible.

The example form is a simple four-function calculator.
Figure 11-7: The example form is a simple four-function calculator.

Creating the Code Behind

The code behind for this example is in pure IronPython. So, while you won’t see much difference in Default.ASPX, you’ll find that Default.ASPX.py looks completely different from any Web project you’ve worked with in the past. Listing 11-2 shows the code for this example.

Listin g 11-2: Creating the code behind for the example

[code]
# Import the required assemblies.
from System import *
# Respond to an Add button click.
def btnAdd_Click(sender, e):
# Get the current value.
Value = Int32.Parse(txtResult.Text)
# Reset the error message label.
lblError.Visible = False
# Obtain the new input value.
try:
Addition = Int32.Parse(txtInput.Text)
except:
# Display an error message when necessary.
lblError.Visible = True
return
# Perform the task and return the result.
Value = Value + Addition
txtResult.Text = str(Value)
# Respond to a Subtraction button click.
def btnSub_Click(sender, e):
# Get the current value.
Value = Int32.Parse(txtResult.Text)
# Reset the error message label.
lblError.Visible = False
# Obtain the new input value.
try:
Subtract = Int32.Parse(txtInput.Text)
except:
# Display an error message when necessary.
lblError.Visible = True
return
# Perform the task and return the result.
Value = Value – Subtract
txtResult.Text = str(Value)
# Respond to a Multiplication button click.
def btnMul_Click(sender, e):
# Get the current value.
Value = Int32.Parse(txtResult.Text)
# Reset the error message label.
lblError.Visible = False
# Obtain the new input value.
try:
Multiply = Int32.Parse(txtInput.Text)
except:
# Display an error message when necessary.
lblError.Visible = True
return
# Perform the task and return the result.
Value = Value * Multiply
txtResult.Text = str(Value)
# Respond to a Division button click.
def btnDiv_Click(sender, e):
# Get the current value.
Value = Int32.Parse(txtResult.Text)
# Reset the error message label.
lblError.Visible = False
# Obtain the new input value.
try:
Divide = Int32.Parse(txtInput.Text)
except:
# Display an error message when necessary.
lblError.Visible = True
return
# Perform the task and return the result.
Value = Value / Divide
txtResult.Text = str(Value)
# Respond to a Clear button click.
def btnClear_Click(sender, e):
txtResult.Text = ‘0’
[/code]

The code begins by importing the required assemblies. As with any IronPython application, you can use a combination of Python modules and .NET assemblies to create your application. You also have full access to both Python and .NET functionality in your application, so the considerable flexibility that IronPython provides is still available in this environment.

Each of the event handlers must provide both the sender and e arguments as shown. You don’t include a self argument in this case, as you would with other IronPython code. As you might expect, the sender argument contains a reference to the control that called the event handler, while e contains a list of event arguments (normally set to None).

The four math buttons begin by obtaining the current value of txtResult (the output TextBox) as an Int32 value. Because txtResult is read-only, you don’t need to worry about someone putting an incorrect value into it. Consequently, this task doesn’t provide any error trapping code.

The next step is to obtain the new value for the math operation from txtInput. In this case, you’re relying on the user to provide the correct input value, which means that the application code could receive anything. Someone might even try to enter a script in order to fool your application into doing something improper. Using the Int32.Parse() method means that any input other than a number triggers an exception, which your code can handle by simply not processing the input. The try…except structure does just that. If the user inputs an incorrect value, the Web page displays an error message, rather than doing anything with the input.

Now that the code has two inputs to process, it performs the required math operation. After the math operation is complete, the code outputs the result to txtResult.Text.

The btnClear_Click() event handler is relatively simple. All it does is place a 0 in txtResult .Text. The next math operation starts with a zero value, which means that txtResult is cleared.

Starting the Visual Studio Built-In Web Server

It’s time to begin testing your application. Many developers don’t realize it, but the .NET Framework includes a special utility that makes it possible to host Web sites without having a full-fledged Web server. The WebDev.WebServer utility originally appeared as part of the .NET Framework 2.0. When you build an application for testing purposes with Visual Studio, you’re using this built-in Web server to execute the code.

You find the WebDev.WebServer.EXE file in the WINDOWSMicrosoft.NETFrameworkv2.0.50727 folder on your system. Alternatively, you can also find versions of this utility in the Program Files Common FilesMicrosoft SharedDevServer10.0 folder as WebDev.WebServer20.EXE or WebDev.WebServer40.EXE.

The amazing part of the built-in Web server is that it works fine for any Web site using any kind of code. If you want to test your standard HTML pages, that’s fine — just point the built-in Web server to the correct directory on your hard drive. Of course, you can’t run some types of applications because the built-in Web server isn’t designed to handle them. For example, you can’t execute your PHP code. This little deficiency doesn’t matter for your IronPython application, however, because the built-in Web server will see it as a standard ASP.NET application.

Unlike your full-fledged Web server, the built-in Web server doesn’t provide outside access, which is the reason you want to use it to test your unsecured, experimental IronPython Webforms application. You don’t have to worry about prying eyes seeing your tests and possibly using them as a means to gain entrance to your machine. More important, because this server is virtual, it’s less likely that a failed experiment will cause your system to crash. The following sections describe the WebDev.WebServer utility in more detail.

Understanding the WebDev.WebServer Command Line Syntax

The WebDev.WebServer utility provides only a few command line switches because you perform most configuration tasks using a special Notification Area icon. Here’s the command line syntax for this utility.

[code]
WebDev.WebServer /port:<PortNumber> /path:<PhysicalPath> [/vpath:<VirtualPath>]
WebDev.WebServer20 /port:<PortNumber> /path:<PhysicalPath>
[/vpath:<VirtualPath>]
WebDev.WebServer40 /port:<PortNumber> /path:<PhysicalPath>
[/vpath:<VirtualPath>]
[/code]

The following list provides an explanation of each of the command line switches.

  • /port:PortNumber: Defines the port number used to host the application. Because your application isn’t accessible to the outside world and you use a local browser to access the Web server, the port you use isn’t as important as it usually is. You can’t select port 80 if you also have IIS installed on the system. Any port number between 1 and 65,535 will work as long as the port you select isn’t in use.
  • /path:PhysicalPath: Specifies the physical location of the application you want to host in the browser. You must provide the full path, but you don’t include a filename.
  • /vpath:VirtualPath: Provides a virtual path for the application where VirtualPath is normally the application name, such as /MyApp. The default setting provides a virtual path of /.
  • ?: Displays the help information for the WebDev.WebServer utility.

Using the Built-In Web Server with a Batch File

When you use a batch (.BAT) file to start the application, use the Start utility to execute WebDev .WebServer. Otherwise, the Web server won’t start properly. In addition, you should include the Start utility’s /B command line switch (it isn’t mandatory). The /B command line switch tells Windows not to open a new window to start the application. If Windows opens a new window, the Web server will start, but it may not display the Web page. Here’s a modified command line for batch files.

[code]
Start /B WebDev.WebServer /port:7171 /path:”F:My Web Site”
[/code]

Interacting with the Built-In Web Server

The WebDev.WebServer utility creates an icon in the Notification Area when you start it. In fact, a popup message alerts you to the presence of this icon. Right-click this icon and you see three options:

  • Open in Web Browser: Tells the utility to start the default Web browser and navigate to the Web page hosted by the Web server. In most cases, WebDev.WebServer uses the same defaults as any full Web server you have set up on your machine. Otherwise, you can count on these defaults working:
    • Default.HTM
    • Default.ASP
    • Default.ASPX
    • Index.HTM
  • Stop: Stops the server and makes any Web pages inaccessible.
  • Show Details: Displays the ASP.NET Development Server dialog box shown in Figure 11-8 where you can see details about the Web server. In addition, this dialog box provides a link to access the default Web page.
The ASP.NET Development Server dialog box provides details about the Web server.
Figure 11-8: The ASP.NET Development Server dialog box provides details about the Web server.

Considering the Built-In Web Server Limitations

It’s important to realize that we’re not using the WebDev.WebServer utility for production purposes. The following list helps you better understand why you can’t use this utility for every purpose.

  • Functionality: The WebDev.WebServer utility doesn’t create a full-fledged Web server. Some of the functionality you rely on, such as the ability to add users or work with virtual directories, simply isn’t available.
  • Security: Any security that you want to set up has to appear as part of your application in a Web.CONFIG file. The WebDev.WebServer utility does tend to follow whatever rules you set for Internet Information Server (IIS) if you have it installed. However, you can’t count on this behavior when working on your own and you certainly can’t count on it when you send the application to other machines.
  • Administrative tools support: Anything you normally configure through the Internet Information Services console located in the Administrative Tools folder of the Control Panel is unavailable when working with the WebDev.WebServer utility. Consequently, if your application relies on a special ISAPI Filter, you won’t be able to execute it using the WebDev.WebServer utility. The same holds true for anything else that you normally have to add using the Internet Information Services console.
  • Single user Web server: It’s important to remember that this is a single-user Web server. No one outside the local machine can access the Web server because theoretically, it doesn’t exist — it’s virtual. This means that you can’t perform some types of testing using the built-in Web server. The feature that makes it so secure also prevents you from performing some kinds of real-world testing. Multi-user applications simply won’t work with the built-in Web server.

Savvy developers can get around some of the WebDev.WebServer configuration limitations through judicious use of the Web.CONFIG file and by creating resources locally. Make sure you don’t assume that an application won’t work simply because you have to configure it in a new way.

Performing Basic Testing

Your IronPython Web application works just like any other Web application you create. Because IronPython is fully .NET capable, you can use any control set you want within the application. Of course, you also have access to standard Web controls. All of this flexibility increases complexity and makes it necessary to test your application fully.

The example application is relatively simple, so testing isn’t cumbersome. When you first start the application by pressing Ctrl+F5, you see the Web page shown in Figure 11-7. When you type a value into the Type an Input Value field and click one of the math buttons (+, -, *, or /), the application performs the desired task. Trying to input an invalid value triggers an error message like the one shown in Figure 11-9.

Incorrect values trigger an error message.
Figure 11-9: Incorrect values trigger an error message.

Interestingly enough, the scripting nature of IronPython makes it possible to use IronPython to test your Web application. This gives you an advantage over compiled languages such as C# and Visual Basic.NET.

Considering Debugging

You may be wondering whether this project can provide any debugging. The fact is that you don’t get direct debugging when working with DLR, even if you use a full Web server. However, there are four ways in which you can debug your application.

  • Use print statements and other old standbys to determine what your application is doing.
  • Attach the debugger to the running process after the fact by choosing Debug ➪ Attach to Process within Visual Studio.
  • Import the Microsoft.Scripting.Debugging.DLL found in the Program Files IronPython 2.6 folder and add debugging information manually.
  • Rely on the output error message from the Web server, such as the one shown in Figure 11-10.

    The Web server provides you with error messages as needed.
    Figure 11-10: The Web server provides you with error messages as needed.

Even though Webforms use isn’t quite ready for prime time, you should still take time to experiment. At the very least, try the example programs that come with DLR to see that IronPython is a viable Web development language. Use the example application in this chapter as a starting point for your own experimentation. In short, have a bit of fun using IronPython to create Web pages. Consider how the dynamic nature of IronPython could help you in future Web application development, because DLR won’t remain in alpha status forever.

Importing the .NET Framework Assemblies

Importing an assembly into IronPython isn’t much different from importing a Python module. In fact, you use about the same code. The primary difference is that you can’t import some .NET assemblies directly into IronPython, just as you can’t import them directly into any .NET language. Instead, you must first create a reference to the .NET assembly and then import it. For anyone who has worked with .NET languages in the past, nothing will have changed from the normal procedure they follow.

One odd thing about IronPython is that it’s case sensitive even when it comes to .NET Framework assemblies. As a consequence, importing system.math won’t work but importing System.Math will. Because many developers aren’t used to thinking about the case of .NET Framework assemblies, you might be caught off guard when an application fails for some unknown reason. One issue always to consider is whether you’ve capitalized the assembly name incorrectly.

Performing a Standard Import

As with Python modules, you can perform a standard import of a .NET assembly. For example, you might want to import the .NET Framework’s System assembly. In this case, you type

[code]

import System

[/code]

and press Enter. If you want to see what the System assembly contains, type

[code]

dir(System)

[/code]

and press Enter. Figure 7-1 shows typical results from importing the System assembly.

Now, let’s say that you want to create a UInt32 variable, just like a UInt32 that you’d create in any other .NET language. Simply type something like MyVar = System.UInt32(5). Of course, you can use any variable within the range that fits within a UInt32. If you don’t provide a value by typing MyVar = System.UInt32() the .NET Framework automatically assigns the variable a value of 0.

However, let’s take a look at MyVar. If you type MyVar by itself, you see that it’s an object that has a value of 5, as shown in Figure 7-2. Type dir(MyVar) and you see that MyVar contains many of the same methods as a standard Python integer. For example, you still have access to the absolute value function, __abs__(), and comparison method, __eq__(). In addition to these standard methods, you also have access to .NET-specific functions such as Parse() and ToChar().

Figure 7-1: Performing a standard import places the System assembly where you’d expect.
Figure 7-1: Performing a standard import places the System assembly where you’d expect.

When you import an assembly using the standard approach, some code can become long and cumbersome. For example, if you want to change the console foreground color, you must type the following:

[code]

System.Console.ForegroundColor = System.ConsoleColor.Blue

[/code]

Notice that you must use the correct enumeration when specifying the color, or the change won’t occur. This code really does work — give it a try and then print the current console foreground color, as shown in Figure 7-3 (the screenshot in this book shows only shades of gray, but you’ll see color on your display). The console color changes to whatever value you specify without creating a variable first because System.Console.ForegroundColor is a property.

Figure 7-2: Creating a .NET object provides a mix of Python and .NET methods.
Figure 7-2: Creating a .NET object provides a mix of Python and .NET methods.
Figure 7-3: Using the standard import can become a little cumbersome.
Figure 7-3: Using the standard import can become a little cumbersome.

As with Python modules, you can get around the problem by assigning a particular object to a variable. For example, if you type Console = System.Console, then you can shorten the code a little, as shown in Figure 7-3. The bottom line is that using a standard import with .NET isn’t much different from using it with Python modules. The only real difference is that you use a different name.

Importing an Assembly into the Global Namespace

Sometimes you need to have an entire assembly available at a global level. Using variables to bring part of the assembly up to the right level won’t work. In this case, you rely on a different import strategy than used in the section “Performing a Standard Import” earlier in this chapter. You’ve already seen this technique before as applied to Python modules, but now you’ll see how it applies to .NET assemblies. Simply use the from AssemblyName import Assembly | * format used for Python modules. For example, if you want to import the System assembly into the global namespace, you type

[code]

from System import *

[/code]

The asterisk (*) means that you import everything in the System assembly at the global namespace level. Figure 7-4 shows what happens when you use the dir() function to see the global namespace.

Figure 7-4: You can import an entire assembly into the global namespace.
Figure 7-4: You can import an entire assembly into the global namespace.

If you want a specific class within the System assembly imported at the global namespace level, you simply specify the name of the class as you would when working with Python. For example, if you want to work with the Console class, then you’d type

[code]

from System import Console

[/code]

Some developers will be tempted to import everything they need into the global namespace. While this strategy works fine for a .NET application created in a language such as C#, it doesn’t always work well in IronPython because of the way the Python language works. For example, Figure 7-4 shows an example of the problems that can occur. Imagine importing four or five assemblies into the global namespace and then using the dir() function to display a list of classes, methods, enumerations, or other .NET features you want to use. The list would be so large as to make any search pointless. Import only what you need into the global namespace.

You can extend individual imports by separating classes, enumerations, or other assembly members with commas. For example, if you want to import both the Console class and the ConsoleColor enumeration into the global namespace, you type

[code]

from System import Console, ConsoleColor

[/code]

If you use the dir() function to see the result, you see output similar to Figure 7-5. Importing only what you need keeps clutter down, makes your application run faster, and reduces potential security issues. In this case, you can reduce the foreground color-changing code shown in the “Performing a Standard Import” section to

[code]

Console.ForegroundColor = ConsoleColor.Blue

[/code]

Figure 7-5: Importing into the global namespace reduces the size and complexity of your code.
Figure 7-5: Importing into the global namespace reduces the size and complexity of your code.

Configuring the Console for .NET Help

Believe it or not, the help() function works fine with .NET assemblies. However, Microsoft designed the assembly help for a much larger display area. If you type help(ConsoleColor) and press Enter, the help output is so long that you can’t see even a small portion of it. In fact, you won’t actually see the help you need because it appears at the beginning of the help listing.

The console window has a buffer associated with it. When you type a command and the interpreter presents output, the buffer accepts all the output up to the size of the buffer. At that point, all the old information drops off the end into the bit bucket and you never see it again. The standard buffer size is 300 lines, which seems like it would be enough, but it isn’t nearly enough for the .NET help. What you really need for .NET help is about 3,000 lines. Use these steps to change the buffer size.

Figure 7-6: Modify the buffer to hold more lines of information.
Figure 7-6: Modify the buffer to hold more lines of information.
  1. Click the system menu in the upper-left corner of the console window and choose Properties from the context menu. You see the IronPython Console Properties dialog box.
  2. Select the Layout tab. You see the information shown in Figure 7-6.
  3. Change the Height property in the Screen Buffer Size area to 3000. This means that the screen buffer can now hold up the 3,000 lines of output. However, it also means that the screen buffer consumes ten times more memory, which means you won’t want to make this change to a console window unless you need the extra space.
  4. Click OK. You see the Apply Properties To Shortcut dialog box shown in Figure 7-7. If you plan to work with .NET very often, you’ll definitely want to choose “Modify Shortcut that Started this Window” so that you don’t have to make the change every time.
  5. Select one of the configuration change options and then click OK. Windows makes the change you requested.
Figure 7-7: Choose a configuration change option that matches your .NET usage habits.
Figure 7-7: Choose a configuration change option that matches your .NET usage habits.

At this point, you need to try out the help() function. Try typing

[code]

help(ConsoleColor)

[/code]

and press Enter. You’ll see that the display takes a second or so to return. At this point, you can scroll through the massive help display to find the information you need. Figure 7-8 shows typical output.

Creating a Reference to .NET Assemblies

Not every .NET assembly is available to IronPython by default, even if that assembly appears in the Global Assembly Cache (GAC). As with any other .NET language, you sometimes need to reference .NET assemblies in order to import and use them. For example, try typing

[code]

from System.Xml import *

[/code]

and press Enter. You get an error message as output stating the following:

[code]

from System.Xml import *
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
ImportError: No module named Xml

[/code]

Figure 7-8: The screen buffer is now large enough to hold the help information you need.
Figure 7-8: The screen buffer is now large enough to hold the help information you need.

Of course, you know that the System.Xml assembly does exist. This error message tells you that you have to add a reference to the System.Xml assembly before you can use it. In order to add a reference, you must import the clr (Common Language Runtime) module. You can then use one of the five following methods to import the assembly.

  • clr.AddReference(AssemblyObjectOrFilename): Adds a reference to the .NET assembly by passing an assembly object directly or by specifying the assembly filename. (You can provide either a partial or full filename.) This is a generic sort of assembly reference addition because you don’t have control over which assembly version IronPython loads. You can use this method when you’re experimenting and really don’t care about which version of the .NET assembly you get. This is also a good method to use when you’re not sure which version of the assembly the user has installed on his or her machine but do know that all versions of the .NET Framework include the functionality you require.
  • clr.AddReferenceToFile(AssemblyFilename[, AssemblyFilename…]): Adds a reference to the .NET assembly by passing a filename. You may supply multiple filenames to load multiple assemblies. IronPython looks for the assembly using the sys.path attribute. Consequently, you can partially control which version of the assembly you get by controlling the sys.path attribute. However, if more than one assembly has the correct filename, IronPython doesn’t guarantee which version of the assembly will load. You can use the clr.AddReferenceByName() method to better control which version of the assembly loads.
  • clr.AddReferenceToFileAndPath(AssemblyPathAndFilename[, AssemblyPathAndFilename …]): Performs about the same task as the clr .AddReferenceToFile() method. However, in this case, you must provide an absolute path to the assembly you want to load, which means that you have better control over which assembly version loads. This method automatically adds the assembly path to sys.path for you.
  • clr.AddReferenceByName(AssemblyName, Version=VersionNumber, Culture= CultureIdentifier|neutral, PublicKeyToken=TokenValue): Adds an assembly reference based on assembly specifics normally found in the GAC. You must supply values that fully define the assembly. For example, to import the .NET Framework 2.0 version of the System. Xml assembly, you would supply: ‘System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089‘.
  • clr.AddReferenceByPartialName(PartialAssemblyName): Adds a reference to the .NET assembly by passing a partial name that IronPython looks up in the GAC. This method doesn’t assure that you obtain any particular version of the assembly you need. You can use the clr.AddReferenceByName() method to better control which version of the assembly loads.

Now that you have a better idea of how to add a reference, let’s try importing the System.Xml assembly. The following steps help you get the assembly referenced and imported into IronPython.

  1. Type import sys and press Enter. This step makes the sys module accessible.
  2. Type sys.path.append(‘C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727‘) and press Enter. In order to add a reference to an assembly, you must provide its location as part of sys.path. You may need to change the drive and directory to match your system.
  3. Type import clr and press Enter. This step makes the clr module accessible, which has the various assembly reference methods described earlier.
  4. Type clr.AddReference(‘System.Xml.DLL‘) and press Enter. IronPython now has a reference to the assembly file it needs, but the assembly isn’t imported yet. If you receive an IO Error message, it means that IronPython couldn’t find the assembly you requested in the location provided as part of sys.path.
  5. Type import System.Xml and press Enter. The System.Xml assembly is now available for use. It’s time to test to verify that the assembly is available.
  6. Type dir(System.Xml) and press Enter. You should see the content of the System.Xml assembly, as shown in Figure 7-9.

This technique works with any .NET assembly, not just those found in the GAC. If you have a custom .NET assembly you want to use in your application, this technique lets you access it with ease. Make sure you use the right technique for the kind of assembly you want to import. If version number is important, then make sure you use the clr.AddReferenceByName() method.

Figure 7-9: Verify that you can access the System.Xml assembly.
Figure 7-9: Verify that you can access the System.Xml assembly.

If you import a module or assembly by mistake, you can unload it in the same way as you remove variables you no longer need, by typing del <NameOfModuleOrAssembly>. For example, if you want to get rid of the System.Xml assembly after using it, type del System and press Enter. The module or assembly you want to remove must appear in the dir() list. In this case, when you type dir() after importing System.Xml, you see System, not System.Xml in the dir() list, so you must del System, not del System.Xml. Never set an assembly or module reference to None (as you would for clearing a variable) because the reference will remain, but none of the content will exist, causing hard to find errors in your application.

 

Baby Name Eliminator (Local Databases & Embedded Resources)

Baby Name Eliminator provides the perfect technique for Type A personalities to name their babies. (It’s the technique my wife and I used to name our two sons!) Rather than trying to brainstorm names and worrying that you’re missing the perfect one, this app enables you to use the process of elimination to name your baby!

Baby Name Eliminator starts with a massive database of essentially every name ever used in the United States: 36,065 boy names and 60,438 girl names. After you choose a gender, the app enables you to quickly narrow down the list with a variety of filters. These filters are based on the popularity of each name, its starting/ending letter, and the year the name was first in use. Once you’ve finished filtering the list, you can eliminate names one-by-one until your decision is made.

When naming our sons, we went through several rounds, eliminating names that were obviously bad and leaving names that we had any hesitation about. Once we got down to about 20 names, my wife and I each picked our top 5 choices. With our first son, we only had one name in common, so our decision was made! If you and your spouse both have a Windows phone, independently eliminating names can be a fun way to come up with a final list of candidate names.

So where does this massive database of names come from? The Social Security Administration, which provides data about almost every first name used in a Social Security card application from 1880 to the present. There are a few caveats to this list:

  • For privacy reasons, only names used at least five times in any given year are included.
  • One-character names are excluded.
  • Many people born before 1937 never applied for a Social Security card, so data from these years is spotty.
  • Distinct spellings of the same name are treated as different names.
  • The data is raw and uncorrected. Sometimes the sex on an application is incorrect, causing some boy names to show up in the girl names list and vice versa. In addition, some names are recorded as “Unknown,” “Unnamed,” or “Baby.” Restricting your list to the top 1,000 or so names in any year generally gets rid of such artifacts.

To enable its filtering, this app makes use of two local databases—one for boy names and one for girl names.

Working with Local Databases

The lack of local database support in Windows Phone 7 is one of its more publicized shortcomings. Apps are encouraged to work with server-side databases instead, but this adds extra burden for developers and extra hassle for users (latency, a working data connection, and potential data charges). Fortunately, several third-party database options exist. My favorite is an open-source port of SQLite for Windows Phone 7 created by Dan Ciprian Ardelean. You can read about it at http://sviluppomobile.blogspot.com/ 2010/03/sqlite-for-wp-7-series-proof-of-concept.html and get the latest version (at the time of this writing) at http://www.neologics.eu/Dan/WP7_Sqlite_20.09.2010.zip. This includes C# source code and a Community.CsharpSqlite.WP.dll assembly that you can reference in your project. It’s certainly not bug-free, but it works quite well for a number of scenarios (such as the needs of this app).

SQLite for Windows Phone 7 reads from and writes to database files in isolated storage. If you want to ship a database with your app that’s already filled with data, you can include the database file in your project with a Build Action of Content. At run-time, your app can retrieve the file then save it to isolated storage before its first use of SQLite.

How can I create a .db file that contains the database I want to ship with my app?

I followed the somewhat-cumbersome approach of writing a Windows Phone app that

  1. Uses SQLite to generate the database, executing CREATE TABLE and INSERT commands
  2. Retrieves the raw bytes from the .db file saved by SQLite to isolated storage, using thenormal isolated storage APIs
  3. Copies the bytes from the Visual Studio debugger as a Base64-encoded string and saves them to the needed .db file with a separate (desktop) program that decodes the string

Listing 24.1 contains a DatabaseHelper class used by Baby Name Eliminator that handles all interaction with the two SQLite databases included in the app.

LISTING 24.1 DatabaseHelper.cs—A Class That Wraps SQLite

[code]

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.IO.IsolatedStorage;
using System.Windows;
using System.Windows.Resources;
using SQLiteClient;
namespace WindowsPhoneApp
{
public class DatabaseHelper
{
// The name of the file included as content in this project,
// also used as the isolated storage filename
public static string DatabaseName { get; set; }
// “Load” the database. If the file does not yet exist in isolated storage,
// copy it from the original file. If the file already exists,
// this is a no-op.
public static void LoadAsync(Action callback)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate(object sender, DoWorkEventArgs e)
{
if (!HasLoadedBefore)
{
StreamResourceInfo info = Application.GetResourceStream(
new Uri(DatabaseName, UriKind.Relative));
using (info.Stream)
SaveFile(DatabaseName, info.Stream);
}
if (callback != null)
callback();
};
worker.RunWorkerAsync();
}
// Retrieve a single value from the database
public static void ExecuteScalar(string command, Action<object> onSuccess,
Action<Exception> onError = null)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate(object sender, DoWorkEventArgs e)
{
try
{
object result = null;
using (SQLiteConnection db = new SQLiteConnection(DatabaseName))
{
db.Open();
SQLiteCommand c = db.CreateCommand(command);
result = c.ExecuteScalar();
}
if (onSuccess != null)
onSuccess(result);
}
catch (Exception ex)
{
if (onError != null)
onError(ex);
}
};
worker.RunWorkerAsync();
}
// Retrieve a collection of items from the database
public static void ExecuteQuery<T>(string command,
Action<IEnumerable<T>> onSuccess,
Action<Exception> onError = null) where T : new()
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate(object sender, DoWorkEventArgs e)
{
try
{
IEnumerable<T> result = null;
List<T> copy = new List<T>();
using (SQLiteConnection db = new SQLiteConnection(DatabaseName))
{
db.Open();
SQLiteCommand c = db.CreateCommand(command);
result = c.ExecuteQuery<T>();
// Copy the data, because enumeration only
// works while the connection is open
copy.AddRange(result);
}
if (onSuccess != null)
onSuccess(copy);
}
catch (Exception ex)
{
if (onError != null)
onError(ex);
}
};
worker.RunWorkerAsync();
}
public static bool HasLoadedBefore
{
get
{
using (IsolatedStorageFile userStore =
IsolatedStorageFile.GetUserStoreForApplication())
return userStore.FileExists(DatabaseName);
}
}
// Save a stream to isolated storage
static void SaveFile(string filename, Stream data)
{
using (IsolatedStorageFile userStore =
IsolatedStorageFile.GetUserStoreForApplication())
using (IsolatedStorageFileStream stream = userStore.CreateFile(filename))
{
// Get the bytes
byte[] bytes = new byte[data.Length];
data.Read(bytes, 0, bytes.Length);
// Write the bytes to the new stream
stream.Write(bytes, 0, bytes.Length);
}
}
}
}

[/code]

  • To enable a responsive user interface while expensive database operations are conducted, interaction with SQLite is done on a background thread with the help of BackgroundWorker, and success/failure is communicated via callbacks.
  • The command strings passed to ExecuteScalar and ExecuteQuery can be SQL commands like SELECT COUNT(*) FROM table.
  • ExecuteQuery is a generic method whose generic argument (T) must be a class with a property corresponding to each column selected in the query.

Application.GetResourceStream works with files included in your project with a Build Action of Content or with a Build Action of Resource. For the latter case, the passed-in URI must have the following syntax:

/dllName;component/pathAndFilename

Note that dllName can refer to any DLL inside the .xap file, as long as it contains the requested resource. It should not contain the .dll suffix.

For this app, the DatabaseName string would look as follows for the database of boy names (Boys.db) included in the root of the project as a resource rather than content:

/WindowsPhoneApp;component/Boys.db

However, if this were done, Listing 24.1’s use of SaveFile would have to change, because the DatabaseName string would no longer be a valid filename for isolated storage.

Application.GetResourceStream Versus Assembly.GetManifestResourceStream

You might stumble across the Assembly.GetManifestResourceStream API as a way to read files included with your app.This works, but only for files marked with a Build Action of Embedded Resource (not Resource).Using this in Listing 24.1 instead of Application.GetResourceStream would look as follows:

[code]

if (!HasLoadedBefore)
{
using (Stream stream = typeof(DatabaseHelper).
Assembly.GetManifestResourceStream(DatabaseName))
SaveFile(DatabaseName, stream);
}

[/code]

However, the string passed to GetManifestResourceStream has its own unique syntax: dllName.filename, where dllName is the name of the DLL containing the embedded resource. That’s because the C# compiler automatically prepends the DLL name (minus the .dll extension) to the filename when naming each embedded resource. (You can see these names by opening a DLL in a tool such as .NET Reflector.) For this app, the two valid strings would be “WindowsPhoneApp.Boys.db” and “WindowsPhoneApp.Girls.db”.

There’s no significant reason to use this approach rather than the more flexible Application. GetResourceStream. Using GetResourceStream with files included as content is generally preferable compared to either scheme with files embedded as resources, because resources increase the size of DLLs, and that can increase an app’s load time.

The Filter Page

Rather than examine this app’s main page, which you can view in the included source code, we’ll examine the filter page that makes use of the DatabaseHelper class. The filter page, shown in Figure 24.1, displays how many names are in your list then enables you to filter it further with several options that map to SQL queries performed on the database. (The choice of boy names versus girl names is done previously on the main page.)

FIGURE 24.1 The filter page supports five different types of filters.
FIGURE 24.1 The filter page supports five different types of filters.

Each button reveals a dialog or other display, shown in Figure 24.2, that enables the user to control each relevant filter. Tapping the count of names reveals the actual list of names, as shown in Figure 24.3. This list doesn’t enable interactive elimination, however, as that is handled on the main page.

FIGURE 24.2 The result of tapping each button on the filter page.
FIGURE 24.2 The result of tapping each button on the filter page.
FIGURE 24.3 Previewing the filtered list of names.
FIGURE 24.3 Previewing the filtered list of names.

Listing 24.2 contains the XAML for the filter page.

LISTING 24.2 FilterPage.xaml—The User Interface for Baby Name Eliminator’s Filter Page

[code]

<phone:PhoneApplicationPage x:Name=”Page”
x:Class=”WindowsPhoneApp.FilterPage”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
xmlns:phone=”clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone”
xmlns:local=”clr-namespace:WindowsPhoneApp”
FontFamily=”{StaticResource PhoneFontFamilyNormal}”
FontSize=”{StaticResource PhoneFontSizeNormal}”
Foreground=”{StaticResource PhoneForegroundBrush}”
SupportedOrientations=”PortraitOrLandscape”>
<Grid Background=”Transparent”>
<Grid.RowDefinitions>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”*”/>
</Grid.RowDefinitions>
<!– The standard header –>
<StackPanel Style=”{StaticResource PhoneTitlePanelStyle}”>
<TextBlock Text=”BABY NAME ELIMINATOR”
Style=”{StaticResource PhoneTextTitle0Style}”/>
<TextBlock Text=”apply filters”
Style=”{StaticResource PhoneTextTitle1Style}”/>
</StackPanel>
<ScrollViewer Grid.Row=”1”>
<Grid Margin=”12,0”>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<!– The current number of names –>
<StackPanel Background=”Transparent” local:Tilt.IsEnabled=”True”
MouseLeftButtonUp=”Preview_Click”>
<TextBlock Text=”Current # of names (tap to preview):”
HorizontalAlignment=”Center”
Style=”{StaticResource LabelStyle}”/>
<TextBlock x:Name=”NumberTextBlock” Text=”0” Margin=”0,-16,0,0”
HorizontalAlignment=”Center”
FontSize=”{StaticResource PhoneFontSizeExtraExtraLarge}”/>
</StackPanel>
<!– Progress indicator while a query is running –>
<Grid x:Name=”ProgressPanel”>
<Rectangle Fill=”{StaticResource PhoneBackgroundBrush}” Opacity=”.9”/>
<ProgressBar x:Name=”ProgressBar” VerticalAlignment=”Top”/>
<TextBlock x:Name=”ProgressText” TextWrapping=”Wrap”
HorizontalAlignment=”Center”
VerticalAlignment=”Top” Margin=”0,60,0,0” Text=”Loading”/>
</Grid>
<!– The five filter buttons –>
<ToggleButton x:Name=”RankMaxButton” Grid.Row=”1”
Content=”eliminate low-ranked names”
local:Tilt.IsEnabled=”True” Click=”RankMaxButton_Click”/>
<ToggleButton x:Name=”NameStartButton” Grid.Row=”2”
Content=”eliminate names starting with…”
local:Tilt.IsEnabled=”True” Click=”NameStartButton_Click”/>
<ToggleButton x:Name=”NameEndButton” Grid.Row=”3”
Content=”eliminate names ending with…”
local:Tilt.IsEnabled=”True” Click=”NameEndButton_Click”/>
<ToggleButton x:Name=”YearMaxButton” Grid.Row=”4”
Content=”eliminate modern names”
local:Tilt.IsEnabled=”True” Click=”YearMaxButton_Click”/>
<ToggleButton x:Name=”YearMinButton” Grid.Row=”5”
Content=”eliminate old-fashioned names”
local:Tilt.IsEnabled=”True” Click=”YearMinButton_Click”/>
<!– A user control that displays the letter grid in a popup –>
<local:LetterPicker x:Name=”LetterPicker”
Page=”{Binding ElementName=Page}”
Closed=”LetterPicker_Closed”/>
</Grid>
</ScrollViewer>
<!– Eliminate low-ranked names dialog –>
<local:Dialog x:Name=”RankMaxDialog” Grid.RowSpan=”2” Closed=”Dialog_Closed”>
<local:Dialog.InnerContent>
<StackPanel>
<TextBlock Text=”…” TextWrapping=”Wrap” Margin=”11,5,0,-5”/>
<TextBox MaxLength=”5” InputScope=”Number”
Text=”{Binding Result, Mode=TwoWay}”/>
<TextBlock Text=”Enter a number, or leave blank to clear this filter.”
TextWrapping=”Wrap” Margin=”11,-10,0,-10”
Foreground=”{StaticResource PhoneSubtleBrush}”/>
</StackPanel>
</local:Dialog.InnerContent>
</local:Dialog>
<!– Eliminate modern names dialog –>
<local:Dialog x:Name=”YearMaxDialog” Grid.RowSpan=”2” Closed=”Dialog_Closed”>
<local:Dialog.InnerContent>
<StackPanel>
<TextBlock TextWrapping=”Wrap” Margin=”11,5,0,-5”>

</TextBlock>
<TextBox MaxLength=”4” InputScope=”Number”
Text=”{Binding Result, Mode=TwoWay}”/>
<TextBlock Text=”…” TextWrapping=”Wrap” Margin=”11,-10,0,-10”
Foreground=”{StaticResource PhoneSubtleBrush}”/>
</StackPanel>
</local:Dialog.InnerContent>
</local:Dialog>
<!– Eliminate old-fashioned names dialog –>
<local:Dialog x:Name=”YearMinDialog” Grid.RowSpan=”2” Closed=”Dialog_Closed”>
<local:Dialog.InnerContent>
<StackPanel>
<TextBlock TextWrapping=”Wrap” Margin=”11,5,0,-5”>

</TextBlock>
<TextBox MaxLength=”4” InputScope=”Number”
Text=”{Binding Result, Mode=TwoWay}”/>
<TextBlock Text=”…” TextWrapping=”Wrap” Margin=”11,-10,0,-10”
Foreground=”{StaticResource PhoneSubtleBrush}”/>
</StackPanel>
</local:Dialog.InnerContent>
</local:Dialog>
<!– The list of names shown when tapping the current number –>
<Grid x:Name=”PreviewPane” Grid.RowSpan=”2” Visibility=”Collapsed”>
<Grid.RowDefinitions>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”*”/>
</Grid.RowDefinitions>
<Rectangle Grid.RowSpan=”2” Fill=”{StaticResource PhoneChromeBrush}”
Opacity=”.9”/>
<StackPanel Style=”{StaticResource PhoneTitlePanelStyle}”>
<TextBlock x:Name=”PreviewHeader”
Style=”{StaticResource PhoneTextTitle0Style}”/>
</StackPanel>
<ListBox Grid.Row=”1” x:Name=”PreviewListBox” Margin=”24,0,0,0”/>
</Grid>
</Grid>
</phone:PhoneApplicationPage>

[/code]

  • The five filter buttons are toggle buttons whose IsChecked state is managed by code-behind. If a filter is active, its corresponding button is checked (highlighted) so the user can see this without tapping every button and double-checking its filter settings.
  • The progress bar and related user interface, shown while a query is executing on a background thread, is shown in Figure 24.4. Because it does not occupy the whole screen, it enables the user to continue working if he or she doesn’t care to wait for the current count of names.
FIGURE 24.4 Showing progress while a database query executes on a background thread.
FIGURE 24.4 Showing progress while a database query executes on a background thread.

Listing 24.3 contains the code-behind for the filter page.

LISTING 24.3 FilterPage.xaml.cs—The Code-Behind for Baby Name Eliminator’s Filter Page

[code]

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
namespace WindowsPhoneApp
{
public partial class FilterPage : PhoneApplicationPage
{
public FilterPage()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
RefreshCount();
RefreshButtons();
}
protected override void OnBackKeyPress(CancelEventArgs e)
{
base.OnBackKeyPress(e);
// If a dialog, letter picker, or preview pane is open,
// close it instead of leaving the page
if (this.RankMaxDialog.Visibility == Visibility.Visible)
{
e.Cancel = true;
this.RankMaxDialog.Hide(MessageBoxResult.Cancel);
}
else if (this.YearMaxDialog.Visibility == Visibility.Visible)
{
e.Cancel = true;
this.YearMaxDialog.Hide(MessageBoxResult.Cancel);
}
else if (this.YearMinDialog.Visibility == Visibility.Visible)
{
e.Cancel = true;
this.YearMinDialog.Hide(MessageBoxResult.Cancel);
}
else if (this.PreviewPane.Visibility == Visibility.Visible)
{
e.Cancel = true;
this.PreviewPane.Visibility = Visibility.Collapsed;
}
}
void RefreshCount()
{
// Choose one of the included databases: boy names or girl names
if (Settings.IsBoy.Value.Value)
DatabaseHelper.DatabaseName = “Boys.db”;
else
DatabaseHelper.DatabaseName = “Girls.db”;
if (!DatabaseHelper.HasLoadedBefore)
{
ShowProgress(“Preparing database for the first time…”);
this.RankMaxButton.IsEnabled = false;
this.NameStartButton.IsEnabled = false;
this.NameEndButton.IsEnabled = false;
this.YearMaxButton.IsEnabled = false;
this.YearMinButton.IsEnabled = false;
}
DatabaseHelper.LoadAsync(delegate()
{
// The callback is called on a background thread, so transition back
// to the main thread for manipulating UI
this.Dispatcher.BeginInvoke(delegate()
{
ShowProgress(“Counting names…”);
this.RankMaxButton.IsEnabled = true;
this.NameStartButton.IsEnabled = true;
this.NameEndButton.IsEnabled = true;
this.YearMaxButton.IsEnabled = true;
this.YearMinButton.IsEnabled = true;
// Execute a query
DatabaseHelper.ExecuteScalar(“SELECT COUNT(*) FROM Names “ +
Settings.BuildQuerySuffix(), delegate(object result)
{
// The callback is called on a background thread, so transition back
// to the main thread for manipulating UI
this.Dispatcher.BeginInvoke(delegate()
{
HideProgress();
this.NumberTextBlock.Text = ((int)result).ToString(“N0”);
});
});
});
});
}
void RefreshButtons()
{
// Check (highlight) any button whose filter is active
this.RankMaxButton.IsChecked =
Settings.RankMax.Value != Settings.RankMax.DefaultValue;
this.NameStartButton.IsChecked =
Settings.ExcludedStartingLetters.Value.Count > 0;
this.NameEndButton.IsChecked =
Settings.ExcludedEndingLetters.Value.Count > 0;
this.YearMaxButton.IsChecked =
Settings.YearMax.Value != Settings.YearMax.DefaultValue;
this.YearMinButton.IsChecked =
Settings.YearMin.Value != Settings.YearMin.DefaultValue;
}
void Preview_Click(object sender, MouseButtonEventArgs e)
{
this.PreviewHeader.Text = “LOADING…”;
this.PreviewListBox.ItemsSource = null;
this.PreviewPane.Visibility = Visibility.Visible;
// Choose one of the included databases: boy names or girl names
if (Settings.IsBoy.Value.Value)
DatabaseHelper.DatabaseName = “Boys.db”;
else
DatabaseHelper.DatabaseName = “Girls.db”;
DatabaseHelper.LoadAsync(delegate()
{
// It’s okay to execute this on the background thread
DatabaseHelper.ExecuteQuery<Record>(“SELECT Name FROM Names “ +
Settings.BuildQuerySuffix(), delegate(IEnumerable<Record> result)
{
// Transition back to the main thread for manipulating UI
this.Dispatcher.BeginInvoke(delegate()
{
this.PreviewHeader.Text = “PRESS BACK WHEN DONE”;
this.PreviewListBox.ItemsSource = result;
});
});
});
}
void ShowProgress(string message)
{
this.ProgressText.Text = message;
this.ProgressBar.IsIndeterminate = true;
this.ProgressPanel.Visibility = Visibility.Visible;
}
void HideProgress()
{
this.ProgressPanel.Visibility = Visibility.Collapsed;
this.ProgressBar.IsIndeterminate = false; // Avoid a perf problem
}
// A click handler for each of the five filter buttons
void RankMaxButton_Click(object sender, RoutedEventArgs e)
{
if (Settings.RankMax.Value != null)
RankMaxDialog.Result = Settings.RankMax.Value.Value;
RankMaxDialog.Show();
}
void NameStartButton_Click(object sender, RoutedEventArgs e)
{
this.LetterPicker.SetBinding(LetterPicker.ExcludedLettersProperty,
new Binding { Path = new PropertyPath(“Value”),
Source = Settings.ExcludedStartingLetters,
Mode = BindingMode.TwoWay });
this.LetterPicker.ShowPopup();
}
void NameEndButton_Click(object sender, RoutedEventArgs e)
{
this.LetterPicker.SetBinding(LetterPicker.ExcludedLettersProperty,
new Binding { Path = new PropertyPath(“Value”),
Source = Settings.ExcludedEndingLetters,
Mode = BindingMode.TwoWay });
this.LetterPicker.ShowPopup();
}
void YearMaxButton_Click(object sender, RoutedEventArgs e)
{
if (Settings.YearMax.Value != null)
YearMaxDialog.Result = Settings.YearMax.Value.Value;
YearMaxDialog.Show();
}
void YearMinButton_Click(object sender, RoutedEventArgs e)
{
if (Settings.YearMin.Value != null)
YearMinDialog.Result = Settings.YearMin.Value.Value;
YearMinDialog.Show();
}
// Two handlers for the dialog or letter picker being closed
void LetterPicker_Closed(object sender, EventArgs e)
{
RefreshCount();
RefreshButtons();
}
void Dialog_Closed(object sender, MessageBoxResultEventArgs e)
{
if (e.Result == MessageBoxResult.OK)
{
// Update or clear a setting, depending on which dialog was just closed
int result;
if (sender == RankMaxDialog)
{
if (RankMaxDialog.Result != null &&
int.TryParse(RankMaxDialog.Result.ToString(), out result))
Settings.RankMax.Value = result;
else
Settings.RankMax.Value = null;
}
if (sender == YearMaxDialog)
{
if (YearMaxDialog.Result != null &&
int.TryParse(YearMaxDialog.Result.ToString(), out result))
Settings.YearMax.Value = (short)result;
else
Settings.YearMax.Value = null;
}
if (sender == YearMinDialog)
{
if (YearMinDialog.Result != null &&
int.TryParse(YearMinDialog.Result.ToString(), out result))
Settings.YearMin.Value = (short)result;
else
Settings.YearMin.Value = null;
}
// Only bother refreshing the count if the dialog result is OK
RefreshCount();
}
// Refresh buttons when the dialog is closed for any reason,
// to undo automatic check-when-tapped
RefreshButtons();
}
}
}

[/code]

  • This project includes two databases (Boys.db and Girls.db) that have an identical schema. They contain a single table called Names with three columns: Name, BestRank (its best single-year ranking), and FirstYear (the first year the name appeared in Social Security data).
  • The query to refresh the count of names is “SELECT COUNT(*) FROM Names” with a WHERE clause based on settings whose values are determined by the filters. The settings and the BuildQuerySuffix method are defined in Listing 24.4.
  • The query to display the list of actual names is “SELECT Name FROM Names” with the same WHERE clause. The Record class used with ExecuteQuery is therefore a class with a single string Name property:

    [code]
    public class Record
    {
    public string Name { get; set; }
    public override string ToString()
    {
    return this.Name;
    }
    }
    [/code]
    The ToString method enables the collection of Records to be used as the data source for the preview list box without any item template, as the default ToStringin- a-text-block rendering is sufficient.

  • Just like the date picker in the preceding chapter, this app leverages two-way data binding with each letter picker.

LISTING 24.4 Settings.cs—The Settings Class for Baby Name Eliminator

[code]

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using Microsoft.Phone.Controls;
namespace WindowsPhoneApp
{
public static class Settings
{
// Step 1: Gender
public static readonly Setting<bool?> IsBoy =
new Setting<bool?>(“IsBoy”, null);
// Step 2: Filters
public static readonly Setting<int?> RankMax =
new Setting<int?>(“RankMax”, null);
public static readonly Setting<List<char>> ExcludedStartingLetters =
new Setting<List<char>>(“IncludedStartingLetters”, new List<char>());
public static readonly Setting<List<char>> ExcludedEndingLetters =
new Setting<List<char>>(“ExcludedEndingLetters”, new List<char>());
public static readonly Setting<short?> YearMax =
new Setting<short?>(“YearMax”, null);
public static readonly Setting<short?> YearMin =
new Setting<short?>(“YearMin”, null);
// Step 3: Elimination
public static readonly Setting<ObservableCollection<string>> FilteredList =
new Setting<ObservableCollection<string>>(“FilteredList”, null);
public static readonly Setting<double> ScrollPosition =
new Setting<double>(“ScrollPosition”, 0);
// Orientation lock for the main page
public static readonly Setting<SupportedPageOrientation>
SupportedOrientations = new Setting<SupportedPageOrientation>(
“SupportedOrientations”, SupportedPageOrientation.PortraitOrLandscape);
// Build up a WHERE clause if any filters have been chosen
public static string BuildQuerySuffix()
{
List<string> conditions = new List<string>();
if (Settings.RankMax.Value != null)
conditions.Add(“ BestRank <= “ + Settings.RankMax.Value.Value);
foreach (char c in Settings.ExcludedStartingLetters.Value)
conditions.Add(“ NOT Name LIKE ‘“ + c + “%’”);
foreach (char c in Settings.ExcludedEndingLetters.Value)
conditions.Add(“ NOT Name LIKE ‘%” + c + “‘“);
if (Settings.YearMax.Value != null)
conditions.Add(“ FirstYear <= “ + Settings.YearMax.Value.Value);
if (Settings.YearMin.Value != null)
conditions.Add(“ FirstYear >= “ + Settings.YearMin.Value.Value);
if (conditions.Count == 0)
return “”;
else
{
StringBuilder whereClause = new StringBuilder(“WHERE “);
whereClause.Append(conditions[0]);
for (int i = 1; i < conditions.Count; i++)
whereClause.Append(“ AND “ + conditions[i]);
return whereClause.ToString();
}
}
}
}

[/code]

The Finished Product

Baby Name Eliminator (Local Databases & Embedded Resources)

 

Deconstructing a “Windows Phone Application” Visual Studio Project

When you create a new “Windows Phone Application” project in Visual Studio, you get a complete app that you can instantly compile into a .xap file and deploy to the emulator or a physical phone. The app doesn’t actually do anything other than display some text on the screen, but it sets up a lot of infrastructure that would be difficult and tedious to
create from scratch. Before creating the Tally app, let’s understand the main pieces of any new “Windows Phone Application” project:

  • The application manifest
  • Images
  • XAML code: MainPage.xaml and App.xaml
  • C# code: MainPage.xaml.cs, App.xaml.cs, and AssemblyInfo.cs

Visual Studio provides a few types of Windows Phone projects for more complex applications, based on the control that populates the main screen: a databound (list) application, a panorama application, and a pivot application. Almost all of the applications in this book were created from the basic “Windows Phone Application” project, as it’s relatively easy to manually add a databound list, a panorama control, or a pivot control to a project without having to start with a specialized project type.

The Application Manifest

The file called WMAppManifest.xml (where WM oddly stands for the outdated “Windows Mobile” term) is an application manifest. It describes your app to the operating system—its name, what it looks like, how it starts, what it’s allowed to do, and more. Listing 1.1 shows what Visual Studio generates inside this file when you create a new project and name it “Tally.” You can find this file in your project’s “Properties” folder.

.xap Files
.xap files, introduced by Silverlight but also used by XNA apps for Windows Phone, are just .zip files. If you rename a .xap file and give it a .zip extension, you can inspect its contents just like any .zip file.A .xap file for a Windows Phone app contains several files: compiled DLL(s), manifests, images, and potentially other assets used by your app that aren’t embedded into a DLL, such as videos or data files.

LISTING 1.1 WMAppManifest.xml—The Initial Application Manifest for the Tally Project

[code]

<?xml version=”1.0” encoding=”utf-8”?>
<Deployment xmlns=”http://schemas.microsoft.com/windowsphone/2009/deployment”
AppPlatformVersion=”7.0”>
<App xmlns=”” ProductID=”{2f711986-cfb4-40d3-9b7d-64aa37faf338}” Title=”Tally”
RuntimeType=”Silverlight” Version=”1.0.0.0” Genre=”apps.normal”
Author=”Tally author” Description=”Sample description” Publisher=”Tally”>
<IconPath IsRelative=”true” IsResource=”false”>ApplicationIcon.png</IconPath>
<Capabilities>
<Capability Name=”ID_CAP_GAMERSERVICES”/>
<Capability Name=”ID_CAP_IDENTITY_DEVICE”/>
<Capability Name=”ID_CAP_IDENTITY_USER”/>
<Capability Name=”ID_CAP_LOCATION”/>
<Capability Name=”ID_CAP_MEDIALIB”/>
<Capability Name=”ID_CAP_MICROPHONE”/>
<Capability Name=”ID_CAP_NETWORKING”/>
<Capability Name=”ID_CAP_PHONEDIALER”/>
<Capability Name=”ID_CAP_PUSH_NOTIFICATION”/>
<Capability Name=”ID_CAP_SENSORS”/>
<Capability Name=”ID_CAP_WEBBROWSERCOMPONENT”/>
</Capabilities>
<Tasks>
<DefaultTask Name =”_default” NavigationPage=”MainPage.xaml”/>
</Tasks>
<Tokens>
<PrimaryToken TokenID=”TallyToken” TaskName=”_default”>
<TemplateType5>
<BackgroundImageURI IsRelative=”true” IsResource=”false”>
Background.png
</BackgroundImageURI>
<Count>0</Count>
<Title>Tally</Title>
</TemplateType5>
</PrimaryToken>
</Tokens>
</App>
</Deployment>

[/code]

The application manifest is a strange file, because most of it gets overwritten by the Windows Phone Marketplace certification process. Therefore, the application manifest inside your app that can be downloaded from the marketplace will be different than the manifest inside your private copy of your app that you manually deploy.

The App element contains a ProductID Globally Unique Identifier (GUID) that uniquely identifies your app, and a RuntimeType value that indicates this is a Silverlight app rather than an XNA app. The value for Title is displayed with your installed app (either in the normal app list or the Games hub). The other four attributes are only applicable for listing your app in the marketplace, but these values (as well as Title) get overwritten by the data you enter on the marketplace website (the App Hub).

The Genre value affects where your app gets installed on the phone. If you use apps.normal, it gets placed in the normal app list. If you instead use apps.games, it gets placed inside the Games hub. (Yes, Silverlight apps can do this; the Games hub is not limited to apps created with XNA.) You must choose one of the two locations; your app cannot be installed in both. Leaving this as apps.normal is much more convenient at development-time, because the emulator does not expose
the Games hub. When submitting an app to the marketplace, this value also gets overwritten by the category you choose on the website.

The text overlaid on a tile is defined by the Title element inside the PrimaryToken element.This means that you can use something different than your app name. Although it is best to use your app name to avoid user confusion, shortening it is a good idea when your app name is too long for the tile.

You can leave the title element empty to produce a text-free tile (as done by the Facebook app), although the marketplace might reject such a submission unless you provide justification.The marketplace wants to ensure that users are not confused about which tile belongs to which app.

The IconPath element points to your icon image file, the Tasks element points to the main Silverlight page where your app begins running, and the Tokens element contains information about your tile (seen by users who pin
your app to their start screen). These parts are rarely changed, but these values are preserved when your app is published in the marketplace.

The Other Manifest
Visual Studio projects contain a second manifest in the “Properties” folder called AppManifest.xml.This is needed by Silverlight infrastructure, but you do not need to touch this file.

Capabilities

The most interesting part of WMAppManifest.xml is the list of capabilities inside the Capabilities element. These are special permissions for actions that users might not want certain apps to perform, whether for privacy concerns or concerns about data usage charges. The Visual Studio-generated manifest requests all available capabilities. You can
restrict this list to test what happens when your app tries to perform an action for which it does not have permission, but that’s a moot point. With one exception described later, the marketplace certification process automatically detects what capabilities your app needs and overwrites your list in the application manifest with the minimal set of required capabilities.

Once your app is running, you do not need to check if you’ve been granted any of your requested  capabilities. (There’s not even an API to do so!) If your app is running, then all requested capabilities have
been granted.They cannot be revoked.

In the marketplace, users are told what capabilities your app will be granted before they decide whether to download it. Each capability has a user-friendly name, so ID_CAP_LOCATION in Listing 1.1 is called “location services” in the marketplace, and ID_CAP_NETWORKING is called “data connection.” The user approval is an implicit part of the action of downloading your app. The location services capability, however, requires explicit consent by the user. The marketplace prompts users to agree to the sending of location data before they download the app.

ID_CAP_NETWORKING is the one capability you must manually request!
There’s one huge exception to the idea that you can let the marketplace certification process worry about the capabilities list for you. Although it can figure out everything else, marketplace certification cannot reliably figure out whether your app needs the phone’s networking capability. If ID_CAP_NETWORKING is present in your manifest, it will be granted even if you don’t need it, and if it is not present, it might not be granted even if you do need it!

The key point is that there’s no need for your app to obtain permission from the user for any capability, nor do you have
to worry about whether your app has been granted certain capabilities. Just remember:

  • If your app is running, it has been granted all the capabilities listed in its manifest.
  • If your app has been downloaded from the marketplace, its manifest automatically lists all the capabilities it needs and no more (except for ID_CAP_NETWORKING, as described in the warning sidebar).

You want to restrict the set of capabilities requested by your app, because it is a competitive advantage. For example, users might decide not to buy your Tip Calculator app if it wants permission to use the phone’s data connection! Therefore, be sure to remove the ID_CAP_NETWORKING capability if you don’t need it.Otherwise, your marketplace listing will say that your app “requires access to your data connection.”

Although ID_CAP_NETWORKING is currently the only capability to be careful about, the best practice is to use the Windows Phone Capability Detection Tool that ships with the Windows Phone Developer Tools starting with the October 2010 release.This runs the same automatic capability detection done by the marketplace certification process and then tells you what to put in your manifest. Before submitting your app to the marketplace, you should replace your requested capabilities with this minimal set (and, if appropriate, ignore the ID_CAP_NETWORKING capability that is usually falsely reported by the tool).

 

Why can I no longer debug my app on a physical phone after updating its capabilities?
That pesky ID_CAP_NETWORKING capability is to blame. Without ID_CAP_NETWORKING, the debugger is unable to communicate with the attached phone. So keep it there during development, but be sure to remember to remove this capability before submitting your app to the marketplace if your app does not require it!

 

How can I write a game that uses Xbox LIVE features?
Some capabilities are for specific developers such as mobile operators and phone manufacturers; not for mere mortals like you and me. ID_CAP_GAMERSERVICES is one such capability that does not work for everyone. It grants access to Xbox LIVE APIs, but only to games approved by Microsoft.You can peruse the Xbox LIVE functionality by looking at the Microsoft.Xna. Framework.GamerServices assembly with Visual Studio’s Object Browser, if you want to know what you’re missing.Most of the functionality inside throws a NotSupportedException unless you are a registered Xbox LIVE developer and have gone through a specific process to enable your game for Xbox LIVE.

If you believe you’ve developed a game worthy of the ID_CAP_GAMERSERVICES capability (so you can integrate with Xbox LIVE achievements, leaderboards, and more), you can email [email protected] for more information. Just keep in mind that the standards are very high! Look at the current set of Xbox LIVE games in the marketplace to get an idea of the kind of games that have been approved.

Of course, anybody can write a great game for Windows Phone without the ID_CAP_GAMERSERVICES capability, and they can do so in XNA or Silverlight.Volume II of this book series shows plenty of examples of Silverlight games.You’ll even see how to take advantage of Xbox LIVE avatar images without needing any kind of special access or arrangement with Microsoft.

 

Images

The project generated by Visual Studio includes three images, shown in Figure 1.1:

  • ApplicationIcon.png—The main icon, used wherever the app is installed. For normal apps (placed in the phone’s app list), the icon should be 62×62 pixels to avoid scaling. For games (placed in the Games hub), the icon should instead be 173×173 pixels.
  • Background.png—The tile icon (173×173) used when the user pins the application to the phone’s start screen, whether the app came from the app list or the Games hub. This poorly named file is named as such because it’s technically the background for the tile. The Title in the application manifest is automatically overlaid on the
    tile’s bottom-left corner, so care must be taken in the image to leave room for the text.
  • SplashScreenImage.jpg—The splash screen (480×800) shown while the application is loading.
The three standard images included in a Visual Studio “Windows Phone Application” project.
FIGURE 1.1 The three standard images included in a Visual Studio “Windows Phone Application” project.

You can change the name and location of the first two images, and they can be either JPEG or PNG files. Just remember to update your application manifest accordingly.

To create an icon that fits in with the Windows Phone built-in apps, it should usually have a transparent background and the drawing inside should:

  • be completely white
  • be composed of simple geometric shapes
  • reuse iconography already used by the phone if possible
  • use an understandable real-world metaphor

The drawing for the 62×62 icon should generally have a 12-pixel margin around all sides. (In other words, the actual content should fit in a 38×38 box centered in the image.) The drawing for the 173×173 icon should generally fit in a 73×73 almost-centered box. It should be nudged 3 pixels higher than center, giving a 47-pixel margin on top, 53-pixel margin on bottom, and 50-pixel margin on the sides.

For drawings significantly longer in one dimension, you may want to leave less of a margin. In  most cases, the drawing inside Background.png should be the same as the one inApplicationIcon.png, just larger. As with all user interface guidelines, games are generally exempt from these guidelines.

Creating these types of images requires some practice and patience.You’ll want to use tools such as PAINT.NET, mentioned in this book’s “Introduction” section. A few of the characters from the Wingdings and Webdings fonts can even be used to help create a decent icon!

These are not strict guidelines or even official guidelines from Microsoft, nor does it match what the initial image files contain; it just tends to look right for most cases.Of course, apps with their own strong branding (such as the Facebook, eBay, and iMDb apps) usually do not follow these guidelines, as being consistent with their own identity outweighs being consistent with Windows Phone. In addition, it often makes sense to deviate from this style if you want your app to stand out in the marketplace.

 

How can my icon get the user’s theme accent color as its background, as with the built-in apps?

Each tile icon is rendered on top of an accent-colored square when pinned to Start, so using a transparent background color in your PNG file is all you need to do.Unfortunately, each thirdparty app icon in the app list is always rendered on top of a dark grey square, so there’s no way to get the same effect in the app list. Nothing prevents you from using one of the standard theme colors as a hard-coded background inside your image file, but you shouldn’t do this unless it happens to be a color associated with your brand.That’s because it will never change and therefore look out-of-place to users who switch their accent color.

 

Icons for your marketplace listing have different guidelines than your app’s real icons!

Whereas using a transparent background is encouraged for your tile icon, it should be avoided for the separate set of icons you upload to the marketplace.The phone’s Marketplace app renders icons on black squares, which looks odd under the dark theme when the icon has transparency. Even worse, the marketplace section of the Zune program leaves its default white background underneath the icon. For typical Windows Phone app icons, the result is a completely invisible icon due to the white-on-white effect!

To avoid this, you must choose a background color for your marketplace icons. It’s a good idea to use this same background for your app icon, even if your tile icon uses transparency to fit in with the user’s theme.

 

Leveraging the built-in splash screen support by supplying the SplashScreenImage.jpg file can be useful for boosting the perceived load time of your app. A desirable approach is to make the image look like what your app will look like once fully loaded, perhaps with disabled-looking controls and without text.This gives the appearance of your app being instantly “there,” but not fully loaded.The text is normally omitted from the image because even if you localize your app for multiple languages, you can still only have the single image file per app. Unfortunately, due to the single-file nature of the splash screen support, it’s only worthwhile for
apps that use hard-coded colors and support only a single orientation.That’s because a typical Windows Phone app looks radically different under the dark versus light theme (or in a portrait versus landscape orientation), so no single image can provide a seamless experience for one case without being jarring for the other cases. In addition, I’m a big believer in making apps feel the same as the built-in apps unless there’s a good reason not to, and none of the built-in apps use a perceivable splash screen.

The good news is that the phone already produces a built-in animated “Loading…” or “Resuming…” user interface when an app is launched or reactivated. If yours is not fast to load, I’d recommend addressing the core issue (such as delaying computationally expensive work) rather than using a sub-standard splash screen. In this book, none of the apps use a splash screen.To remove the splash screen from your app, simply remove SplashScreenImage.jpg from your project.

Many apps in the marketplace (such as the Facebook and Twitter apps) do use a splash screen, but not to improve the perceived loading time.They simply use it to help customize the loading process with their own branding.

 

 

The icon and splash screen images must have a build action set to Content!

If you replace any of the three image files with your own, be sure to set each file’s Build Action in Visual Studio to Content, rather than the default Resource, as shown in Figure 1.2.This correctly places the files directly inside your .xap file rather than embedded inside your DLL. Note that the value of Copy to Output Directory does not matter. Even if the file is not copied to the output directory, it still gets copied to the correct place inside the resultant .xap file.

The three image files discussed in this section must be given a build action of Content in Visual Studio’s Properties window.
FIGURE 1.2 The three image files discussed in this section must be given a build action of Content in Visual Studio’s Properties window.

 

MainPage.xaml

Every app consists of one or more pages. New projects are given a single page called MainPage. This page defines what the user sees once your app has loaded. It is implemented across two files: MainPage.xaml contains the user interface, and MainPage.xaml.cs contains the logic, often called the code-behind. Listing 1.2 shows the initial contents of MainPage.xaml, and Figure 1.3 shows what this XAML produces when you run the app.

Remember that MainPage.xaml is referenced in WMAppManifest.xml!
If you want to rename this file, you must also change its name inside your application manifest; otherwise your app will stop working.

LISTING 1.2 MainPage.xaml—The Initial Markup for the App’s Main Page

[code]

<phone:PhoneApplicationPage
x:Class=”Tally.MainPage”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
xmlns:phone=”clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone”
xmlns:shell=”clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone”
xmlns:d=”http://schemas.microsoft.com/expression/blend/2008”
xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006”
FontFamily=”{StaticResource PhoneFontFamilyNormal}”
FontSize=”{StaticResource PhoneFontSizeNormal}”
Foreground=”{StaticResource PhoneForegroundBrush}”
SupportedOrientations=”Portrait” Orientation=”Portrait”
mc:Ignorable=”d” d:DesignWidth=”480” d:DesignHeight=”768”
shell:SystemTray.IsVisible=”True”>
<!–LayoutRoot contains the root grid where all other page content is placed–>
<Grid x:Name=”LayoutRoot” Background=”Transparent”>
<Grid.RowDefinitions>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”*”/>
</Grid.RowDefinitions>
<!–TitlePanel contains the name of the application and page title–>
<StackPanel x:Name=”TitlePanel” Grid.Row=”0” Margin=”24,24,0,12”>
<TextBlock x:Name=”ApplicationTitle” Text=”MY APPLICATION”
Style=”{StaticResource PhoneTextNormalStyle}”/>
<TextBlock x:Name=”PageTitle” Text=”page name” Margin=”-3,-8,0,0”
Style=”{StaticResource PhoneTextTitle1Style}”/>
</StackPanel>
<!–ContentPanel – place additional content here–>
<Grid x:Name=”ContentGrid” Grid.Row=”1”>
</Grid>
</Grid>
<!– Sample code showing usage of ApplicationBar

–>
</phone:PhoneApplicationPage>

[/code]

At a quick glance, this file tells us:

  • This is a class called MainPage (in the Tally namespace) that derives from the PhoneApplicationPage control.
  • It is marked to only support the portrait (vertical) orientation.
  • It contains two text blocks with boilerplate text that are meant to be the application name and an appropriate page title.
  • The page leverages Grid and StackPanel controls to arrange the current text blocks, and additional content is meant to be placed in the grid named ContentGrid.
  • For such a simple page, there are a lot of things in here!

We’ll examine the following two aspects of this file more deeply:

  • The XML namespaces used at the top of the file
  • Phone theme resources, referenced as “{StaticResource XXX}”
The initial MainPage.xaml.
FIGURE 1.3 The initial MainPage.xaml.

XML Namespaces

MainPage.xaml contains most of the XML namespaces you’ll see in this book. Table 1.1 explains them. Although some look like URLs that you can view in a Web browser, they are not. They all map to .NET namespaces in specific assemblies.

The Common Namespaces in Windows Phone XAML Files
TABLE 1.1 The Common Namespaces in Windows Phone XAML Files
Continued
TABLE 1.1 Continued

The first three namespaces are almost always used in Windows Phone apps. The shell namespace is only needed when a page uses an application bar via the ApplicationBar class, or when it enables the status bar by setting  SystemTray.IsVisible to True. The status bar is the top area of the phone that displays the time and, based on various
factors, signal strength, battery charge, and more. As a developer, you can’t do anything with the status bar other than show or hide it.

Phone Theme Resources

Rather than hardcoding fonts, font sizes, and colors, MainPage.xaml makes use of several phone-specific resources using
StaticResource syntax. Windows Phone defines several resources to make it easy for apps to get a look-and-feel consistent with guidelines and with the user’s chosen theme. Appendix C, “Theme Resources Reference,” lists them all and demonstrates what they look like for both user themes (light and dark). These resources not only contain individual colors, brushes, fonts, font sizes, and thicknesses (for borders and margins/padding) but also contain a bunch of styles for text blocks that package individual resources together.

The resources used by this initial page are

  • PhoneFontFamilyNormal—Segoe WP
  • PhoneFontSizeNormal—20 px (15 pt)
  • PhoneForegroundBrush—A solid color brush that is white in the dark theme and black in the light theme
  • PhoneTextNormalStyle—The previous three resources combined: a FontFamily of PhoneFontFamilyNormal, FontSize of PhoneFontSizeNormal, and Foreground of PhoneForegroundBrush
  • PhoneTextTitle1Style—A FontFamily of PhoneFontFamilySemiLight (Segoe WP Semilight), FontSize of  PhoneFontSizeExtraExtraLarge (72 px, which is 54 pt), and Foreground of PhoneForegroundBrush.
The initial MainPage.xaml, shown under the light theme.
FIGURE 1.4 The initial MainPage.xaml, shown
under the light theme.

This explains how Listing 1.2 produces the result from Figure 1.3 when the user’s theme is dark. Figure 1.4 shows the same page when the phone uses the light theme.

MainPage.xaml.cs

Listing 1.3 shows the initial contents of MainPage.xaml.cs, the code-behind file for MainPage.xaml. Because this app does not yet do anything, it only contains the required call to InitializeComponent that constructs the page with all the visuals defined in the XAML file. The class is marked with the partial keyword because its definition is shared with a hidden C# file that gets generated when the XAML file is compiled.

LISTING 1.3 MainPage.xaml.cs—The Initial Code-Behind for the App’s Main Page

[code]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
namespace Tally
{
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
}
}
}

[/code]

App.xaml and App.xaml.cs

App.xaml is a special XAML file that doesn’t define any visuals, but rather defines an App class that can handle application-level tasks. Usually the only reason to touch this XAML file is to place new application-wide resources, such as custom styles, inside its Application.Resources collection.

AssemblyInfo.cs

This file is not worth showing in this book. It contains a bunch of attributes where you can put a title, description, company name, copyright, and so on, that get compiled into your assembly. But setting these is unnecessary because all of the information used by the marketplace is separately managed. Still, the AssemblyVersion and AssemblyFileVersion
attributes, typically set to the same value, can be useful for you to keep track of distinct versions of your application:

[code]

[assembly: AssemblyVersion(“1.0.0.0”)]
[assembly: AssemblyFileVersion(“1.0.0.0”)]

[/code]

By using *-syntax, such as “1.0.*”, you can even let the version number auto-increment every time you rebuild your app.

Packaging the Application

You must package your application as a XAP file to be able to upload it to Windows Marketplace. The Windows Phone Tools for Visual Studio and the special versions of Microsoft Visual Studio® Express and Microsoft Expression Blend® design software included in the Windows Phone Tools automatically create a XAP file that you can upload.

A XAP file contains a series of artifacts that are required to install the application on a Windows Phone. These include the following:

  • A valid Windows Phone application manifest file named WMAppManifest.xml, with the application title contained inthe <Title> element, and including a list of permission demands for device capabilities
  • A valid .NET Framework application manifest file named AppManifest.xaml that lists the assemblies used by the
    application
  • The assembly (DLL) files specified in the AppManifest.xaml file
  • Any user controls or XAML files required by the application
  • The resources, icons, images, media, and other content files that the application requires; the application icon must be a 62-by-62 pixel PNG file, and the application tile image used on the Start screen must be a 173-by-173 pixel PNG file.

The maximum size of the XAP package file you can upload is 400 MB, including all the media or other content that is included in the XAP file but not compiled into the application as resources.

If the package to download to the phone exceeds 20 MB in size, users will not be able to download it from Windows Marketplace when using a GPRS or 3G phone connection. Packages over 20 MB in size must be downloaded and installed over a Wi-Fi or physical wired connection.

If your application requires a separate download in order to work, such as an additional data package, and if the separate download exceeds 50 MB, you must inform the user and obtain permission to download it.

The Windows Marketplace Repackaging Process

The validation process, which the Windows Marketplace applies to all submitted applications, automatically unpacks and then repackages the application. It unpacks all the contents of the submitted XAP file and performs a range of validation checks on these. Afterward, it creates a new WMAppManifest.xml file based on the results of the validation process and repackages all the original content with the new WMAppManifest.xml into the XAP package that users will download.

The updated WMAppManifest.xml file contains a product identifier that is used in Windows Marketplace to uniquely identify the application download package; it also includes an indicator of the application type (such as a music and videos hub application). However, the main feature of the validation process that it is important for developers to understand is the way that the security permission demands are applied in the new WMAppManifest.xml file.

By default, the WMAppManifest.xml file created by the Windows Phone Tools templates in Visual Studio and Expression Blend contains permission demands for all the device capabilities. These include networking, location, microphone, push notifications, and more. However, applications distributed through Windows Marketplace advertise the capabilities they require, and users can be sure that an application that does not indicate it uses (for example) the location service, actually will not use it internally without the user being aware.

To ensure that this is the case, the validation process searches the application code for calls to any of the device capabilities that require permission. If the way that these are used in the application meets the certification  requirements, the validation process includes that permission demand in the WMAppManifest.xml file and the product
metadata displayed by Windows Marketplace. If a feature is not used, the permission demand is removed from the  MAppManifest.xml file. Therefore, code can use only the features for which it is certified, and any attempt to use other features (such as using reflection to invoke a feature) will fail because the application does not have the relevant permission.

You can perform the same detection process as is carried out by the submission validation process on your own computer by using the Windows Phone Capability Detection Tool to check that the correct capabilities are detected.

The Windows Phone Capability Detection Tool (Capability Detection.exe) is installed by default in the  %ProgramFiles%MicrosoftSDKsWindows Phonev7.0ToolsCapDetect folder. For information on using this tool, see “How to: Use the Windows Phone Capability Detection Tool” on MSDN (http://msdn.microsoft.com/en-gb/library/gg180730(v=VS.92).aspx).

The Windows Phone Capability Detection Tool is part of the October 2010 update to the Windows Phone Tools, which is available from the Microsoft Download Center (http://www.microsoft.com/downloads/en/details.aspx?FamilyID=49B9D0C5-6597-4313-912A-F0CCA9C7D277&displaylang=en).

After the validation process completes, the XAP file is rebuilt, including all the original content plus a file named WMAppPRHeader. xml that contains the Digital Rights Management (DRM) information. The DRM information controls how and where the application can be used, and it manages the trial period where applicable.