The DataContractSerializer is a Dirty Little Peeping Tom

I had a problem today with my SelfSerializer class and the DataContractSerializer. I’ve got this test which was throwing an annoying ‘Object graph for type ‘SelfSerializer’ contains cycles and cannot be serialized if reference tracking is disabled’ error:

    <TestMethod>
    Public Sub SelfSerializerSerializationTest_XMLSerializer()
        Try
            Dim token As New SelfSerializer
            Dim dcs As XmlSerializer = New XmlSerializer(GetType(SelfSerializer))
            Dim xml As String = String.Empty
            Using stream As New StringWriter(), writer As XmlWriter = XmlWriter.Create(stream)
                'Serialize the item to the stream using the namespace supplied
                dcs.Serialize(writer, token)
                writer.Flush()
                'Read the stream and return it as a string
                xml = stream.ToString
            End Using
            Debug.Print(xml)
            Dim newToken As SelfSerializer
            Dim sr As New StringReader(xml)
            Dim reader As XmlReader = XmlReader.Create(sr)
            dcs = New XmlSerializer(GetType(SelfSerializer))
            newToken = CType(dcs.Deserialize(reader), SelfSerializer)
            reader.Close()
            sr.Close()
            Assert.IsTrue(newToken IsNot Nothing)
        Catch ex As Exception
            Debug.Print(ex.Message)
            Throw
        End Try
    End Sub

What’s happening internally is that the DataContractSerializer is using reflection to peep into the private parts of my class!

So to prevent this filthy little activity I have to add the DataContract attribute to the SelfSerializer class and IgnoreDataMember to the two private fields I use internally.

Frankly it’s all a bit Benny Hill…

bennyhill

<cue YakketySax music>

Update a Word Document to the Word 2013 Style (aka Template)

This has been annoying me for ages as I have a lot of documents written in the default Word 2007/2010 template and the Word 2013 default styles look a lot nicer.

To change the ‘template’ (it’s not really a template any more it’s now a ‘Style Set’):

  1. Open the document in Word 2013
  2. Save As .docx
  3. Click on the Design tab
  4. Click on the More button of the Style Sets toolbar
  5. Click ‘Reset to the Default Style Set’
  6. You’re done

Ajax Post a Knockout Stringified Json Object to an ASP.NET MVC5 Controller

Typically there isn’t a simple way to post from JavaScript a Json object as a string and receive it as such in a controller. This is because the model binding system interferes in the background and when it sees a Json-like string attempts to bind it to a model, but if you have not specified a model in your controller function parameter then it turns it into a null even if you specified that you wanted a string. Thanks model binder, you suck.

So the way around it is to create a wrapper model that contains a string into which you can dump your stringified Json object like this:

Public Class JsonViewModel
    Property Json As String = ""
End Class

Here’s the controller with the function that takes the model as a parameter:

 <HttpPost>
Function Save(model As JsonViewModel) As ActionResult
    If model IsNot Nothing AndAlso model.Json.Length > 0 Then
        Debug.Print(model.Json.ToString)
        'TODO: Process data, either of the following ways will work but the second is more obvious
        'Dim json = JsonConvert.DeserializeObject(model.Json)
        'For Each item In json
        ' Debug.Print(item.Name & ": " & item.Value)
        'Next
        Dim dictjson = JsonConvert.DeserializeObject(Of Dictionary(Of String, Object))(model.Json)
        For Each kvp As KeyValuePair(Of String, Object) In dictjson
            Debug.Print(kvp.Key & ": " & kvp.Value.ToString)
        Next
        Return New HttpStatusCodeResult(200)
    Else
        Return New HttpStatusCodeResult(500)
    End If
End Function

Here’s the JavaScript from inside a Knockout ViewModel that posts the stringified Json object:

self.Save = function () {
    var payload = ko.toJSON(self);
    $.ajax("/Home/Save/", {
        type: "POST",
        processData: false,
        contentType: "application/json; charset=utf-8",
        data: JSON.stringify({ Json: payload })
    })
    .done(function (data) {
        alert('Data saved');
    })
    .fail(function () {
        alert('Data not saved.');
    });
};

VB.NET Equivalent of C# typeof

This is one of the tricky bits of C# to VB.NET conversion that I, like others, always forget. Anyway,

typeof(thing)

should be converted to

GetType(thing)

How To Test Command Line Parameters the Easy Way

Sometimes it’s necessary to call a program and pass some command line parameters, this can be difficult to test as the other program may not be under your control or may be a live system. To test calling another program with command line parameters create a text file, name it ‘DisplayCommandLine.bat’, and insert the following text:


@echo off
@echo %*
pause

Then in your calling program simply substitute ‘CommandLine.bat’ for the program name and you’ll soon be able to see exactly what is being passed from one program to the other.

Do You Know Your Message Queue from Your Windows Procedure?

If you’re a Windows developer and don’t know this then you need to learn it, now. Here’s why:

I’ve been examining some legacy code that has a Timer event with some lengthy code that contains DoEvents calls. A simplified version looks something like this:


Private Sub tmrProcess_Timer()
'Run some slow processing code here
DoEvents
'More slow code here
DoEvents
'Lots more slow code and the occasional DoEvents here
If booComplete Then
tmrProcess.Enabled = False
End If
End Sub

The timer has it’s Interval set to 250 and the slow code could take up to thirty or so seconds to complete.

Given that VB6 is single threaded and that timer messages are low priority the question is if it is at all possible for the Timer event to be re-entered during a DoEvents call or will the VB6 runtime block execution of a Timer event if the Timer event is currently executing?

The reference above contains some interesting clues as to the answer. In particular it states that WM_PAINT messages are combined into a single message but there is no mention of whether or not WM_TIMER messages are combined.

So the best way to find out is to write a test program:


Option Explicit

Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Private Counter As Integer
Private StopTimer As Boolean

Private Sub cmdStart_Click()
txtResults.Text = "Time: " & Format(Now, "HH:mm:ss") & " Start"
Counter = 0
StopTimer = False
tmrTest.Interval = 500
tmrTest.Enabled = True
End Sub

Private Sub cmdStop_Click()
txtResults.Text = txtResults.Text & vbCrLf & "Time: " & Format(Now, "HH:mm:ss") & " Counter: " & Counter & " Stop"
StopTimer = True
End Sub

Private Sub tmrTest_Timer()
Static Index As Integer
Index = Index + 1
Counter = Counter + 1
txtResults.Text = txtResults.Text & vbCrLf & "Time: " & Format(Now, "HH:mm:ss") & " Index: " & Index & " Counter: " & Counter
DoEvents
Sleep 1000
DoEvents
Sleep 1000
DoEvents
txtResults.Text = txtResults.Text & vbCrLf & "Time: " & Format(Now, "HH:mm:ss") & " Index: " & Index & " Counter: " & Counter
If StopTimer = True Then
tmrTest.Enabled = False
txtResults.Text = txtResults.Text & vbCrLf & "Time: " & Format(Now, "HH:mm:ss") & " Stopped"
End If
End Sub

Here’s my test results:

Time: 15:54:38 Start
Time: 15:54:38 Index: 1 Counter: 1
Time: 15:54:40 Index: 1 Counter: 1
Time: 15:54:41 Index: 2 Counter: 2
Time: 15:54:43 Index: 2 Counter: 2
Time: 15:54:43 Index: 3 Counter: 3
Time: 15:54:44 Counter: 3 Stop
Time: 15:54:45 Index: 3 Counter: 3
Time: 15:54:45 Stopped

What I think is happening is that additional calls to the Timer event are being cancelled by the VB6 runtime Windows Procedure so that only one Timer event can run at once. Then when the Timer is cancelled any remaining WM_TIMER messages are either removed from the queue, ignored, or fail silently because the function they are calling is no longer valid.

Because the Windows Procedure is the core of a client program and are not required to be standardized it would be very interesting to compare how different programs, and especially different programming language runtimes, handle the same situation.

Search XML Column as a String Using LIKE

You can search an XML column as a string without using XQuery by converting it to a string and then using LIKE. However as a computed column can’t be part of a WHERE clause you need to wrap it in another SELECT like this:


SELECT * FROM
(SELECT CONVERT(varchar(MAX), [XMLData]) as [XMLDataString] FROM [TABLE_NAME]) this_is_irrelevant
WHERE [XMLDataString] like '%SEARCH_STRING%'

Resolving the dreaded System.ServiceModel.AddressAccessDeniedException error

I had a complex WCF service based solution working on my development PC but due to a problem when installing Windows 8.1 I had to ‘Refresh’ my PC which basically means reinstalling Windows, losing all my applications, but not my documents or data. So I had a few days of sorting out Windows, reinstalling apps and trying to get all my settings back to just the way they were before.

Now that I’ve reinstalled Visual Studio 2012 my project no longer functions correctly. When I debug a unit test the WCF Service Host displays an error:

Please try changing the HTTP port to 8733 or running as Administrator.
System.ServiceModel.AddressAccessDeniedException: HTTP could not register URL http://+:8732/Design_Time_Addresses/MyWCFService/Name/. Your process does not have access rights to this namespace (see http://go.microsoft.com/fwlink/?LinkId=70353 for details). —> System.Net.HttpListenerException: Access is denied

Apparently the usual solution to this is to run Visual Studio as administrator. However I’m unable to do that as the architecture we use at work requires us to reference DLLs on a network drive and network drives cannot be accessed when you run a program as administrator (aka elevated).

The other alternative of changing the port to 8733 would require editing every service, re-referencing them and then rebuilding the solution and hoping that it works (unlikely).

After a bit of digging around and research I found out that recent versions of Windows enforce security settings that prevent you from listening to any ports. I’m assuming that when Visual Studio is installed it reserves a port for it’s own use so that you can host and thus debug web services. This was the localhost:8732 port on my development machine but with reinstalling Visual studio it is now localhost:8733, unfortunately all my app.config files in the solution point to port 8732.

However this can be fixed!

Run an elevated command prompt and execute ‘netsh http show urlacl’. This displays the list of reserved ports amongst which I found this:

Reserved URL            : http://+:8733/Design_Time_Addresses/
User: NT AUTHORITY\INTERACTIVE

Which, I’m assuming, is the entry added when Visual Studio was installed.

Executing the following command reserves the 8732 url/port:

‘netsh http add urlacl url=http://+:8732/ user=WORK\Robin’

Restart Visual Studio and all of a sudden my solution works again.

Passing a small object in the querystring

For the current project at work I need to pass some information from an application or website to another website. Rather than storing the state in a database I decided to serialize the state object and pass it, encrypted, in the querystring.

This is the encoding function:


Dim plaintext as String = myState.Serialize
Dim bytes As Byte() = ASCIIEncoding.UTF8.GetBytes(plaintext)
Dim encrypted As Byte() = Encryption.Encrypt(bytes, key)
Dim encodedtext As String = Convert.ToBase64String(encrypted)
Return System.Web.HttpUtility.UrlEncode(encodedtext)

Note that the serialized and encrypted object is url encoded using the System.Web.HttpUtility.

This is the decode function:


Dim decodedtext As Byte() = Convert.FromBase64String(querystring)
Dim decrypted As Byte() = Encryption.Decrypt(decodedtext, key)
Dim plaintext As String = ASCIIEncoding.UTF8.GetString(decrypted)
Dim mystate As New State
mystate.Deserialize(plaintext)
Return mystate

and in C# because I need to practice my skills:


byte[] decodedtext = Convert.FromBase64String(querystring);
byte[] decrypted = Encryption.Decrypt(decodedtext, key);
string plaintext = ASCIIEncoding.UTF8.GetString(decrypted);
State mystate = new State();
mystate.Deserialize(plaintext);
return mystate;

Note that the querystring is not url decoded, this is because the ASP.NET http handler decodes the querystring before it is passed to your code.

Handy Tip for Virtual Machines

When I’m booting up a virtual machine on my PC I always have to dig into the network details or run ipconfig in  order to determine the IP Address before I can use remote desktop. To save one of those steps you can run bginfo from the startup folder to get it to automatically write the IP Address and other details to the desktop background:

http://technet.microsoft.com/en-us/sysinternals/bb897557.aspx

and

http://windows.microsoft.com/en-us/windows/run-program-automatically-windows-starts