Welcome to MSDN Blogs Sign in | Join | Help

Windows Vista cannot obtain an IP address from certain routers or from certain non-Microsoft DHCP servers

I recently had a need to prove that my home router was not the cause of some networking issues.  So, I pulled an older router out of the closet and hooked it up.  I quickly noticed that there was a problem.  My Windows XP machine could obtain an IP address, but my Windows Vista machine could not.  I replaced the old router with the original one, and both machines started working again.  However, I needed to get the OLD router to work, so I had to do some digging.

I quickly found KB 928233 (http://support.microsoft.com/kb/928233/en-us).  This article discusses a difference in the way that XP and Vista handle the DHCP BROADCAST flag.  In XP, this flag is disabled by default.  In Vista, however, it is enabled by default.  My old router couldn't properly handle this flag, so it wouldn't give my Vista box an IP address.  I followed the instructions in the KB article, and everything started working.

Later on, and for other reasons, I decided to purchase a newer router.  Just for fun, I decided to set Vista back to its default behavior.  Sure enough, Vista had no problem with the new router.  This problem seems to occur only with older routers that cannot process the BROADCAST flag.

Posted by Jerry.Dixon | 1 Comments

Password-protecting Compressed Folders in Windows XP

During a conversation today, a colleague of mine pointed out something that I didn't know:  A Compressed Folder in Windows XP can be protected with a password.  Like many of you, I've been using XP for years, and yet I somehow missed this.  Therefore, I'm going to write it down!

You can read all about it in How To Use Compressed (Zipped) Folders in Windows XP.  Check out the section entitled "How to Use Password Protection".

I've been using Vista for over a year now, so I immediately tried this there.  Interestingly, I couldn't find a way to do it.  You can encrypt the Compressed Folder, but you can't simply password-protect it.  Of course, encryption is much more secure, and Vista emphasizes security.  Still, it's good to know.

Posted by Jerry.Dixon | 1 Comments
Filed under: ,

Social Bookmarks

Microsoft has launched a new web site where IT Professionals can store and share Internet bookmarks.  Each bookmark can be tagged and given a friendly name and description, which makes it pretty easy for others to search through the list of bookmarks and find the information that they need.  You can read the official FAQ here, but the hope is that this will become a valuable resource for those needing good, reliable information about various technologies.

(By the way, I've given you the links for MSDN.  However, TechNet is doing this too.  You can visit their web site and FAQ, but the information seems to be identical.)

Since I work in Premier Support, I am always hitting various MSDN and TechNet pages. As you might expect, I've found that there are some pages that I hit over and over again.  Therefore, I'm going to start migrating some of my private browser-based bookmarks over to my public MSDN bookmark page.  You are welcome to take a peek, and I hope that you find this useful.

Posted by Jerry.Dixon | 0 Comments
Filed under:

Running SQL Reporting Services on Vista

I recently decided to fire up SQL Server 2005 Reporting Services on my Vista laptop.  I installed SQL Server 2005 (with SP2 of course!) and made sure that I used the SQL Server User Provisioning tool to give myself full rights.  I then launched the Report Manager, and stopped dead in my tracks.  I couldn't access the Report Manger.  Hmm.

I am running Vista with ALL of the security features enabled.  I do this for two reasons.  First, it is more secure.  Second, I want to learn how to work in the new environment without turning security features off.  I knew that I had a security issue, but I didn't want to "fix" the problem by removing security.  Therefore, I launched an elevated IE instance and added my credentials to the Report Manager.  I gave myself full rights to everything.  That should do it, right?  Nope!  I still couldn't access Report Manager from a standard IE instance.  Hmm.

I did some more research and found this blog post:

http://www.jameskovacs.com/blog/ReportingServicesOnWindowsVistaRedux.aspx

My problem was solved.  All I needed to do was to add the SSRS site to my trusted sites list.  This allowed the Report Manager to run in an un-elevated browser instance, but with protected mode turned off.  That was the trick, and it all works now.

I hope this helps!

Posted by Jerry.Dixon | 1 Comments
Filed under: ,

Microsoft SQL Server Data Services

Today at MIX, Microsoft unveiled Microsoft SQL Server™ Data Services (SSDS), a Web-facing utility-based service designed for businesses and developers that need scalable, highly available, secure and easily programmable on-demand data storage with robust database query capabilities.

What SSDS offers:

  • Virtually limitless, on-demand scalability. Customers pay only for the resources actually consumed
  • Reduced costs.  Customers use the service with minimal up-front infrastructure and operational cost. Businesses can minimize their initial investment in hardware and software and the on-going cost for storage administration, scale maintenance.
  • Universal access.  SSDS supports simple web-programming interfaces like SOAP and REST for quick provisioning of web applications. Primary wire format is XML for data interchange.These easy to use, standards-based interfaces enable developers to focus on innovating with data quickly.

Microsoft SSDS is built upon existing Microsoft SQL Server relational database technologies, and will include a strong service level agreement (SLA) for business continuity. This will cover high availability, performance, and protection against data loss.

For more information, check out the SSDS Home Page, or The Data Platform Insider.

Posted by Jerry.Dixon | 1 Comments
Filed under:

The data provider required to connect to the local data file cannot be found

I've been trying to get SQL Compact 3.5 working in Visual Studio 2008.  Since SQL Compact 3.5 ships with Visual Studio 2008, I thought that this wouldn't be a problem.  All I needed to do was to create a new Windows Forms project and add a Local Database item.  Sadly, this didn't work.  After added the local database, I received an error:

The data provider required to connect to the local data file cannot be found

I couldn't find any reference to this exact message anywhere, but I did find KB 945371.  Although the article refers to a different problem, it provided a solution to this one as well. 

It seems that when you install VS 2005 and VS 2008 side-by-side, some file paths are incorrectly specified.  The solution is to reinstall the SQL Compact Design tools from the installation DVD.  (I also reinstalled the Desktop and Device runtimes as well, as I had uninstalled these while trying to solve the problem.)

After reinstalling these bits, SQL Compact works just fine.

Posted by Jerry.Dixon | 1 Comments
Filed under: ,

VB 6 Support is Winding Down

Ah.... VB 6.  It's been one of my favorite languages.  Over the years, I've created all kinds of programs with it; games, business applications, hardware control systems, etc.  Although I was among the first to admit its faults, I was also one of the first to proclaim its strengths and to defend its value.  Even after moving to .NET, I've always kept VB 6 close at hand.  Sometimes I did this because of necessity, such as when working for companies running multiple large VB6 applications.  At other times, I did it because it was a familiar and trusted tool.  Mostly, though, I think I kept it around because it had always been there, and it just felt right.

Looking back, I can't help but notice that I've been using it less and less.  In fact, I haven't written any new VB 6 programs in 3 or 4 years.  Instead, I've been been using C# and VB.NET.  However, many developers HAVE continued to use VB 6, as have many companies. It is for those developers and companies that I write this post.

Support for the Visual Basic 6 IDE will cease in April 2008.

After this date, Microsoft will no longer support issues dealing with the IDE or with Visual Basic source code.  Runtime issues will still be supported, as the VB 6 runtime is supported along with the operating system with which it shipped.  However, for most practical purposes, April marks the beginning of the end for VB 6.  Developers should seriously consider migrating their applications to another supported environment, probably .NET.

Obviously, this cannot be done overnight, so you should start making your plans now. 

Posted by Jerry.Dixon | 1 Comments
Filed under:

Web Part Connections in WSS 3.0 (Part 4)

In part 1 of this series, I showed how to make one web part accept data from another.  In part 2, I showed that this can be extended so that one web part can provide data for many.  In part 3, which I thought was going to be the last part, I showed how a single web part can accept data from multiple web parts.  Now, in the fourth part of my three-part series, I'm going to show how data can be passed from one web part to another in a sort of chain.

I have three web parts; NumberPicker, NumberDoubler, and NumberTripler.  I want the data to flow like this:

NumberPicker --> NumberDoubler --> NumberTripler

The user will use NumberPicker to select a number from one to five.  This number is passed to NumberDoubler, which doubles the number and displays it.  This doubled number is then passed to NumberTripler, which triples the number and displays the final result.  The important point here is that data is received from one part and provided to the next, and specifically that the data provided depends upon the data recieved. Here's the code that I used:

INumber
Public Interface INumber

    ReadOnly Property Number() As Integer

End Interface

NumberPicker
Imports System
Imports System.ComponentModel
Imports System.Web.UI.HtmlControls
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts

Public Class NumberPicker
    Inherits WebPart
    Implements INumber

    Private _NumberList As DropDownList

    Public Sub New()

        Me.ExportMode = WebPartExportMode.All

    End Sub

    Protected Overrides Sub CreateChildControls()
        MyBase.CreateChildControls()

        Dim t As Table = Nothing
        Dim tr As TableRow = Nothing
        Dim td As TableCell = Nothing

        Try
            _NumberList = New DropDownList

            With _NumberList
                .AutoPostBack = True

                .Items.Clear()

                .Items.Add(New ListItem("One", 1))
                .Items.Add(New ListItem("Two", 2))
                .Items.Add(New ListItem("Three", 3))
                .Items.Add(New ListItem("Four", 4))
                .Items.Add(New ListItem("Five", 5))

                .SelectedIndex = 0
            End With

            t = New Table
            tr = New TableRow

            td = New TableCell
            td.Text = "Number: "
            tr.Controls.Add(td)

            td = New TableCell
            td.Controls.Add(_NumberList)
            tr.Controls.Add(td)

            t.Controls.Add(tr)

            Me.Controls.Add(t)
        Catch ex As Exception
            Me.Controls.Clear()

            Dim msg As New Literal()
            msg.Text = ex.Message
            Me.Controls.Add(msg)
        End Try

    End Sub

    <ConnectionProvider("Selected Number")> _
    Public Function GetNumberInterface() As INumber
        Return Me
    End Function

    Public ReadOnly Property Number() As Integer Implements INumber.Number
        Get
            Return _NumberList.SelectedValue
        End Get
    End Property

End Class

NumberDoubler
Imports System
Imports System.ComponentModel
Imports System.Web.UI.HtmlControls
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts

Public Class NumberDoubler
    Inherits WebPart
    Implements INumber

    Protected _numberInterface As INumber = Nothing
    Protected _number As Integer = 0
    Protected _result As Integer = 0

    Protected _headerMessage As Literal = Nothing

    Public Sub New()

        Me.ExportMode = WebPartExportMode.All

    End Sub

    Protected Overrides Sub CreateChildControls()
        MyBase.CreateChildControls()

        Try
            _headerMessage = New Literal
            Me.Controls.Add(_headerMessage)
        Catch ex As Exception
            Me.Controls.Clear()

            Dim msg As New Literal()
            msg.Text = ex.Message
            Me.Controls.Add(msg)
        End Try

    End Sub

    Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
        MyBase.OnPreRender(e)

        If _numberInterface IsNot Nothing Then
            _number = _numberInterface.Number
            _result = 2 * _number
        End If

    End Sub

    Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter)

        If _headerMessage IsNot Nothing AndAlso _number > 0 Then
            _headerMessage.Text = _result
        End If

        MyBase.RenderContents(writer)

    End Sub

    <ConnectionConsumer("Selected Number", "1")> _
    Public Sub AcceptNumberInterface(ByVal numberInterface As INumber)
        _numberInterface = numberInterface
    End Sub

    <ConnectionProvider("Doubled Number", "2")> _
    Public Function GetNumberInterface() As INumber
        Return Me
    End Function

    Public ReadOnly Property Number() As Integer Implements INumber.Number
        Get
            Return _result
        End Get
    End Property
End Class

NumberTripler
Imports System
Imports System.ComponentModel
Imports System.Web.UI.HtmlControls
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts

Public Class NumberTripler
    Inherits WebPart

    Protected _numberInterface As INumber = Nothing
    Protected _number As Integer = 0
    Protected _result As Integer = 0

    Protected _headerMessage As Literal = Nothing

    Public Sub New()

        Me.ExportMode = WebPartExportMode.All

    End Sub

    Protected Overrides Sub CreateChildControls()
        MyBase.CreateChildControls()

        Try
            _headerMessage = New Literal
            Me.Controls.Add(_headerMessage)
        Catch ex As Exception
            Me.Controls.Clear()

            Dim msg As New Literal()
            msg.Text = ex.Message
            Me.Controls.Add(msg)
        End Try

    End Sub

    Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
        MyBase.OnPreRender(e)

        If _numberInterface IsNot Nothing Then
            _number = _numberInterface.Number
            _result = 3 * _number
        End If

    End Sub

    Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter)

        If _headerMessage IsNot Nothing AndAlso _number > 0 Then
            _headerMessage.Text = _result
        End If

        MyBase.RenderContents(writer)

    End Sub

    <ConnectionConsumer("Doubled Number", "3")> _
    Public Sub AcceptNumberInterface(ByVal numberInterface As INumber)
        _numberInterface = numberInterface
    End Sub

End Class

The real trick here is that the numbers are calculated during the OnPreRender event.  We have to wait long enough to ensure that the NumberPicker web part has created its internal controls, which is where the original data resides.  We also need to act early enough to allow the data to be passed along from part to part.  If you are not careful, one of the web parts may be rendered before the data has been passed to it.

I hope this helps.

Posted by Jerry.Dixon | 2 Comments
Filed under: ,

Virus Scanners and ASP.NET apps don't always mix

In a previous post, I talked about Alternate Data Streams and how some virus-scanning engines use them for tracking purposes.  When this happens, the file is changed.  It doesn't matter that the "real" data hasn't changed.  The system still knows that a change has occurred.  When this happens to a file inside an ASP.NET web site, this can cause some interesting side effects.

First, let's review File Change Notification in ASP.NET 2.0. 

ASP.NET receives a notification whenever a file in a virtual folder is changed.  This includes updates, inserts, and deletions.  When a change occurs, the application is restarted.  This causes a loss of application state, including session variables, cached data, and so on.  See these sites for more info:

http://blogs.msdn.com/toddca/archive/2006/07/17/update-deleting-asp-net-2-0-application-sub-directories-shuts-down-the-appdomain.aspx

http://weblogs.asp.net/owscott/archive/2006/02/21/ASP.NET-v2.0-_2D00_-AppDomain-recycles_2C00_-more-common-than-before.aspx

Now, let's add virus scanning to the mix.  Since some scanning engines insert tracking information inside a file's alternate data stream, and since this action can be considered a change to the file, this can cause an application restart.  Yes, that's right.  Using a virus scanner to scan your application files for viruses can cause your web application to restart.  Refer to this KB article: http://support.microsoft.com/kb/312592.

I'm seeing more of this lately, so I though I'd put the word out.  Test your virus scanner against your web applications before you place them in production.  If you have this problem, you can exclude the application folders from scanning or you can try the steps listed in this KB article: http://support.microsoft.com/kb/911272.  I'm not really happy with either solution, so I'm not making any recommendations.  However, I do urge you to perform a test before you go live.

Posted by Jerry.Dixon | 1 Comments
Filed under: ,

Alternate Data Streams

I want to recap a topic that has been around for a while:  Alternate Data Streams.  This topic has resurfaced again in an interesting way.  I'm going to tell you about it in later post.  However, I thought I'd explain Alternate Data Streams again, just in case some of you don't know what they are.  The later post will then make more sense.

So, what are Alternate Data Streams?  Well, it happens that NTFS files can have more than one set of data stored inside them.  (You cross-platform guys might know about "resource forks."  This is a similar concept.)  Everyone knows about the primary stream.  This is the data that we normally associate with files.  It is the data that we see when we open the file, and it accounts for the file size numbers that we see in file listings.  However, we can "hide" other sets of data in a file too.  Let's look at a simple example. (It is very important that you use the same commands that I do.  Not all console commands are aware of alternate data streams.)

Open a command window and try the following:

Create a file called "names.txt" that contains my name:
echo Jerry > names.txt

Prove that the file contains my name:
more < names.txt

Get the size of the file:
dir names.txt

You should see that the file contains 8 bytes.  That's 5 bytes for my name, 2 bytes for the CRLF, and 1 byte for the space.  (Look closely at the command.  There's a space between my name and the ">" character.)  This is all very normal.  Now let's mess with some alternate streams.

Add my wife's name to the file, in an alternate data stream:
echo Tammy > names.txt:wife

Prove that the file contains both names:
more < names.txt
more < names.txt:wife

Get the size of the file:
dir names.txt

There are now two separate sets of data in this one file.  One, the default, contains my name.  The alternate stream contains my wife's name.  However, the directory listing shows 8 bytes, not the 16 that are actually there.  The dir command is not aware of alternate data streams, so it doesn't account for them in its listing.  Some of the file's data is now hidden.

Let go one step further.  Add my son's name:
echo > Evan names.txt:son

Check out the results as we did before.  You now have 23 bytes of data contained in three steams, but the directory listing still shows 8.

Neat, huh?

I'll leave the rest to you.  Remember that we are talking data streams here, not text.  There is a demo on the web that shows how to hide calc.exe inside sol.exe.  You can execute either program, too.  The possibilities run deep.

Now, why did I bring this up?  I'll tell you more in another post.  However, for right now, you should know that many programs take advantage of these streams.  For example, some virus scanners hide tracking data in the files that they scan.  Some viruses hide themselves in there too.  Stay turned for more.

Posted by Jerry.Dixon | 8 Comments
Filed under:

Web Part Connections in WSS 3.0 (Part 3)

In part 1 of this series, I showed how to make one web part accept data from another.  In part 2, I showed that this can be extended so that one web part can provide data for many.  Now, in this final post of the series, I'll show how a single web part can accept data from multiple web parts.  As you will see, we need to make only a few changes for this to happen.

Let's continue from where we left off.  Our page now has three web parts.  The first, HanoiDisks, allows the user to select the number of disks that should be used for our Towers of Hanoi puzzle.  The second web part, HanoiSteps, displays the steps needed to solve the puzzle.  The third web part, HanoiCount, displays the number of steps that will be needed to complete the puzzle.  Now we'll add a fourth web part.  This web part, HanoiColor, will allow the user to select the font color used by the HanoiCount web part.  HanoiCount will then be using data from both the the HanoiColor and the HanoiDisk web parts.

So far, we've been passing around instances of the IDisks interface.  We're now going to pass color information, so we need a new interface:

IColor.cb 

Public Interface IColor

    ReadOnly Property Color() As Integer

End Interface

This interface will allow a color number to be passed from HanoiColor to HanoiCount.  First, lets look at the new HanoiColor web part:

HanoiColor.vb 

Imports System
Imports System.Drawing
Imports System.ComponentModel
Imports System.Web.UI.HtmlControls
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts

Public Class HanoiColor
    Inherits WebPart
    Implements IColor

    Protected _colorList As DropDownList = Nothing

    Protected Overrides Sub CreateChildControls()
        MyBase.CreateChildControls()

        Try
            _colorList = New DropDownList

            With _colorList
                .AutoPostBack = True

                .Items.Clear()

                .Items.Add(New ListItem("Black", 0))
                .Items.Add(New ListItem("Blue", 1))
                .Items.Add(New ListItem("Green", 2))
                .Items.Add(New ListItem("Red", 3))

                .SelectedIndex = 0
            End With

            Dim lit As New Literal()
            lit.Text = "Text Color: "
            Me.Controls.Add(lit)

            Me.Controls.Add(_colorList)
        Catch ex As Exception
            Me.Controls.Clear()

            Dim msg As New Literal()
            msg.Text = ex.Message
            Me.Controls.Add(msg)
        End Try

    End Sub

    <ConnectionProvider("Text Color")> _
    Public Function GetColorInterface() As IColor
        Return Me
    End Function

    Public ReadOnly Property Color() As Integer Implements IColor.Color
        Get
            Return _colorList.SelectedValue
        End Get
    End Property
End Class

By now, you should recognize all of the features of this code.  The GetColorInterface method provides an instance of the IColor interface, and uses the ConnectionProvider attribute to communicate this fact to WSS.

Now, let's look at the HanoiCount web part.  It now needs to accept data from two different sources, so some code changes are needed:

HanoiCount.vb

Imports System
Imports System.Drawing
Imports System.ComponentModel
Imports System.Web.UI.HtmlControls
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts

Public Class HanoiCount
    Inherits WebPart

    Protected _diskInterface As IDisks = Nothing
    Protected _disks As Integer = 0

    Protected _colorInterface As IColor = Nothing
    Protected _color As String = "#000000;"

    Protected _headerMessage As Literal = Nothing

    Protected Overrides Sub CreateChildControls()
        MyBase.CreateChildControls()

        Try
            _headerMessage = New Literal
            Me.Controls.Add(_headerMessage)
        Catch ex As Exception
            Me.Controls.Clear()

            Dim msg As New Literal()
            msg.Text = ex.Message
            Me.Controls.Add(msg)
        End Try

    End Sub

    Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
        MyBase.OnPreRender(e)

        If _diskInterface IsNot Nothing Then
            _disks = _diskInterface.NumberOfDisks
        End If

        If _colorInterface IsNot Nothing Then
            _color = GetColorFromNumber(_colorInterface.Color)
        End If

    End Sub

    Private Function GetColorFromNumber(ByVal colorNumber As Integer) As String

        Dim selectedColor As String = "#000000;"

        Select Case colorNumber
            Case 0
                selectedColor = "#000000;"
            Case 1
                selectedColor = "#0000FF;"
            Case 2
                selectedColor = "#00FF00;"
            Case 3
                selectedColor = "#FF0000;"
        End Select

        Return selectedColor

    End Function

    Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter)

        If _headerMessage IsNot Nothing AndAlso _disks > 1 Then
            Dim steps As Integer = 2 ^ _disks - 1
            With _headerMessage
                .Text = "<span style=""color:" & _color & """>"
                .Text &= "There are " & steps & " steps for " & _disks & " disks."
                .Text &= "</span>"
            End With
        End If

        MyBase.RenderContents(writer)

    End Sub

    <ConnectionConsumer("Number of Disks", "1")> _
    Public Sub AcceptDiskInterface(ByVal diskInterface As IDisks)
        _diskInterface = diskInterface
    End Sub

    <ConnectionConsumer("Text Color", "2")> _
    Public Sub AcceptColorInterface(ByVal colorInterface As IColor)
        _colorInterface = colorInterface
    End Sub

End Class

Notice that two interfaces are used here.  This fact is reflected throughout most of the code.  Also notice that there are two methods that accept interfaces; AcceptDiskInterface and AcceptColorInterface.  Look closely at the ConnectionConsumer attributes.  Up until now, I've supplied only one parameter to the attribute constructor.  This works just fine when there is only one ConnectionConsumer or ConnectionProvider attribute used by a web part.  When you have more than one, however, you need to also supply a unique identifier for each attribute.  This is the real trick to using multiple web part connections.

At this point, we've written four web parts that communicate with each other in various ways:

  • HanoiCount - provides an instance of the IColor interface to the HanoiCount web part
  • HanoiDisks - provides an instance of the IDisks interface to the HanoiCount and HanoiSteps web parts
  • HanoiCount - accepts an instance of the IColor interface from the HanoiColor web part, and an instance of the IDisks interface from the HanoiDisks web part
  • HanoiSteps - accepts an instance of the IDisks interface from the HanoiDisks web part

I leave it to the reader to create a final web part; one that is both a provider and a consumer of data.  The can be accomplished by using the same techniques presented in this series of posts.

I hope these posts have helped you get started with web part communication.  This can be a very useful feature of WSS 3.0.

Posted by Jerry.Dixon | 11 Comments
Filed under: ,

Web Part Connections in WSS 3.0 (Part 2)

In part 1 of this series, I showed how to make one web part accept data from another.  In this part, I'll show how one web part can send data to two different web parts. 

Let's add another web part.  We already have a web part that allows the user to select the number of disks to use in our Towers of Hanoi puzzle.  We also have a web part that displays the steps needed to solve the puzzle for that number of disks.  Now, let's add a web part that displays the number of steps the are needed.  This part will get it's data from the HanoiDisks web part, just like the HanoiSteps web part does.

HanoiCount.vb 

Imports System
Imports System.Drawing
Imports System.ComponentModel
Imports System.Web.UI.HtmlControls
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts

Public Class HanoiCount
    Inherits WebPart

    Protected _diskInterface As IDisks = Nothing
    Protected _disks As Integer = 0

    Protected _headerMessage As Literal = Nothing

    Protected Overrides Sub CreateChildControls()
        MyBase.CreateChildControls()

        Try
            _headerMessage = New Literal
            Me.Controls.Add(_headerMessage)
        Catch ex As Exception
            Me.Controls.Clear()

            Dim msg As New Literal()
            msg.Text = ex.Message
            Me.Controls.Add(msg)
        End Try

    End Sub

    Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
        MyBase.OnPreRender(e)

        If _diskInterface IsNot Nothing Then
            _disks = _diskInterface.NumberOfDisks
        End If

    End Sub

    Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter)

        If _headerMessage IsNot Nothing AndAlso _disks > 1 Then
            Dim steps As Integer = 2 ^ _disks - 1
            With _headerMessage
                .Text = "There are " & steps & " steps for " & _disks & " disks."
            End With
        End If

        MyBase.RenderContents(writer)

    End Sub

    <ConnectionConsumer("Number of Disks")> _
    Public Sub AcceptDiskInterface(ByVal diskInterface As IDisks)
        _diskInterface = diskInterface
    End Sub

End Class

If you read part 1, then this should look very familiar.  In fact, there is no real difference between a web part providing data to one web part, and a web part providing data to many web parts.  Add this web part to the same page as the HanoiDisks and HanoiSteps web parts from part 1, and you see what I mean.

In my next post, I'll show you how to do something that IS a bit different.  I'll show you how one web part can accept data from two different web parts.

Posted by Jerry.Dixon | 3 Comments
Filed under: ,

Web Part Connections in WSS 3.0

Josh Holmes recently asked me to present a couple of small demos for an ArcReady event in Memphis.  While looking through my material, I discovered a couple of web parts that I had written a while ago.  I wrote these web parts in order to investigate how to send data from one part to another.  They illustrate how easy it is to set up Web Part Connections in WSS 3.0, so I thought I'd share them here.

In this post, I'll show how to send data from one web part to another.  Later posts will demonstrate how one web part can provide data to multiple web parts, how one web part can consume data from multiple parts, and how a web part can be a provider and a consumer at the same time.  As you will see, with WSS 3.0, this is very easy to do.

Now, programs need a purpose, and web parts are no different.  These web parts provide different types of information about the Towers of Hanoi puzzle.  In this post, I'll use two web parts.  One web part contains a drop-down list that can be used to specify the number of disks used for the puzzle.  The second web part contains a list box that enumerates the steps needed to solve the puzzle with the number of disks specified in the first web part.  For this to work properly, the second web part must obtain data from the first web part.  So, how does this work?

First, data is exchanged via interfaces. In this case, the interface is called IDisks:

IDisks.vb

Public Interface IDisks

    ReadOnly Property NumberOfDisks() As Integer

End Interface

This interface will be used to pass the selected number of disks from one web part to the other.

The first web part will allow the user to select the number of disks that should be used for the puzzle.  It is called HanoiDisks:

HanoiDisks.vb 

Imports System
Imports System.ComponentModel
Imports System.Web.UI.HtmlControls
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts

Public Class HanoiDisks
    Inherits WebPart
    Implements IDisks

    Protected diskList As DropDownList = Nothing

    Protected Overrides Sub CreateChildControls()
        MyBase.CreateChildControls()

        Dim t As Table = Nothing
        Dim tr As TableRow = Nothing
        Dim td As TableCell = Nothing

        Try
            diskList = New DropDownList

            With diskList
                .AutoPostBack = True

                .Items.Clear()

                .Items.Add(New ListItem("Three", 3))
                .Items.Add(New ListItem("Four", 4))
                .Items.Add(New ListItem("Five", 5))
                .Items.Add(New ListItem("Six", 6))
                .Items.Add(New ListItem("Seven", 7))
                .Items.Add(New ListItem("Eight", 8))

                .SelectedIndex = 0
            End With

            t = New Table
            tr = New TableRow

            td = New TableCell
            td.Text = "Number of Disks: "
            tr.Controls.Add(td)

            td = New TableCell
            td.Controls.Add(diskList)
            tr.Controls.Add(td)

            t.Controls.Add(tr)

            Me.Controls.Add(t)
        Catch ex As Exception
            Me.Controls.Clear()

            Dim msg As New Literal()
            msg.Text = ex.Message
            Me.Controls.Add(msg)
        End Try

    End Sub

    <ConnectionProvider("Number of Disks")> _
    Public Function GetDiskInterface() As IDisks
        Return Me
    End Function

    Public ReadOnly Property NumberOfDisks() As Integer Implements IDisks.NumberOfDisks
        Get
            Return diskList.SelectedValue
        End Get
    End Property

End Class

Notice that HanoiDisks inherits from WebPart.  This is the standard .NET 2.0 WebPart class, and it performs most of the work for us.  All we have to do is override CreateChildControls to add our UI.  The HanoiDisks class also implements the IDisks interface that was mentioned earlier.  It returns the value of the selected item in the dropdown list in our UI.

The GetDiskInterface method is the key to the web part communication.  It returns the HanoiDisks class as an instance of the IDisks interface.  It uses the ConnectionProvider attribute to inform WSS that this method provides data for another web part.  WSS does the rest.

Now let's look at the HanoiSteps web part, which lists the steps needed to solve the puzzle.

HanoiSteps.vb 

Imports System
Imports System.ComponentModel
Imports System.Web.UI.HtmlControls
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts

Public Class HanoiSteps
    Inherits WebPart

    Protected _diskInterface As IDisks = Nothing
    Protected _disks As Integer = 0
    Protected _stepList As ListBox = Nothing
    Protected _headerMessage As Literal = Nothing

    Protected Overrides Sub CreateChildControls()
        MyBase.CreateChildControls()

        Try
            _headerMessage = New Literal
            Me.Controls.Add(_headerMessage)

            Dim br As New HtmlGenericControl("br")
            Me.Controls.Add(br)

            _stepList = New ListBox

            With _stepList
                .SelectionMode = ListSelectionMode.Single
                .Rows = 7
            End With

            Me.Controls.Add(_stepList)
        Catch ex As Exception
            Me.Controls.Clear()

            Dim msg As New Literal()
            msg.Text = ex.Message
            Me.Controls.Add(msg)
        End Try

    End Sub

    Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
        MyBase.OnPreRender(e)

        If _diskInterface IsNot Nothing Then
            _disks = _diskInterface.NumberOfDisks
        End If

    End Sub

    Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter)

        If _headerMessage IsNot Nothing Then
            _headerMessage.Text = "Here are the steps for " & _disks & " disks:"
        End If

        If _stepList IsNot Nothing AndAlso _disks > 1 Then
            FillListWithSteps(_stepList, _disks)
        End If

        MyBase.RenderContents(writer)

    End Sub

    <ConnectionConsumer("Number of Disks")> _
    Public Sub AcceptDiskInterface(ByVal diskInterface As IDisks)
        _diskInterface = diskInterface
    End Sub

    Private Sub FillListWithSteps(ByVal list As ListBox, ByVal disks As Integer)

        list.Items.Clear()

        Towers(list, disks, 1, 3, 2)

    End Sub

    Private Sub Towers(ByVal list As ListBox, ByVal disks As Integer, ByVal fromPole As Integer, ByVal toPole As Integer, ByVal usingPole As Integer)

        If disks = 1 Then
            list.Items.Add(String.Format("Move a disk from pole {0} to pole {1}", fromPole, toPole))
        Else
            Towers(list, disks - 1, fromPole, usingPole, toPole)
            Towers(list, 1, fromPole, toPole, usingPole)
            Towers(list, disks - 1, usingPole, toPole, fromPole)
        End If

    End Sub

End Class

Most of this code is needed just to make this web part work.  That is, to create UI controls that list the steps needed to solve the puzzle.  The AcceptDiskInterface method is the key to accepting data from the first web part.  It accepts an instance of the IDisks interface and stores it for later use.  The ConnectionConsumer attribute is used to tell WSS that this method will consume data.  The OnPreRender method checks to see if the interface has been received.  If so, it stores the number of disks in a variable.  The RenderContents method uses this variable to determine how many disks have been specified, and what steps are needed to solve the puzzle.

All that's left is to edit one of the web parts, select the connections menu, and link the parts together.

This completes the first post.  We've seen how one web part can pass data to another.  Look for another post soon, where I'll expand on this topic.

Note that, while you are free to use this code as you wish, it comes with no guarantees.  I wrote it simply for illustration purposes, not for actual use.

Posted by Jerry.Dixon | 7 Comments
Filed under: ,

Subscribe to the Lifecycle Newsletter

In my last post, I discussed the Microsoft product lifecycle site, and how you should use it to ensure that your products remain supported.  If you have a lot of products, this can be pretty time-cosuming.  There is a better way.  Simply subscribe the the quarterly newsletter.  Each quarter, you'll receive an email update of all of the products that are undergoing a change in their support status.  This includes any change in support, including support phase transitions or a complete loss of support.

Check it out here.  It can save you a lot of time and trouble.

Posted by Jerry.Dixon | 1 Comments
Filed under:

Check Your Service Pack Lifecycle

Many people know about the support lifecycle site at Microsoft.  It contains information about the support policy for Microsoft products.  It allows you to look up a specific version of a Microsoft product and to see when support for that product ends.  It also explains the support phases that a product can travel through, and how these phases affect support for different customers. (You can read about the support phases here.)

Now that I work for Microsoft Services, this support page has become very important to me.  I've discovered that many people DO NOT know that Service Packs also have a support lifecycle.  Even though a product has support, a particular Service Pack may not.  Let's look at an example.

Supposed that I am using SQL Server 2005.  If we look this up on the lifecycle site, we'll see that SQL Server 2005 will be Mainstream support until 4/12/2011.  We'll also see that it will be in Extended support until 4/12/2016.  Looks like I'm covered, right?

Maybe not.

What if I haven't installed any Service Pack?  This is known as the RTM or SP0 release.  Support for this release expired on 7/10/2007.  Oops!  No support!  I could be in trouble.  If I install Service Pack 1, then I'm a bit better off.  I will have support until 4/8/2008.  Service Pack 2 woud be even better.  Since it is the most current release, it doesn't yet have an expiration date. (Other than the expiration date of the product itself, of course.)  You can read about Service Pack support here.

So, as you can see, product support is determined not only by the product itself, but also by the installed Service Pack.  Because no one should install a Service Pack without appropriate testing, it pays to use the lifecycle site and to plan your testing accordingly.  Do let yourself get caught with an out-of-date Service Pack!

Posted by Jerry.Dixon | 3 Comments
Filed under:
More Posts Next page »
 
Page view tracker