Moving from eMbedded Visual Basic to Visual Basic .NET

 

Microsoft Corporation

August 2003

Applies to:
   Microsoft® Windows® Powered Pocket PC 2002
   Windows Mobile-based devices
   Windows Mobile 2003 Second Edition software for Pocket PCs
   Windows Mobile 2003 Second Edition software for Smartphones
   Microsoft eMbedded Visual Basic®
   Microsoft .NET Framework
   Microsoft Visual Basic .NET
   Microsoft .NET Compact Framework
   Microsoft Visual Studio® .NET

Summary: Learn how to move Microsoft Windows Pocket PC 2002 software development from Microsoft eMbedded Visual Basic to the Microsoft .NET Framework and Visual Basic .NET. The release of the .NET Compact Framework enables mobile application development to make use of the same tools and languages used in server and desktop application development. (36 printed pages)

Contents

Introduction
Fundamental Changes
Overview of Development Changes and Improvements
User Interface and Application Navigation
Developing for the Pocket PC and Smartphone
Application Integration
Working with Databases
Deployment and Distribution
Conclusion

Introduction

The purpose of this white paper is to explain how to move Microsoft® Windows® Pocket PC 2002 software development from Microsoft eMbedded Visual Basic® to the Microsoft .NET Framework and Visual Basic .NET. The release of the Microsoft .NET Compact Framework enables mobile application development to make use of the same tools and languages used in server and desktop application development.

The .NET Compact Framework contains a subset of the .NET Framework types and namespaces. There is also an integrated part of the Microsoft Visual Studio® .NET environment called Smart Device Programmability that includes support for development with the .NET Compact Framework, for example, forms designer, device emulator, and so on. In essence, the new platform and improvements in tools and languages help increase the quality of mobile application development, code, execution, and deployment. The following figure illustrates in overview the relationship between the development building blocks.

Figure 1. How it all fits together

Fundamental Changes

Microsoft eMbedded Visual Basic has, since its release, become a popular application development environment. The similarities with Microsoft Visual Basic 6.0 and ease-of-use have attracted many developers. However, inherent language and platform-dependent limitations have required workarounds, third-party products, and low-level Windows CE API calls. Most of these limitations are addressed by the new environment, while some still need creative attention. The fundamental changes are found in the fact that Visual Basic .NET is an object-oriented, modern language and that code is built using common .NET Compact Framework class libraries such as System.Windows.Forms for Windows forms and System.Data for database management with common methods, properties, and events. Inherent support for common tasks such as strong types, using classes, calling XML Web services, and implementing structured exception handling fills gaps that eMbedded Visual Basic developers have had to address less efficiently before.

Porting Your Application

There is no automatic upgrade path for existing eMbedded Visual Basic code base; the platform and language differences are simply too significant to successfully and efficiently design and implement such support. The majority of the porting effort typically relates to:

  • Language Grammar: eMbedded Visual Basic is a Visual Basic Script language, so grammar differences vary from insignificant to very significant.
  • Implementation of Common Code Libraries: Existing eMbedded Visual Basic common code libraries will need to be ported. Since the use of classes is now supported, the implementation and use of the code libraries are likely to be different. Since the .NET Compact Framework is a subset of the .NET Framework, existing Visual Basic .NET class libraries can now be leveraged for Pocket PC development.
  • Application Navigation and Control Flow: Managing forms and application navigation is handled through types in System.Windows.Forms and is implemented differently from eMbedded Visual Basic.
  • Databases: Data access is handled through a subset of ADO.NET. Microsoft provides a managed data provider for Microsoft SQL Server™ 2000 Windows CE Edition 2.0 (SQL Server CE 2.0). The .NET Compact Framework does not include managed types for accessing the local data store sometimes called CEDB or Pocket Access, which is commonly used among eMbedded Visual Basic developers.
  • XML Web Services: eMbedded Visual Basic provided no built-in support to call remote components, requiring third-party support to call and use remote components. Support for Web services is one of the core types of the .NET Compact Framework and considered to be the primary integration mechanism in the entire .NET Framework. System integration code rewrite is very likely since efforts related to system integration are typically considerable in most development projects.
  • Exception Handling: Error handling in eMbedded Visual Basic consisted of four words: "On Error Resume Next", and "neverending "If Err.Number <> 0 Then" statements. Structured and efficient exception handling using Try ... Catch ... Finally blocks will improve code robustness and fault tolerance.

More overview information can be found on the following pages:

Microsoft .NET Compact Framework
(https://msdn.microsoft.com/vstudio/device/compact.asp)

Frequently Asked Questions About Microsoft .NET Compact Framework
(https://msdn.microsoft.com/vstudio/device/compactfaq.asp)

Microsoft .NET Framework
(https://msdn.microsoft.com/netframework/)

GotDotNet .NET Smart Device Development
(https://www.gotdotnet.com/team/netcf/default.aspx)

Overview of Development Changes and Improvements

Platform Improvements

Development in eMbedded Visual Basic is done in a separate environment and for a runtime specific to eMbedded Visual Basic. The .NET Compact Framework replaces the Visual Basic runtime environment, which means that developers are tasked with re-learning platform, language, and tools skills. The core benefits behind most changes can be derived from the word "common." The .NET Compact Framework shares a number of common characteristics with the full .NET Framework, including:

  • A Common Language Runtime (CLR): All mobile application development uses the same runtime environment for executables. The CLR provides program loading, memory management, and other core operating system features (Figure 2).
  • A Common Type System (CTS): The CTS defines how types are declared, used, and managed in the runtime and is accessible from all .NET Framework languages.
  • A Common Intermediate Language (CIL): Also known as Microsoft Intermediate Language (MSIL), CIL is a CPU-independent set of instructions that can be efficiently converted to native code.

Figure 2. One of many commons: The Common Language Runtime

The actual Core Framework is operating system and hardware. However, this cross-platform approach does not imply a least common denominator across all programming aspects. Platform invoke (Figure 3) is a service that enables managed code to call unmanaged functions implemented in dynamic link libraries (DLLs), such as those in the Windows CE API. Fortunately, the development environment Visual Studio .NET hides these intricacies and makes available only the appropriate elements to the developer. This can be done since the developer can choose what platform should be targeted when starting a new project.

Figure 3. Choosing target platform in Visual Studio .NET

Applications developed are Just In Time (JIT) compiled and the CLR takes care of garbage collection, a memory maintenance process. This memory maintenance process, or the lack of a working one, has been an issue for eMbedded Visual Basic developers using the CreateObject-statement and the subsequent potential memory leaks. From a mobile solutions perspective, the single most important platform improvement is related to connectivity. The .NET Compact Framework inherently supports key Internet protocols (TCP IP, HTTP, and so on) and document standards (XML, SOAP, and so on), which will provide the foundations of connected mobile applications.

Development Environment Improvements

eMbedded Visual Basic programming was completed in a separate development environment. The .NET Compact Framework development is done in Visual Studio .NET 2003, thereby sharing development environment with development of the full .NET Framework. eMbedded Visual Basic developers will recognize the familiar look of Visual Studio elements and note that things likeIntelliSense in editor, user interface designers, robust debugging, and intuitive compile, deploy, and debug steps are in place. Support for development using remote Web services and other .NET Framework components has been built in and is seamlessly integrated into both tools and language.

Significant Language Improvements

Conceptually, eMbedded Visual Basic and Visual Basic .NET share the same roots and have a common Basic history. Key language structures and elements are brought forward to modern Visual Basic developers, but, more importantly, a vast number of new features are introduced. Significant improvements in terms of language include:

  • Development based on the .NET Compact Framework class libraries
  • Support for true object-oriented design
  • Structured exception handling
  • Multithreading capabilities
  • Integration with other languages

The .NET Compact Framework does not, however, support interoperability with COM, meaning wrappers around existing COM and ActiveX components have to be developed and then called using Platform Invoke (P/Invoke). A third-party workaround, available from Odyssey Software, lets the developer seamlessly use COM and ActiveX components from managed code.

For more information on differences and improvements, you can read the article
Ten Code Conversions for Visual Basics for Applications, Visual Basic .NET, and C#(https://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnoxpta/html/odc\_tencodeconverts.asp).

User Interface and Application Navigation

This new development tool (Visual Basic .NET) brings a more solid coding experience than before. As the development environment inherited many of the features that Visual Basic and eMbedded Visual Basic developers recognize, the transition will not be too difficult. Many of the familiar features such as double-clicking on a button and then entering the code for the Click event are still available. However, beneath the surface, much has changed.

In eMbedded Visual Basic, many steps were hidden from the developer, like design-time properties and event definitions. Also, due to the lack of a true object-oriented environment, some classes (forms, controls) were instantiated automatically on first use. In Visual Basic .NET all manipulation of the form in the designer is reflected in the code. Forms and controls are objects that have to be declared and instantiated in code.

Form Basics

As an introduction, let's look at a sample form as it looks to the designer (Figure 4). (Note that only the top part of the form is shown.)

Figure 4. Sample form

When this form is created using the forms designer, the code generated to declare the controls in the form looks like this:

Friend WithEvents Label1 As System.Windows.Forms.Label
Friend WithEvents TextBox1 As System.Windows.Forms.TextBox
Friend WithEvents Button1 As System.Windows.Forms.Button

The code generated to create the controls looks like this:

Me.Label1 = New System.Windows.Forms.Label
Me.TextBox1 = New System.Windows.Forms.TextBox
Me.Button1 = New System.Windows.Forms.Button

Finally, the code generated to set up the properties on those controls looks like this:

Me.Label1.Location = New System.Drawing.Point(8,11)
Me.Label1.Size = New System.Drawing.Size(48,16)
Me.Label1.Text = "Label1"

Me.TextBox1.Location = New System.Drawing.Point(56,8)
Me.TextBox1.Size = New System.Drawing.Size(104,22)
Me.TextBox1.Text = "TextBox1"

Me.Button1.Location = New System.Drawing.Point(168,8)
Me.Button1.Size = New System.Drawing.Size(64,24)
Me.Button1.Text = "Button1"
Me.Controls.Add(Me.Button1)
Me.Controls.Add(Me.TextBox1)
Me.Controls.Add(Me.Label1)
Me.Text = "Form1"

The location, size, and content (Text property) of each control is set; note that code is generated to actually add the controls to the form. An important detail is all controls now have the Text property (no Caption property on forms and labels).

There are still controls that need to store extra metadata (like design-time control properties and other resources like pictures) and that data is stored in a resource file with the same name as the form (in the above sample in Form1.resx).

The rest of the code generated for the form creates the basic structure of the form. First, the form is declared like any other class and inherits from the base window class provided by .NET Compact Framework:

Public Class Form1 : Inherits System.Windows.Forms.Form

The code above is actually generated in two lines, but to ease readability the two statements have been placed on the same row. When this form is created the constructor is called, and looks like this:

Public Sub New()
MyBase.New()
InitializeComponent()
End Sub

The constructor makes a required call to the base class constructor and then makes another required call to a generated private function (Sub InitializeComponent) that holds the code to create controls and set properties shown above. The InitializeComponent function is required by the forms designer and the code it contains should be manipulated with caution.

To start off the application, the Form1 form is defined as the "Startup object" in the Project Properties dialog box (right-click on the project in the Solution Explorer and select Properties), which is very similar to how it is done with eMbedded Visual Basic.

Developing for the Pocket PC and Smartphone

Events

When designing forms in an application, you should create the event handlers for the controls. Just as with eMbedded Visual Basic, you can double-click on a control to create the default event handler. If we continue the above sample and double-click on the button, the event handler for the Click event is generated:

Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
MessageBox.Show("Text on button is: " & Button1.Text)
End Sub

The parameters for an event procedure always look the same. The "sender" parameter is a reference to the object that raised the event and the "e" parameter is specific to each type of event. It usually contains data about the event raised that was provided as separate event parameters in eMbedded Visual Basic. Note that there is a special keyword (Handles) at the end of the declaration that specifies which controls use this event. A key detail is that the Handles keyword accepts multiple arguments, which means that this can be used to make the same event handler handle events for multiple controls. Let's add a button to the form (Button2) and modify the event handler code:

Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click,
Button2.Click
MessageBox.Show("Text on button is: " & CType(sender,
Button).Text)
End Sub

Now the event handler will handle the click events from both buttons. Note that the "sender" parameter is used to get the button object that raised the event.

The adding and removing of events is very similar to the way it is handled in eMbedded Visual Basic. To add another event handler, simply select it in the dropdown list of method names to the top-right of the code window. To remove an event handler, simply remove the event handler code. Also, just as in eMbedded Visual Basic, you have to manually copy the event handler code when you copy a control from one form to another.

Look and Feel

A common question from eMbedded Visual Basic developers moving to Visual Basic .NET is how to control the top-right button in the task bar. When you create a new form, the default setting shows the smart minimize button (which looks like an X) which does not close the window but places it in the background (not visible, but still running). This is because the form's Minimize property is set to True by default. As the ControlBox property is also set to True by default, the only step you need to change to make the OK button appear is to set the Minimize property to False. Then, if you need to add an event before the form closes (like validation) when the OK button is tapped, add a Closing event handler to the form. To continue the sample, the Minimize property of the form is set to False and the following event handler is added:

Private Sub Form1_Closing(ByVal sender As Object, _
ByVal e As System.ComponentModel.CancelEventArgs) _
Handles MyBase.Closing
If TextBox1.Text <> "TextBox1" Then
MessageBox.Show("Text in TextBox cannot be changed!")
e.Cancel = True
End If
End Sub

This example shows how to prevent the form from closing if the text in the TextBox is altered. Note how the second parameter (e) is used to handle event-specific values (properties).

Another special consideration is that a menu (actually MainMenu) control has to be added to the form to make the bottom menu bar and SIP (Soft Input Panel) visible. A menu control is always added to the first form generated when a new project is created, but when adding new forms the menu has to be added manually.

To make your applications look like other Pocket PC applications, here are additional guidelines to follow:

  • Always put the application name on all form captions (Text property on forms).
  • In subforms, add a Label control with the name of the dialog box in the top-left corner of the form (X=8, Y=5 is a good position). Beneath that label, add a single point line with the same width as the form. A suggestion is to use the Panel control with the height set to 1 and the width set to 240 (X=0, Y=23 is a good position).
  • To align Label controls to the text in a TextBox control, put the Label 3 pixels below the TextBox (Y of TextBox + 3).

For other user interface considerations, see the help file for Visual Basic .NET and the general Pocket PC design guidelines(https://www.microsoft.com/mobile/assets/Design\_PocketPC.pdf).

Form Navigation and Interaction

An application consisting of only one form is not very common, so let's look at how to make the navigation between the different forms efficient with high usability. Building further on the above sample, let's add another form (select menu option Project, Add Windows Form, and accept the default name, Form2). The first step is to add a MainMenu control to make the bottom menu bar visible and then also change the Minimize property of the form to False to make the top-right OK button visible. Try making this a habit when creating child forms, as it will save you some debugging time.

The simplest way to bring up the new form is to add this code to a button on the first form:

Dim secondForm As Form2
secondForm = New Form2
secondForm.ShowDialog()

As mentioned before, and opposed to how it worked in eMbedded Visual Basic, you have to create all class instances before they are used, including forms.

Depending on the size of the loaded form, you may want to show the wait cursor during the load of the form; therefore, you could put the following line of code before the call to ShowDialog:

Cursor.Current = Cursors.WaitCursor

Then, when the form is loaded, the wait cursor should be removed. This can be done with the following code in the constructor of the second form:

Public Sub New()
MyBase.New()
InitializeComponent()
Cursor.Current = Cursors.Default
End Sub

However, if the Load event handler for the second form (Form2_Load) contains a fair amount of initialization, such as loading values in controls, it may be a good idea to restore the cursor at the end of the Load event handler instead.

A common issue is communication between forms. The simplest form of communication is provided by the call to ShowDialog. ShowDialog can return any of the predefined result codes (OK, Cancel, Yes, No, Abort, Retry, Ignore, and None). In the first form, the code can be changed to:

MessageBox.Show ("You selected: " +
secondForm.ShowDialog().ToString())

This will show the result code returned by the second form. There are two ways to return this value from the second form. Either you set the DialogResult property of a button, or you set the DialogResult property of the form itself. If a button is added to the second form (named btnCancel), the following code can be added to the constructor to set this property:

btnCancel.DialogResult = DialogResult.Cancel

Note that this property is not available from the designer, and has to be set in the code. If the Minimize property of the second form was changed to False, two result codes could be returned from the form: OK by tapping the top-right OK button, and Cancel by tapping the btnCancel button.

According to the general Pocket PC user interface guidelines (see above), the application should only show one instance in the Running Programs (tab in Memory Setting) list. But if you open a second form with the first one visible (below the second), each form will show up in the Running Programs list. Therefore, a way to hide the first form when the second is visible is needed. The form is hidden with a call to the Hide method on the first form. You could do this in the first form by calling Me.Hide() before the call to ShowDialog and Me.Show() afterwards. However, there are issues with this approach. If the second form takes a long time to load, no form will be shown in the transition between the forms (this looks very awkward to the user of the application); and, building on the sample above, the "You selected ... " message box will be displayed without any form visible. A more viable way of handling this is to make the second form hide the first form. To do this, the second form needs to somehow receive the instance of the first form. The most obvious way to pass this information along is to use the constructor of the second form. In the second form, let's add a private class variable to hold the first form's instance:

Private firstForm As Form1

The constructor is updated with a new parameter and the code to save that parameter to the private variable:

Public Sub New(ByVal firstForm As Form1)
Me.firstForm = firstForm
' other code...
End Sub

Now the line of code in the first form to instantiate the second form is altered to:

secondForm = New Form2(Me)

This way the second form now "knows" about the first form and can call:

firstForm.Hide()

It is best to use this at the end of the form's Load event (Form2_Load). In the form's Closing event (Form2_Closing), the first form can be shown again using the code:

firstForm.Show()

The result is a smooth transition between the forms, and the transition is independent of any performance issues such as the time to load or close the second form. This is a great way to communicate between the forms. There is no problem with adding any number of parameters to the constructor of the second form, thereby allowing any data to flow from the first form to the second. But how about passing data back from the second form to the first? We know that we can notify the first form with a result code (as shown above), but we need other means to pass data back to it. In the code above, you may have noticed that the firstForm variable (and parameter) was declared as Form1 and not the base type Form. The Hide and Show methods are part of the base type Form, which makes this possible. However, if the firstForm variable is declared as type Form1, the Form1 class could implement a public (or rather friend) method to be called by the second form for passing data back to the first form.

Building further on the same sample above, the following method is added to the first form:

Friend Sub Form2Data(ByVal text As String)
textFromForm2 = text
End Sub

The textFromForm2 is a privately declared class variable in Form1. Here's a modified version of the code in the Click event of the button that loads the second form:

Dim secondForm As Form2
Dim result As DialogResult
Cursor.Current = Cursors.WaitCursor
secondForm = New Form2(Me)
result = secondForm.ShowDialog()
MessageBox.Show("You selected: " + result.ToString())
If result = DialogResult.OK Then
txtFromForm2.Text = textFromForm2
End If

The TextBox control to show the data from the second form is named txtFromForm2.

In the second form, the following code is added to the Closing event (Form2_Closing):

firstForm.Form2Data(TextBox1.Text)

This approach allows for any data to be transferred from the second form to the first.

Application navigation is critical to increase usability of an application. With the suggestions made above, you now know ways to both navigate efficiently between forms and communicate data between those forms.

Most enterprise mobile solutions are extensions of existing systems. Therefore, a critical aspect of the solution architecture is how it integrates with existing IT systems.

Application Integration

Integration Strategies

Two integration dimensions are the integration pattern (how) and connection frequency (how often).

Integration Patterns

Many commercially available Pocket PC applications today are not connected at all. They are referred to as "stand-alone" applications and have no integration capabilities. However, as devices become more connected, integration becomes critical. A relevant question to answer is, "How?" The most common architectural patterns for integration are:

  • Device Data to Server Data: The database on the device is synchronized directly with the server database. This option is necessary when there is little logic involved in data synchronization, as the performance is very good.
  • Device Logic to Server Logic: The device application connects to components on the server. This is the most common option, as most synchronization involves business logic.
  • Device Logic to Server Data: The device connects directly to the server database. This option is advisable when there's not very much logic involved and there's sufficient bandwidth available.
  • Server-only: The device browser is used to connect to a server-side Web application. This is an intriguing option for the same reasons all thin client-based (Web) applications are, but this obviously requires a device that is always connected to the server.

The last option is not really an integration option, but is included to demonstrate that sometimes available bandwidth is enough for an always-online application. Frankly, this option is seldom the best alternative. The different technologies that apply to each one of these patterns are:

  • Device Data to Server Data: SQL Server CE .NET Remote Data Access and Merge Replication
  • Device Logic to Server Logic: Web services
  • Device Logic to Server Data: SQL Server .NET Provider
  • Server-only: Mobile Device Programmability included in Visual Studio .NET 2003

As illustrated above, most of the technologies needed for implementing an application using the patterns are in place.

Connection Frequency

Another critical aspect is the network dependency the solution requires. The traditional choice of offline versus online is no longer sufficient. Depending on the specific scenario, the choice is more often something in between offline and online.

The most important connection frequencies are:

  • Seldom: Most of the time, the application can work offline, and synchronization is only needed at certain intervals when there's little or no concurrency (many users working with the same data), as it requires very low bandwidth or high bandwidth at certain times (for example, when at the office).
  • Automatically: This is very similar to Seldom (see previous bullet), but the application includes logic that determines when the synchronization needs to take place.
  • Often: The application makes frequent calls to the server to send or retrieve data. There is no need for a continuous connection and, as the application only transfers the data across, the required bandwidth is relatively low. If the cost for bandwidth is related to bytes sent, this is a very appealing option.
  • Always: This is an online (most often Web) application that requires a connection to work.

The available bandwidth at different times is also important to determine the amount of data that can be transferred in a certain scenario. The most common options are:

  • High (WLAN/Wi-Fi = 11 Mb or higher)
  • Low (WAN/GPRS = 43.2 Kb)
  • Minimal (WAN/CDMA = 4.8 Kb or WAN/GSM = 9.6 Kb)

Making the Choice

There is no single combination of these options that fit all scenarios, but each scenario should be evaluated with the above options taken into consideration. For an eMbedded Visual Basic developer, most of the synchronization options mentioned above could be accomplished (most often using some middleware). The database synchronization, using Remote Data Access or Merge Replication in SQL Server for Windows CE, is very similar when using Visual Basic .NET. Connection directly to server databases is very similar to what you would do with Visual Basic .NET on the desktop and the creation of online (Web) applications is not really related to eMbedded Visual Basic. Therefore, the most relevant option to consider is the use of Web services as a way to connect to server components.

XML-based Web Services

In short, Web services is a way to call (requests/responses) methods using Extensible Markup Language (XML) and, most often, HyperText Transfer Protocol (HTTP). The overall goal of Web services is application integration; its use will start with company internal applications followed by a way to connect systems in different companies. There are two important reasons why Web services will succeed. The first is the fact that it's built on easily recognized standards (HTTP, XML, SOAP, WSDL, UDDI, and so on) and the second is that most of the major companies are on board (IBM, Microsoft, Sun, Oracle, and many more).

There is a huge effort in the works to extend Web services Simple Object Access Protocol (SOAP) with more capabilities called Global XML Architecture (GXA). GXA is not a product, but rather a common name for a number of specifications for applications like finding Web services (WS-Inspection), making secure calls (WS-Security), defining message and alternative paths (WS-Routing/WS-Referral), providing transaction support (WS-Transaction), and sending binary attachments (WS-Attachments). The first concrete proof of GXA is the Web Services Enhancements (https://msdn.microsoft.com/webservices/building/wse/) that include the .NET Framework implementation of some of these specifications.

Connecting to Server Components

With eMbedded Visual Basic, there was no native way to easily connect to server components; developers had to rely on third-party middleware components to enable this functionality. There are several examples of such middleware products, and the most useful ones are those that resemble the native way of using components. With a product like Odyssey Software's CEfusion, the eMbedded Visual Basic developer could create objects on the server and use them as if they were using DCOM (Distributed Component Object Model) on the desktop. With CEfusion, data is transferred using Recordsets that are almost identical to the desktop counterpart. With Visual Basic .NET there is a native way of doing application integration using Web services. Components on the server can publish their functionality using simple protocols like XML and HTTP. Visual Basic .NET and .NET Compact Framework have built-in support for consuming Web services, and, as we will see, the support makes application integration not only possible, but straightforward and very powerful.

Publishing Web Services

With Visual Studio .NET on the server, the creation of Web services is very simple; Web services can also be used to publish both .NET Framework and Component Object Model (COM) components. There are ready-made templates to create an Active Server Pages (ASP) .NET Framework Web service project. There are many good resources on creating Web services on MSDN (https://msdn.microsoft.com/).

A good introduction to creating Web services in the .NET Framework is provided in the article Getting Started with XML Web Services in Visual Basic.NET and Visual C# (https://msdn.microsoft.com/library/en-us/dv\_vstechart/html/vbtchGetting StartedWithXMLWebServicesInVisualStudioNET.asp) and, for using COM components from .NET, see the Visual Basic .NET documentation (https://msdn.microsoft.com/library/en-us/vbcn7/html/vaconIntroductionToCOMInteroperability.asp).

As the support for COM interoperability is very efficient in the .NET Framework, the alternative to encapsulating COM components is many times more efficient than recoding the existing components to native .NET Framework components.

Even if the .NET Framework is not available on the server, the Microsoft SOAP Toolkit(https://www.microsoft.com/downloads/details.aspx?FamilyID=ba611554-5943-444c-b53c-c0a450b7013c\&DisplayLang=en) allows you to publish COM components as Web services.

Many other server platforms allow publishing of server components as Web services, so it should be possible to integrate your mobile solutions with most existing systems running on various platforms.

Client Basics

Let's start off with a very simple Web service, client-implemented with Visual Basic .NET. Demonstrating its existing functionality, this sample will use a public (free) Web service. In a new project, simply add a Web Reference (Menu Option Project and Add Web Reference); this sample uses a free public Web service for checking validity of credit card numbers. See Figure 5 to see what the form looks like. (Note that only the top part of the form is shown.)

Figure 5. Web service sample form

After creating a new Visual Basic .NET project, the only actions taken are to the Names and Text properties of the form and controls. These have been changed to reflect more appropriate values. To create a reference to the Web services, simply select menu option Project and Add Web Reference and add the URL of the Web service (the URL https://secure.cdyne.com/creditcardverify/luhnchecker.asmx was used for this sample).

In the Click event on the button, add the following code:

Dim CreditCardWS As New cdyne.LUHNChecker
Dim Result As cdyne.ReturnIndicator
Result = CreditCardWS.CheckCC(txtCreditCardNumber.Text)
MessageBox.Show("Card type: " & Result.CardType & _
Microsoft.VisualBasic.vbCrLf & _
"Card valid: " & IIf(Result.CardValid, "Yes", "No"))

Note that the name of the Web Reference was changed to "cdyne" (the suggested name, which is actually a reversal of the domain URL, is not very convenient). When the reference to the Web services is set, the Web service type can be treated like any other type (class) in your application.

The Web service might return a basic type (String, Integer, and so on), but as shown in this sample, it could also return a complex type that is translated into a class in Visual Basic .NET. In this sample, that class is ReturnIndicator that has two members (CardType and CardValid).

If you want a look behind the scenes of this application, click on the Show All Files button in the Solution Explorer and look for the Reference.vb file (under the Reference.map file). If you bring up the code for that file, you can see how the Web services and the complex type are actually declared as pure .NET Framework classes.

"Chatty" vs. "Chunky" Interaction

This sample also shows an interesting pattern for working with Web services. Since the return value is a complex type, multiple values can be returned in a single call. This is often the preferred solution for performance before making multiple calls to get each value. However, if the amount of data returned in one call is very large, there might be a problem returning all data in one call. The problem is usually referred to as chatty versus chunky interaction. "Chatty" interaction means that there are many calls to perform a certain task, while "chunky" means that each call contains a large amount of data. In many mobile applications, the bandwidth is usually limited. With low bandwidth, extra attention must be paid to selecting the optimal solution.

DataSet Integration

A convenient construct when working with data in eMbedded Visual Basic is the ActiveX Data Objects (ADO) Recordset. The corresponding, but far from identical, construct in Visual Basic .NET is a DataSet (for more information on the difference between a Recordset and a DataSet, please see section "Working with Databases" below). In short, the DataSet is a disconnected data container with the ability to extract its data as XML, which could very useful when combined with Web services.

To show how to integrate with a server component using Web services and a DataSet, let's start by creating a Web service using the .NET Framework tools. In Visual Studio .NET, a new project is created with the type "ASP.NET Web Service" and the name "DataSetWS". To illustrate this point, with integration independently of language used in the .NET Framework, the Web service is created as a C# project. Hopefully, the code is simple enough to read even if you have no prior experience of C#. When the new project is created, the following code is added:

[WebMethod]
public DataSet GetEmployees(int EmployeeID)
{
SqlConnection con;
SqlDataAdapter da;
DataSet ds;

con = new SqlConnection("data source=(local);initial
catalog=Northwind;" +
"uid=sa;pwd=");
con.Open();
if (EmployeeID > 0)
da = new SqlDataAdapter("SELECT EmployeeID,LastName,
FirstName, " +
"HomePhone FROM Employees WHERE EmployeeID="
+
EmployeeID.ToString(),con);
else
da = new SqlDataAdapter("SELECT EmployeeID,LastName,
FirstName, " +
"HomePhone FROM Employees",con);
ds = new DataSet("Employees");
da.Fill(ds,"Employees");

return ds;
}

The database used for this sample is a sample Northwind database in SQL Server. The Web service method (GetEmployees) takes one parameter (EmployeeID) and returns a DataSet. When the connection is created and opened, a check is made to determine whether the provided employee identity is valid (above zero). If it is, a specific employee is selected from the database; if not, all the employees are retrieved. The result of the query is stored in a table in a DataSet (both named "Employees") and the DataSet is returned to the caller. Note that only the necessary fields are returned from the database query to make the data as small as possible. When the project is built, you have a Web service for getting employee information from your "server" (probably your development PC).

Let's extend the previous sample and add a number of items to the form, ending up with something that looks like this (Figure 6):

Figure 6. DataSet Web service sample form

The new items are a label and a textbox (named "txtEmployeeID") for entering the employee identity; a button for making the Web service call; and a ListView (named "lvwEmployees") to hold the returned employees' data. If no employee identity is entered in the text box, the data for all employees is returned. Three columns are added to the ListView (Name, Phone, ID), and the last column is hidden (Width=0).

After the Web Reference is added (using the URL of your newly created Web service), the following code is added to the Get button Click event:

Dim DataSetWS As New server.NETCF
Dim ds As DataSet
Dim iEmployeeID As Integer = 0
Dim dr As DataRow
Dim lvi As ListViewItem

If IsNumeric(txtEmployeeID.Text) Then
iEmployeeID = Convert.ToInt32(txtEmployeeID.Text)
End If

ds = DataSetWS.GetEmployees(iEmployeeID)

If ds.Tables(0).Rows.Count < 1 Then
MessageBox.Show("No Employee(s)found!")
Else
lvwEmployees.Items.Clear()
For Each dr In ds.Tables(0).Rows
lvi = New ListViewItem(dr("FirstName").ToString() & " " & _
dr("LastName").ToString())
lvi.SubItems.Add(dr("HomePhone").ToString())
lvi.SubItems.Add(dr("EmployeeID").ToString())
lvwEmployees.Items.Add(lvi)
Next
End If

First, the Web service is created (in this sample it's referenced as "server" and the name of the Web service class on the server is named "NETCF") and, after the other declarations, a check is made to see if an employee identity was added to the TextBox (txtEmployeeID). If no identity was supplied, the default value (0) is used when calling the Web service. The DataSet (ds) returned is checked to include rows and, if any, those rows are added to the ListView. Note that the third column retrieves the employee identity, which can be useful when getting more employee information on a specific row in the ListView.

This is a convenient way to transfer relational data between the server and a client, and the support in the .NET Framework is excellent when it comes to the amount of code needed to accomplish this task. Upon examination of the data transferred, you will notice that not only is the data transferred, but so is the metadata (field types, lengths, and so on).

Asynchronous Web Service Calls

Per the discussion on "chatty" versus "chunky", it's apparent that data in the form of XML can easily become very "chunky". Therefore, care should be put into minimizing the data sent across (like selecting only the needed columns as shown in the sample above). Sometimes there is a need to transfer large amounts of data without locking up the device—that's where asynchronous Web service calls become necessary.

Building further on the sample above, let's add another button (named "btnGetAsync") and the following code to its Click event:

Dim DataSetWS As server.NETCF = New server.NETCF
Dim iEmployeeID As Integer = 0a
Dim cb As AsyncCallback

btnGetAsync.Enabled = False
lvwEmployees.Items.Clear()

If IsNumeric(txtEmployeeID.Text) Then
iEmployeeID = Convert.ToInt32(txtEmployeeID.Text)
End If

cb = New AsyncCallback(AddressOf GetAsyncReturn)

DataSetWS.BeginGetEmployees(iEmployeeID,cb,DataSetWS)

Most of the code is very similar to the synchronous call covered above, but now the button is disabled during the call. This is a convenient way of notifying the user that the operation is active. The most important addition is the asynchronous callback variable (cb) used in the asynchronous call to the Web service (BeginGetEmployees).

The callback variable (cb) is essentially a function pointer to a public function that looks like this:

Public Sub GetAsyncReturn(ByVal ar As IAsyncResult)
Dim DataSetWS As server.NETCF
Dim ds As DataSet
Dim i As Integer = 0
Dim dr As DataRow
Dim lvi As ListViewItem

DataSetWS = ar.AsyncState

ds = DataSetWS.EndGetEmployees(ar)

If ds.Tables(0).Rows.Count < 1 Then
MessageBox.Show("No Employee(s) found!")
Else
For Each dr In ds.Tables(0).Rows
lvi = New ListViewItem(dr("FirstName").ToString() & " "
& _
dr("LastName").ToString())

lvi.SubItems.Add(dr("HomePhone").ToString())
lvi.SubItems.Add(dr("EmployeeID").ToString())
lvwEmployees.Items.Add(lvi)
Next
End If

btnGetAsync.Enabled = True

End Sub

Most of the declarations are identical to the synchronous call, but note that the Web service (DataSetWS) is not instantiated in the declaration. Instead, the instance is retrieved from the asynchronous state parameter (ar). The returned DataSet (ds) is retrieved through the finalizing call to the Web service (EndGetEmployees). After filling the ListView the same way as before, the button is enabled again.

Interestingly, during the call between the execution of the Click event on the button and the call to GetAsyncReturn, the user is able to interact with the application as usual. This will bring a better user experience, even if the data retrieved is the same.

Integration Optimization

With the ability to make asynchronous calls to Web services, the user experience can be much more pleasant, but the amount of data transferred could still be too large for the bandwidth (or money) available. As an addition to the pure design-oriented actions that can be taken (like splitting up the data transferred into smaller parts), you could look into the possibility of compressing the data. The size of that topic, however, would require a separate article or white paper.

More information can be found on the following pages:

XML Web Services Developer Center
(https://msdn.microsoft.com/webservices/)

SOAP
(https://msdn.microsoft.com/soap/)

Working with Databases

This section provides more details on the Device Data to Server Data integration pattern previously explained. Most mobile applications share the requirements to retrieve data from remote databases, persist collected data locally, and push data back to remote databases. eMbedded Visual Basic provided a programming model based on ADOCE, a sibling of the server-side ADO object model. Similarly, the .NET Compact Framework exposes a subset of ADO.NET through the System.Data namespace. On the surface, the new programming model is not that different from ADOCE. Basic tasks such as getting a connection, working with collections of records and fields, and using transactions through the connection object work in very much the same way.

However, there are two noticeable changes in the data access landscape. Primarily, there is no managed provider for the local data store, sometimes called CEDB or Pocket Access. Microsoft instead provides managed providers for SQL Server CE 2.0 and for remote connections to server-side SQL Servers. Second, XML stored as local files can be used as a data store in scenarios of lower volumes of data; in other words SQL Server CE 2.0 might not be needed. The DataSet, an ADOCE Recordset equivalent, is used in both cases. Table 1 provides an overview of three available database strategies.

Table 1. Three available database strategies

    DataSet as XML in Local Files Local SQL Server
CE Database
Remote SQL
Server Database
Data Volume Lower Volumes of Data Medium Volumes of Data Large Volumes of Data Remote, Device uses Small Result set
Querying Simple, Offline Rich, Offline Rich, (Always) Online
Main Advantage Least Expensive Most Robust Local Data Solution, Bandwidth Efficient Real-time, No Replication Issues

When moving from eMbedded Visual Basic and SQL Server CE 1.0 / 1.1 to Visual Basic .NET and SQL Server CE 2.0, it can be helpful to know that the two database versions have the same file format and can peacefully coexist on one device.

Most types in the System.Data namespace relate to the DataSet class. The following figure illustrates how the DataSet implements XML reading and writing and usage of local and remote SQL Servers.

Figure 7. At the heart of System.Data

The DataSet

The DataSet class replaces the ADOCE Recordset, and, while doing so, adds more functionality. A DataSet can contain several result sets, called DataTable objects, which in turn can relate to each other with DataRelation and DataRelationCollection objects.

The following code illustrates opening a connection and recordset from SQL Server CE database using eMbedded Visual Basic:

' Variables
Dim provider As String
Dim datasource As String
Dim rs As ADOCE.Recordset
Dim cn As ADOCE.Connection

' Create ADOCE-objects
Set cn = CreateObject("ADOCE.Connection.3.1")
Set rs = CreateObject("ADOCE.Recordset.3.1")

' Set provider and datasource strings
provider = "Provider=Microsoft.SQLSERVER.OLEDB.CE.2.0"
datasource = "Data Source=\SQL\sample.sdf"
' Open connection
cn.Open provider & "; " & datasource

' Open recordset
rs.Open "SELECT SupplierID, CompanyName FROM Suppliers ORDER
BY CompanyName",cn,adOpenDynamic,adLockOptimistic

This is equivalent code in Visual Basic .NET:

' Variables
Dim cn As New SqlServerCe.SqlCeConnection
Dim ds As New DataSet
Dim da As New SqlServerCe.SqlCeDataAdapter
Dim cmd As New SqlServerCe.SqlCeCommand

' Set connectionstring, open connection
cn.ConnectionString = "Data Source=\SQL\sample.sdf"
cn.Open()

' Set command properties
cmd.CommandText = "SELECT SupplierID,CompanyName FROM
Suppliers ORDER BY CompanyName"
cmd.Connection = cn

' Set data adapter command
da.SelectCommand = cmd

' Populate dataset with datatable from adapter
da.Fill(ds,"Suppliers")

The Visual Basic .NET code can be made more compact by setting property values through constructor parameters when instantiating classes, something not possible in eMbedded Visual Basic. This is how it is done:

' Variables
Dim cn As New SqlServerCe.SqlCeConnection("Data
Source=\SQL\sample.sdf")
Dim ds As New DataSet
Dim da As New SqlServerCe.SqlCeDataAdapter("SELECT SupplierID,
CompanyName FROM Suppliers", cn)

' Open connection
cn.Open()

' Populate DataSet with DataTable from adapter
da.Fill(ds, "Suppliers")

The DataAdapter is used to insert, update, and delete data from a database. The easiest way to begin creating the necessary commands is to build a desktop Windows Forms application and create a data adapter by using the "Data Adapter Configuration Wizard". The wizard will create the appropriate code and commands.

A DataSet manages data and schema as XML documents and implements methods (WriteXml, ReadXml, WriteXmlSchema, and ReadXmlSchema) to read and write both data and schema. So, the DataSet can by and in itself realize the physical implementation of a local data store for lower data volumes.

The following two lines of code write the XML schema and data based on the DataSet:

ds.WriteXmlSchema("\SuppliersSchema.xsd")
ds.WriteXml("\Suppliers.xml")

The XML schema file looks like this:

<?xml version="1.0" standalone="yes" ?>
- <xs:schema id="NewDataSet" 
xmlns:xs="https://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xs:element name="NewDataSet" msdata:IsDataSet="true"
msdata:Locale="sv-SE">
- <xs:complexType>
- <xs:choice maxOccurs="unbounded">
- <xs:element name="Suppliers">
- <xs:complexType>
- <xs:sequence>
<xs:element name="SupplierID" type="xs:int" minOccurs="0" />
<xs:element name="CompanyName" type="xs:string"
minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>

The first few lines of XML in the data file:

<?xml version="1.0" standalone="yes" ?>
- <NewDataSet>
- <Suppliers>
<SupplierID>1</SupplierID>
<CompanyName>Aux joyeux ecclésiastiques</CompanyName>
</Suppliers>
- <Suppliers>
<SupplierID>2</SupplierID>
<CompanyName>New Orleans Cajun Delights</CompanyName>
</Suppliers>
- <Suppliers>
<SupplierID>3</SupplierID>
<CompanyName>Grandma Kelly's Homestead</CompanyName>
</Suppliers>
- <Suppliers>
<SupplierID>4</SupplierID>
<CompanyName>Tokyo Traders</CompanyName>
</Suppliers>
</NewDataSet>

The XML capabilities of the DataSet represent a major advantage over what is available in eMbedded Visual Basic. Moving the data from the database to the user interface using the DataSet is made quite simple. The following code illustrates how to clear the contents of combo box and then populate it with a DataSet:

' Bind DataSet to combo box and select first item
With cboSuppliers
.Items.Clear()
.DataSource = ds.Tables("Suppliers")
.ValueMember = "SupplierID"
.DisplayMember = "CompanyName"
.SelectedIndex = 0
End With

The DataReader

While the DataSet produces a disconnected result set, the DataReader is a connected, read, and forward-only cursor result set which provides higher performance. In essence, the DataReader behaves like an ADOCE recordset created using the following eMbedded Visual Basic code:

rs.CursorType = adOpenForwardOnly
rs.LockType = adLockReadOnly
rs.ActiveConnection = cn
rs.Open "SELECT SupplierID, CompanyName FROM Suppliers ORDER
BY CompanyName"

Note that the variable "cn" is an ADOCE Connection object. The equivalent code in Visual Basic .NET looks like this (including the instantiation of the connection object):

Dim cn As New SqlServerCe.SqlCeConnection("Data
Source=\SQL\sample.sdf")
cn.Open()
Dim dc As New SqlServerCe.SqlCeCommand("SELECT SupplierID,
CompanyName FROM Suppliers ORDER BY CompanyName", cn)
Dim dr As SqlServerCe.SqlCeDataReader = dc.ExecuteReader()

The following code shows how to loop through the DataReader data and populate a combo box. The code implements a generic procedure that accepts two parameters: a ComboBox reference and a SQL string. The procedure assumes that an open connection (cn) is available. An ArrayList, a new class in the .NET Compact Framework, is used to address the common challenge of populating the combo box with items with a hidden unique ID as well as a descriptive visible name.

Private Sub PopulateComboBox(ByRef ComboBox As ComboBox, ByVal
SQL As String)
' Generic sub that populates a combo with ID/Name

' Declarations
Dim IDNameArray As ArrayList = New ArrayList

' Clear combo box
With ComboBox
.Visible = False
.DataSource = Nothing
.Items.Clear()
End With

Try
Dim dc As New SqlServerCe.SqlCeCommand(SQL, cn)
Dim dr As SqlServerCe.SqlCeDataReader = dc.ExecuteReader()

' Loop through the reader
While dr.Read()
IDNameArray.Add(New IDName(Trim(dr("ID")),
Trim(dr("Name"))))
End While

' Set combo box properties
With ComboBox
.DataSource = IDNameArray
.DisplayMember = "Name"
.ValueMember = "ID"
.SelectedIndex = 0
End With

Catch ex As Exception
MessageBox.Show(ex.Message)
Finally
ComboBox.Visible = True
End Try

End Sub

The combo box gets populated by the ArrayList, which is built using item data from a custom class implementing two generic properties: ID and Name. The class looks like this:

Class IDName
Private _ID As String
Private _Name As String

Public Sub New(ByVal ID As String, ByVal Name As String)
MyBase.New()
_ID = ID
_Name = Name
End Sub

Public ReadOnly Property ID() As String
Get
Return (_ID)
End Get
End Property

Public ReadOnly Property Name() As String
Get
Return _Name
End Get
End Property

Public Overrides Function ToString() As String
Return _ID + " - " + _Name
End Function
End Class

Managing Local Databases

There are two methods of getting a SQL Server CE database in place on a device:

  • Distribute the database file with the setup
  • Create the database at runtime

To create a new database using eMbedded Visual Basic:

' Variables
Dim provider As String
Dim datasource As String
Dim ct As ADOXCE.Catalog

' Handle errors
On Error Resume Next

' Create ADOXCE Catalog
Set ct = CreateObject("ADOXCE.Catalog.3.1")

' Set provider and datasource strings
provider = "Provider=Microsoft.SQLSERVER.OLEDB.CE.2.0"
datasource = "Data Source=\SQL\sample.sdf"

' Create the database
ct.Create provider & "; " & datasource

' Check for error
If Err.Number <> 0 Then
' Put handling code here!
End If

Obviously, the database might already exist when it is created at runtime. There are two ways to manage this situation. First, the code can check whether or not the database file exists. If it does exist, then the code won't try creating a new one. Second, the code can manage the error that is raised when the code tries to create a database that already exists. In eMbedded Visual Basic code, this is done by using a preceding "On Error Resume Next", and checking the Err-object's Number property (Err.Number) after the Create method. To do the same thing in Visual Basic .NET, the more structured Try ... Catch ... Finally exception handling is used:

' Variables
Dim db As String = "\sample2.sdf"
Dim sqlEngine As New SqlServerCe.SqlCeEngine("Data Source=" +
db)
Try
' Create database
sqlEngine.CreateDatabase()
Catch ex As SqlServerCe.SqlCeException
' Put handling code here!
Finally
' Put code that executes both if CreateDatabase worked or
failed here!
cn.Open()
End Try

To first check for an existing database, and then create a new database using Visual Basic .NET:

' Variables
Dim db As String = "\sample.sdf"
Dim sqlEngine As New SqlServerCe.SqlCeEngine("Data Source=" +
db)

' Delete database if it already exists
If File.Exists(db) Then
File.Delete(db)
End If
0

' Create database
sqlEngine.CreateDatabase()

Once the database is created, it is time to create tables and start working with data.

The following code:

  • Creates the table Suppliers
  • Inserts two records
  • Updates one record and deletes one record
  • Manages data modification in transactions
' Variables
Dim db As String = "\sample.sdf"
Dim sqlEngine As New SqlServerCe.SqlCeEngine("Data Source=" +
db)
Dim cn As New SqlServerCe.SqlCeConnection("Data Source=" + db)
Dim cmd As New SqlServerCe.SqlCeCommand

' Delete database if it already exists
If File.Exists(db) Then
File.Delete (db)
End If

Try
' Create database
sqlEngine.CreateDatabase()
Catch
' Put handling code here!
Finally
' Put code that executes both if CreateDatabase worked or
failed here!
cn.Open()
End Try

' Create the table using a Command object
cmd.Connection = cn
cmd.CommandText = "CREATE TABLE Suppliers(SupplierID int
PRIMARY KEY, CompanyName nvarchar(50))"
cmd.ExecuteNonQuery()

Try
' Populate the table
cmd.CommandText = "INSERT INTO Suppliers(SupplierID,
CompanyName) VALUES (1, 'Best Foods')"
cmd.ExecuteNonQuery()
cmd.CommandText = "INSERT INTO Suppliers(SupplierID,
CompanyName) VALUES (2, 'Best Clothes')"
cmd.ExecuteNonQuery()
Catch ex As SqlServerCe.SqlCeException
cn.Close()
MsgBox("Could not insert data!" + ex.Message)
Exit Sub
Finally
End Try

Try
' Update and Delete!
cmd.CommandText = "UPDATE Suppliers SET CompanyName = 'Good
Foods' WHERE SupplierID = 1"
cmd.ExecuteNonQuery()
cmd.CommandText = "DELETE FROM Suppliers WHERE SupplierID =
2"
cmd.ExecuteNonQuery()
Catch ex As SqlServerCe.SqlCeException
MsgBox("Could not modify data!" + ex.Message)
Finally
cn.Close()
End Try

Using Remote Databases

There are two main methods of reaching remote databases from Pocket PCs using the .NET Compact Framework:

  • Multi-Tier Web Service Application: You can pass DataSets (XML) to server-side Web services, which in turn connect to remote databases. Or, you can leverage the built-in XML support in SQL Server 2000 using the server-side SQLXML Managed Classes (https://msdn.microsoft.com/library/default.asp?url=/library/en-us/sqlxml3/htm/dotnet\_9n03.asp).
  • Remote Database Application: Developing a remote database application can either be done by using the SQL Managed Provider in .NET Compact Framework, which enables direct database querying, something not available in eMbedded Visual Basic, or by using the SQL Server CE Managed Provider in .NET Compact Framework, which enables remote database table pushing and pulling and merge replication. Tight database-to-database integration provides better performance, but provides little to no ability to use business logic often implemented in Web services.

The following two MSDN articles discuss how to securely configure the remote server:

Security Models and Scenarios for SQL Server 2000 Windows CE Edition 2.0
(https://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsqlce/html/sqlce\_secmodelscen20.asp)

Troubleshooting SQL Server CE Connectivity Issues
(https://www.microsoft.com/sql/techinfo/administration/2000/CEconnectivitywp.asp)

eMbedded Visual Basic developers with experience in configuring SQL Server CE 1.x connectivity will be happy to see the SQL Server CE Connectivity Management console and the SQL Server CE Virtual Directory Creation Wizard (Figure 8).

Figure 8. SQL Server CE virtual directory creation wizard

Direct Database Querying

The components in the System.Data.SqlClient namespace (https://msdn.microsoft.com/library/default.asp?url=/library/en-us/sqlce/htm/\_lce\_sqlclient\_705.asp) are used to connect to, execute commands in, and retrieve results from remote SQL Servers. The following code illustrates how this can be done using a DataReader. The sample code lets the user enter part of a company name, which is used in the query against the Suppliers table in the Northwind database. Note that the SqlCommand object references a stored procedure that resides in the remote SQL Server.

' Variables
Dim cmd As SqlClient.SqlCommand
Dim dr As SqlClient.SqlDataReader
Dim cn As New System.Data.SqlClient.SqlConnection("user
id=sa;password=;initial
catalog=northwind;server=192.168.100.100")
Dim CompanyName As String

' Get company name to search for
CompanyName = txtCompanyName.Text

Try
' Open connection to remote server
cn.Open()
' Get suppliers
cmd = New System.Data.SqlClient.SqlCommand("getSuppliers " +
CompanyName, cn)
dr = cmd.ExecuteReader()

' Populate the combo box
cboSuppliers.Items.Clear()
While dr.Read()
cboSuppliers.Items.Add(dr("CompanyName"))
End While

' Display the first supplier
cboSuppliers.SelectedIndex = 0

Catch ex As SqlClient.SqlException
MsgBox(ex.Message)
Finally
dr.Close()
cn.Close()
End Try

The stored procedure getSuppliers looks like this:

CREATE PROCEDURE getSuppliers
@CompanyName NVARCHAR(50)
AS
SELECT SupplierID, CompanyName
FROM Suppliers
WHERE CompanyName LIKE '%' + @CompanyName + '%'

Many applications built using eMbedded Visual Basic use the SQL Server CE COM components Remote Data Access (RDA) and Merge Replication (MR) to synchronize data between devices and remote servers. The same feature set is implemented in the SqlCeRemoteDataAccess and SqlCeReplication namespaces of the .NET Compact Framework.

A detailed step-by-step of these namespaces can be found in the article SQL Server 2000 Windows CE Edition and the .NET Compact Framework
(https://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsqlce/html/sqlwince.asp).

More information can be found on the following pages:

.NET Compact Framework Data Providers (https://msdn.microsoft.com/library/default.asp?url=/library/enus/sqlce/htm/\_lce\_sql\_server\_ce\_manage\_provider.asp)

Microsoft SQL Server 2000 Windows® CE Edition Home
(https://www.microsoft.com/sql/CE/default.asp)

Converting an eMbedded Visual Basic 3.0 Application from SQL Server CE 1.x to SQL Server CE 2.0
(https://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsqlce/html/sqlce\_convertssce10to20\_.asp)

Deployment and Distribution

When the application is built, it needs to be deployed and distributed in a convenient way. In eMbedded Visual Basic, there is an Application Install Wizard that packages the project and needed resources in an installation executable (Setup.msi). In Visual Basic .NET, things are not as obvious.

Creating Cabinet Files

When Application Installation Wizard (included with eMbedded Visual Basic) creates an installation, it always includes a cabinet (.cab) file that is run on the device to make the actual installation. The files generated (Setup.msi, Setup.ini, and the cabinet file) end up in a folder named CD1. In Visual Basic .NET, there's no way to create a complete PC installation, but there's built-in support for creating the cabinet file. When the project is loaded and built (probably using the "Release" configuration) in Visual Basic .NET, choose menu option Build and submenu option Build Cab File, and the cabinet files for a number of processor types are generated. The cabinet files are generated in a folder beneath the project folder (<project path>/cab/Release).

For installation on a Pocket PC, use the cabinet file with the "_PPC.ARM.CAB" suffix. This is the Pocket PC installation, and if this file is copied to a device and tapped on from within File Explorer (on the device), the application will be installed. This is also the file that you can make available on a Web page to have connected Pocket PC users start their installations directly from within Internet Explorer for the Pocket PC.

However, if you take the generated cabinet file and install it on a device, you will notice that the application name is identical to the project name used in Visual Basic .NET. Also, the company name is set to "My Company," which may not be applicable to your company. To customize the creation of the cabinet files you have to look in another folder located beneath the project folder (at <project path>/obj/Release). There you will find the setup information file (.inf) used to create the cabinet files. In that file, there are entries for the application and company name used. Here's a sample extract from such a file (excluded sections marked with periods):

[Version]
Signature="$Windows NT$"
Provider="CompanyName"
CESignature="$Windows CE$"

[CEStrings]
AppName="ApplicationName"
InstallDir=%CE1%\%AppName%
.
.
[DefaultInstall]
CEShortcuts=Shortcuts
CopyFiles=Files.Common
.
.
[SourceDisksFiles]
ProjectName.exe=1
.
.
[Files.Common]
ProjectName.exe,,,0
.
.
[Shortcuts]
ApplicationName,0,ProjectName.exe,%CE11%

The name of the Visual Basic .NET project to generate this file was "ProjectName" and, in the generated file, the Provider value in the Version section was changed from "My Company" to "CompanyName". Also, the AppName value in the CEStrings section was changed from "ProjectName" to "ApplicationName". The same change was made in the last Shortcuts section (the first value in the list is the name of the shortcut generated). This is just a brief introduction, as the .inf files are quite a large topic (for more information, see the Visual Basic .NET help file index topic "inf files for device projects").

When updates are made to the .inf file, you can generate a new set of cabinet files by running the batch file (BuildCab.bat) found in the same folder as the .inf file.

More information can be found on the following page:

Microsoft Windows CE 3.0 Installation and Configuration Guide
(https://msdn.microsoft.com/library/en-us/wcesetup/htm/\_wcesdk\_installation\_and\_configuration\_guide.asp)

ActiveSync Setup

As previously mentioned, the Application Installation Wizard included with the eMbedded Visual Basic creates a folder (CD1) with the installation files. Included in those files are the installation application run on the PC (Setup.msi) and a configuration settings file (Setup.ini). To create a PC installation from a Visual Basic .NET project, you can use these two files, provided you have eMbedded Visual Basic installed. Building on the sample above, the setup.ini file should look something like this:

[General]
Component=ProjectName
DefaultDirectory=ApplicationName
CabCount=1
Cab0=ProjectName_PPC.ARM.CAB
Description=CompanyName ApplicationName

For a Pocket PC installation, the only cabinet file that we need is the one with the "_PPC.ARM.CAB" suffix. The three files (Setup.msi, Setup.ini, and ProjectName_PPC.ARM.CAB) are what you need to send to your users to install the application from a PC via ActiveSync. To make things more convenient for the end user, you might even include the three files into a self-extracting archive file that starts the installation application (Setup.msi) on extraction. This can be done using most of the commercial archive tools (like WinZip and WinRAR).

Conclusion

The move from eMbedded Visual Basic to Visual Basic .NET for Pocket PC development is an important advancement. Development related to user interface, application navigation, system integration, database management, system integration, and solution deployment has been improved. The environment, language, and platform are re-engineered from the ground up, leading to robust features and coding efficiency—making development easier and improving the connectivity of mobile applications.