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.aspx.

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