Friday, December 28, 2007

.NET Method Parameters and Their SOAP

This article provides an overview of the different ways .NET Web Services SOAP will be generated based on how the parameters or return values are declared.

No Output
The following is the SOAP for a web method with no output:

VB.NET Code

<WebMethod()> _
Public Sub NoOutput()

End Sub

C# Code

[WebMethod]
public void NoOutput()
{
}

SOAP Input

  <soap:Body>
    <NoOutput xmlns="urn:sample" />
  </soap:Body>

SOAP Output

  <soap:Body>
    <NoOutputResponse xmlns="urn:sample" />
  </soap:Body>

Output Only
The following is the SOAP for a web method with no parameters that returns a string value:

VB.NET Code

<WebMethod()> _
Public Function StringOutput() As String
    Return "test"
End Function

C# Code

[WebMethod]
public string StringOutput()
{
    return "test";
}

SOAP Input

  <soap:Body>
    <StringOutput xmlns="urn:sample" />
  </soap:Body>

SOAP Output

  <soap:Body>
    <StringOutputResponse xmlns="urn:sample">
      <StringOutputResult>string</StringOutputResult>
    </StringOutputResponse>
  </soap:Body>

Input Only Parameter 
The following is the SOAP for a web method with a single integer input-only parameter and no return value:

VB.NET Code

<WebMethod()> _
Public Sub InputOnly(ByVal InputInt As Integer)
End Sub

C# Code

[WebMethod]
public void InputOnly(int InputInt)
{
}

SOAP Input

  <soap:Body>
    <InputOnly xmlns="urn:sample">
      <InputInt>int</InputInt>
    </InputOnly>
  </soap:Body>

SOAP Output

  <soap:Body>
    <InputOnlyResponse xmlns="urn:sample" />
  </soap:Body>

Output Only Parameter
The following is the SOAP for a web method with a single integer output-only parameter and no return value:

VB.NET Code

<WebMethod()> _
Public Sub OutputOnly( _
    <Runtime.InteropServices.Out()> ByRef _
    OutputInt
As Integer)
    OutputInt = 0
End Sub

C# Code

[WebMethod]
public void OutputOnly(out int OutputInt)
{
    OutputInt = 0;
}

SOAP Input

  <soap:Body>
    <OutputOnly xmlns="urn:sample" />
  </soap:Body>

SOAP Output

  <soap:Body>
    <OutputOnlyResponse xmlns="urn:sample">
      <OutputInt>int</OutputInt>
    </OutputOnlyResponse>
  </soap:Body>

Input-Output Parameter
The following is the SOAP for a web method with a single integer input-output parameter and no return value:

VB.NET Code

<WebMethod()> _
Public Sub InputOutput(ByRef InOutInt As Integer)
End Sub

C# Code

[WebMethod]
public void InputOutput(ref int InOutInt)
{
}

SOAP Input

  <soap:Body>
    <InputOutput xmlns="urn:sample">
      <InOutInt>int</InOutInt>
    </InputOutput>
  </soap:Body>

SOAP Output

  <soap:Body>
    <InputOutputResponse xmlns="urn:sample">
      <InOutInt>int</InOutInt>
    </InputOutputResponse>
  </soap:Body>

VMware and .NET Timers

The Problem
We recently had a client inform us that a Windows Service we had written would occasionally appear to be running, but not actually be doing anything. Once the Windows Service was restarted, it would begin processing again. The service would get into this state after it had been running for awhile, but it was never the same amount of time. Sometimes it would run for days without issue, but other times the problem would appear after only 8 hours of operation. We had not made any changes to the Windows Service in a long time so we were a little baffled. We attempted to reproduce the error, but were not successful.

After much investigation it was discovered that the client had moved the Windows Service to a VMware Virtual Machine. We had the client move the service back to a physical machine and the problem went away. At that point we knew something in our service did not agree with VMware, but our service was written using Visual Basic 2005 and was not doing anything out of the ordinary (e.g. no calls to unmanaged code, no third-party libraries, etc.).

We finally narrowed the problem down to a System.Timers.Timer we were using to determine when the Windows Service should start processing again once it had completed its work and had gone to sleep. Apparently the System.Timers.Timer would stop firing at some point, which prevented our application from waking up. The timer would only stop firing if the service was run on a VMware Virtual Machine.

The Solution
We were able to replace the System.Timers.Timer with a background worker thread (specifically a System.ComponentModel.BackgroundWorker control) that used a call to System.Threading.Sleep. This allowed our Windows Service to work on both physical machines and VMware Virtual Machines.

Some Lessons Learned

  • .NET Timers and other time keeping techniques may not function properly in a VMware Virtual Machine.
  • Additional testing needs to be performed to verify an application will work properly in a VMware Virtual Machine.
  • The System.Threading.Sleep method does not appear to be affected by the issues that caused the System.Timers.Timer to stop firing.

More Details
VMware has an excellent article called "Timekeeping in VMware Virtual Machines", which can be found at http://www.vmware.com/resources/techresources/238. This article contains information that might explain why our .NET timer stopped firing.

Another valuable resource for .NET timers is an article written by Alex Calvo called "Comparing the Timer Classes in the .NET Framework Class Library", which can be found at http://msdn.microsoft.com/msdnmag/issues/ 04/02/TimersinNET/default.aspxhttps://docs.microsoft.com/en-us/archive/msdn-magazine/2004/february/comparing-the-timer-classes-in-the-net-framework-class-library.

Paste Unformatted Text Macro for Outlook 2007

One macro that I have used for years in both Word and Outlook is one that will paste the contents of the clipboard as unformatted text instead of the default paste behavior which is pasting with formatting. The code below is a copy of this macro:

Sub PasteUnformattedText()
'
' PasteUnformattedText Macro
' Pastes the contents of the clipboard as unformatted text.
'
     Selection.PasteSpecial Link:=False, _
         DataType:=wdPasteText, _
         Placement:=wdInLine, _
         DisplayAsIcon:=False
End Sub

When I upgraded my copy of Microsoft Office to Office 2007, I created and tested my Paste Unformatted Text macro in Word 2007 without any trouble. In previous versions of Office my macro would be available to both Word and Outlook. However, this is not the case in Office 2007. The e-mail editor in Outlook 2007 is Word, but it is not Word at the same time. Outlook 2007 has its own macros.

I copied my macro into Outlook 2007's macro editor, but received the following error when I tried to execute it:

Run-time error '429': ActiveX component can't create object

It turns out that Word and Outlook use two different object models in Office 2007. To get the Paste Unformatted Text macro to work in Outlook 2007 you need to first add a reference to the "Microsoft Word 12.0 Object Library" library. This is done by selecting Tools->References... from the Outlook 2007 macro editor (a.k.a. Microsoft Visual Basic 6.5). Once this reference has been added, the following macro will perform a Paste Unformatted Text operation:

Sub PasteUnformattedText()
'
' PasteUnformattedText Macro
' Pastes the contents of the clipboard as unformatted text.
'

    Dim objDoc As Word.Document
    Dim objSel As Word.Selection
   
    On Error Resume Next
   
    ' get a Word.Selection from the open Outlook item
    Set objDoc = Application.ActiveInspector.WordEditor
    Set objSel = objDoc.Windows(1).Selection

    objSel.PasteSpecial Link:=False, _
        DataType:=wdPasteText, _
        Placement:=wdInLine, _
        DisplayAsIcon:=False
   
End Sub

Friday, August 31, 2007

C# ‘out’ Parameters: Good or Bad?

VB.NET supports two parameter passing modes: ByVal and ByRef. ByVal indicates changes made to the parameter by the method will not be reflected back to the caller. ByRef indicates changes made to the parameter by the method will be reflected back to the caller.

C# supports three parameter passing modes: ‘in’, ‘ref’, and ‘out’. ‘in’ is the same as the VB.NET ByVal. ‘ref’ is the same as the VB.NET ByRef. ‘out’ indicates the argument does not need to be initialized outside of the method, but this parameter must be set before the method returns.

I recently read two Microsoft articles on C# ‘out’ parameter passing. The first one, written by Peter Hallam, titled “Why does C# have both 'ref' and 'out'?”, explains why C# supports ‘out’ parameter passing and explains why it is a good thing. You can read this article at http://msdn2.microsoft.com/en-us/vcsharp/aa336814.aspx https://web.archive.org/web/20080408140708/http://msdn2.microsoft.com/en-us/vcsharp/aa336814.aspx. The second one, which is really an FxCop rule, titled “Avoid out parameters”, indicates that one should not use ‘out’ parameter passing. You can read this article at http://msdn2.microsoft.com/en-us/library/ms182131(VS.80).aspx.

After reading both articles and doing a little research on my own, I came to the conclusion that the ‘out’ parameter passing mode has its place. Part of this decision was based on the ‘out’ parameter’s usefulness in web service methods. I also decided that I is would be nice if VB.NET had an equivalent parameter passing mode.

The “Avoid out parameters” main argument against using ‘out’ parameters is as follows:
Although return values are commonplace and heavily used, the correct application of out and ref parameters requires intermediate design and coding skills. Library architects designing for a general audience should not expect users to master working with out or ref parameters.

I do not think this is a good reason for not using a language feature. I would like to hear other’s comments on the issue of whether or not using ‘out’ parameters in C# code is a good thing or a bad thing.

Monday, August 27, 2007

Problem with Security Update for Microsoft .NET Framework, Version 2.0 (KB928365)

On July 10, 2007 Microsoft release “Security Update for Microsoft .NET Framework, Version 2.0 (KB928365)”. Unfortunately, this security update has caused some VB.NET software we had written to stop working.

Example Code

Imports System.Xml.Serialization

<System.Serializable()> _
<Xml.Serialization.XmlRoot("SampleData")> _
Public Class SampleData

    Public Sub New()
        mNameList = New List(Of String)
    End Sub

    Private mNameList As List(Of String)
    Public Property NameList() As List(Of String)
        Get
            Return mNameList
        End Get
        Set(ByVal value As List(Of String))
            mNameList = value
        End Set
    End Property

    <XmlIgnore()> _
    Public ReadOnly Property NameListSpecified() As Boolean
        Get
            If mNameList Is Nothing OrElse mNameList.Count < 1 Then
                Return False
            Else
                Return True
            End If
        End Get
    End Property

    Public Overridable Function GetXml() As String

        Dim Buffer As New IO.MemoryStream()
        Dim XmlStreamer As New XmlSerializer(Me.GetType)

        ' Serialize object into XML
        XmlStreamer.Serialize(Buffer, Me)

        ' Encode into String variable
        Return System.Text.Encoding.UTF8.GetString(Buffer.ToArray())

    End Function

End Class

Module Startup

    Sub Main()

        Dim DataObject As New SampleData()

        DataObject.NameList.Add("Name 1")
        DataObject.NameList.Add("Name 2")

        Try
            Console.WriteLine("SampleData as XML:")
            Console.WriteLine(DataObject.GetXml())
        Catch ex As Exception

            Console.Error.WriteLine("The following error occurred:")
            Console.Error.WriteLine(ex.Message)
            Console.Error.WriteLine()
            Console.Error.WriteLine("Error Stack:")
            Console.Error.WriteLine(ex.StackTrace)

        End Try

    End Sub

End Module

Results Pre-KB928365

The following is the output of the sample application above if KB928365 is not installed:

SampleData as XML:
<?xml version="1.0"?>
<SampleData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <NameList>
    <string>Name 1</string>
    <string>Name 2</string>
  </NameList>
</SampleData>

Results Post-KB928365

The following is the output of the sample application if executed on a machine that has KB928365 installed:

SampleData as XML:
The following error occurred:
Unable to generate a temporary class (result=1).
error CS0200: Property or indexer 'XmlSerializationIssueKB928365.SampleData.NameListSpecified' cannot be assigned to -- it is read only

Status as of 07/13/2007

Microsoft has confirmed this is an issue with KB928365. Specifically they have stated the following:
1. Serialization of List<T> fails on Windows 2003 SP2 and Windows XP SP2 after application of Security Patch KB928365.
2. Failure happens only when the project is written in VB.NET. Same code written in C#.NET works.

Microsoft plans to release a hotfix to correct this issue. I will update this posting when that hotfix has been released.

Status as of 07/16/2007

I had the opportunity to discuss this issue with my Microsoft Support contact again today, and we discovered the following:
1. The failure was introduced by KB934529 (which was not widely distributed), but was included as part of KB928365.
2. Changing the NameListSpecified property to writable will workaround the issue.
3. The two items listed in the “Status as of 07/13/2007” section of this article may be incorrect.

I am waiting to hear back as to why this change was made in the .NET 2.0 Framework.

Status as of 07/19/2007

Here is what Microsoft Support had to say about the issue described above:

The reason behind a sudden change in the behavior of serialization / de-serialization was due to a bug identified in the .NET 2.0 version. First let me explain the bug to you. Consider a case where the xxxSpecified property is not marked with [XmlIgnore] and a set method is provided in the code. In that case, the property will look something like this:

Public Property NameListSpecified() As Boolean
   Get
      ‘code
   End Get
   Set(ByVal value As Boolean)
      ‘code
   End Set
End Property

So, when this particular object is serialized, the value of this property will appear in the Xml. In our case, it will appear like this:

   <NameList>
      <string>Name 1</string>
      <string>Name 2</string>
   </NameList>
   <NameListSpecified>true</NameListSpecified>

Everything is fine till now. The problem arises during de-serialization.

During de-serialization, the value of this property should be set as well. You might be aware that XmlSerializer creates dynamic assemblies to accomplish the task of serialization/de-serialization. The code generated during the de-serialization process, in certain cases, did not have the set() method for the property. This was the bug. Hence, the Boolean value used to get set to null, when we were expecting a valid value. This bug was present in .NET 2.0 RTM and was fixed in later updates on System.Xml.dll . (available with hot fixes like 934529, 928365). The fix was in case this property is implemented in code, both the get() and set() has to be provided. You cannot mark this as ReadOnly anymore.

I do fully understand that this scenario is not applicable in our case, as we have attribute tagged with the property. So why should our application break? This has been identified as a valid point. I have had a discussion with the dev team on this as well. They have identified it as another bug induced by a fix. However, they said that since a valid workaround to this is available right now, a fix for this will be provided with .NET 2.0 service pack release. However, a KB article will soon be published by our team acknowledging this break situation with the workaround.

I am waiting to hear back what the KB article number will be and when the .NET 2.0 service pack that contains this fix will be released.

Status as of 08/10/2007

Microsoft sent me a private fix to test against my software yesterday (08/09/2007). The fix was an updated copy of the system.xml.dll (Version 2.0.50727.914). I tested the updated dll against our production code and verified it corrected the issue we were seeing. I will update this posting when the official hotfix is released.

Status as of 08/27/2007

Microsoft contacted me and let me know that KB 941349 (http://support.microsoft.com/?id=941349 https://static.heironimus.info/blog/msdn/941349.html) would be published in a few weeks. This article details the Microsoft .NET Framework 2.0 hotfix that fixes the issue discussed in this article. The version of the system.xml.dll that is included in this hotfix is 2.0.50727.919.

Friday, July 27, 2007

Using an Object Property as a ByRef Parameter

The Problem

We had developed a class that contained an internal flag that would be set if a specific property on the class was set. The class would perform different logic based on whether or not the flag was set. In our testing we found that this internal flag was getting set in a scenario where it should not have been set. We verified that the code path executed in our test scenario would not explicitly set the property, but the flag was getting set.

Through a little investigation we determined that the Set property procedure for the property was being executed, even though we never explicitly set this property in our code. After a little more digging we discovered that this class’s property was being passed in to a procedure as the argument for a ByRef parameter.

What VB.NET Does

It appears that VB.NET basically does the following anytime an object property is used as an argument for a ByRef parameter:

1. VB.NET calls the Get property procedure for the object property that is being used as the argument for a ByRef parameter and stores it in a local variable. This is the same thing that happens when an object property is used as the argument for a ByVal parameter.

2. VB.NET will then execute the code in the procedure. Any Get or Set operations performed on the ByRef parameter will be executed against this local variable rather than the object property.

3. When the procedure has finished executing, VB.NET will set the object property’s value to the local variable by calling the Set property procedure for that object property. This will occur even if the local variable associated with the ByRef parameter was never set. If this were a ByVal parameter, this set operation would not occur.

This technique for handling object properties used as arguments for ByRef parameters seems reasonable, but if you are not aware that this is how VB.NET handles this situation, you can find yourself in trouble. In our case we had logic associated with the class’s property being set and we assumed the property would only be set if the ByRef parameter was set. Unfortunately, this is not how VB.NET was implemented by Microsoft, so we had to modify our code to accommodate the actual behavior of VB.NET.

What C# Does

Out of curiosity, I wanted to see if C# exhibited the same behavior as VB.NET, so I wrote a simple application to duplicate what we were doing in our VB.NET code. When I tried to compile the application, the following error occurred:
error CS0206: A property or indexer may not be passed as an out or ref parameter
It appears that the C# development team, when faced with the same technical issue, decided to inform the developer that he or she could not do what they were trying to do by raising a compiler error. In contrast, the VB.NET group provided the desired functionality, but implemented it in a way that is not necessarily intuitive.

Personal Opinion

I personally prefer the C# approach for handling this issue, but I understand why VB.NET works the way it does.

Lesson Learned

It is best to avoid using an object’s property as the argument for a ByRef parameter in VB.NET.

Friday, June 01, 2007

Possible Security Issue in IE on Windows XP

Conventional wisdom states that users should use non-administrative accounts for day-to-day usage on Windows XP machines to minimize exposure to malware. Windows XP even has a nice feature where an application can be executed using another user’s credentials if that application needs to run with special privileges.

An example of something that cannot be done with a non-administrative account is using Windows Update. To use Windows Update a user will typically right-click on the Internet Explorer (IE) icon, select Run As, and enter the credentials of an administrative account.

If that IE window is left open and a user clicks on a URL that appears in an e-mail message, the page will appear in the IE window with the administrative privileges instead of an IE window running as the user that is logged in. This would be very bad if the page that was loaded contained malware.

I expected Windows would open the URL in an IE window running under the logged-in users credentials. Unfortunately, IE opens the URL in the most recently opened IE window, even if that IE window is running under a different user’s credentials. This seems like a security issue to me.

Friday, May 18, 2007

.NET Left & Right Replacements

Back in the days of VB6 there were two trusty functions that programmers could rely on to truncate string: Left and Right. These functions live on in the .NET Framework, but they live in the dreaded Microsoft.VisualBasic namespace, a namespace both shunned and feared by many. But do not loose hope, fair .NET programmer, for there is a “pure” way to easily reproduce the functionality of the Left and Right function with out resorting to the Microsoft.VisualBasic namespace methods.

All silliness aside, there is a simple way to reproduce the functionality of the Left and Right functions using non-Microsoft.VisualBasic namespace methods. The typically answer to this question is to use the Substring method, but the Substring method requires to use valid startIndex and length arguments.

In VB6 if you had a three-character string called TestString and called Left(TestString, 5), you would get your three-character string back. In .NET if you were to have this same string and call TestString.Substring(0, 5) method, you would get a ArgumentOutOfRangeException exception.

I colleague of mine came up with the following way to reproduce the functionality of the old VB6 Left function: TestString.Substring(0, Math.Min(5, TestString.Length))

The old VB6 Right function can be reproduced by using the following: TestString.Substring(TestString.Length – Math.Min(5, TestString.Length), Math.Min(5, TestString.Length))

Tuesday, April 24, 2007

Virtual Memory and Performance Counters

A few months ago my PC at work, which is running Windows XP SP2, was upgraded from 1 GB of RAM to 3 GB of RAM. I though, "Wow! With 3 GB of RAM I will not even need any virtual memory." So I went to my computer’s System Properties and turned off the paging file. After a quick reboot I was running with just 3 GB of RAM and no virtual memory, and everything was running just fine.

I happened to be working on adding some performance monitoring statistics to a Windows Service I was writing in VB.NET at the time I turned off my virtual memory, so I was using the Performance Administrative Tool that is part of Windows XP a lot. The next time I opened the Performance Administrative Tool only the "Avg. Disk Queue Length" counter was shown. The "Pages/sec" and "% Processor Time" counters were missing. When I tried to add them back to the graph, I discovered the Memory and Processor performance objects were missing from the list.

I though maybe I did something in my code to break them (although I did not really think my application would be able to affect system counters like that). I did a little research and discovered a nice tool that is part of the Windows 2000 Resource Kit called the Extensible Performance Counter List (Exctrlst.exe). You can read more about this tool at http://support.microsoft.com/kb/927229 https://web.archive.org/web/20130327215623/http://support.microsoft.com/kb/927229 and http://technet2.microsoft.com/WindowsServer/en/library/
48edd368-2bde-4647-9fea-1b5f28a23ca91033.mspx?mfr=true
https://web.archive.org/web/20080223113806/http://technet2.microsoft.com/windowsserver/en/library/48edd368-2bde-4647-9fea-1b5f28a23ca91033.mspx?mfr=true. By using this tool I discovered that the perfos.dll was disabled. I re-enabled the perfos.dll and restarted the Performance Administrative Tool. The "Pages/sec" and "% Processor Time" counters were back, but as soon as I tried to add an additional counter, the "Pages/sec" and "% Processor Time" counters would stop working. If I were to exit and restart the Performance Administrative Tool, the counters would be missing once again.

At this point I was pretty sure my application was not causing the disappearance of these counters and the disabling of the perfos.dll. I then remembered that I had disabled my virtual memory and decided to try re-enabling my virtual memory. Once my machine’s virtual memory and perfos.dll had been re-enabled, the "Pages/sec" and "% Processor Time" counters came back for good.

I have not been able to find any documentation to collaborate this discovery, but it seems that the performance counters hosted by the perfos.dll will not work unless you have virtual memory enabled. I have only tried this on Windows XP SP2 machines at this time. Please let me know if you have every experienced anything similar to this. I would like to hear your stories.