# Saturday, February 27, 2010

AppDomains in Windows Services

I've been meaning to write down something on this for a couple weeks now but have been having a tough time formulating a nice message on this. The short version is be very carefull doing this. I believe that somehow AppDomain's rely on something out of the Windows User Profile, and over time in a Windows Service when you're loading and unloading AppDomain's stuff MAY get screwed up.

We had a scheduler app which basically used AppDomain's as an isolation mechanism for controlling when and how each scheduled component was called and then disposed. Code looked something like this...

for each s as ScheduledComponent in myList
Dim d as new AppDomain
try
dim i as IExecutableInstance = d.CreateInstance("LaunchPad")
i.Execute
finally
AppDomain.Unload(d)
end try
next

Over time we'd eventually get into a state where the entire scheduler service which just cause a windows segmentation fault and shut down. No nice dot net exception or anything. Like I said, our best guess is that something gets out of sync between the windows profile and whatever exists in a dot net AppDomain.

Anyway, our solution was to create a wrapper layer which basically called each scheduled component as a process instead. This seems to be working very reliably ever since.

#    Comments [0] |
# Tuesday, January 26, 2010

Windows 7 / Internet Explorer 8 Javascript

We were having some problems on our development machines at work as soon as we upgraded to Windows 7 and IE8. The problem first manifested itself as a javascript "client out of memory" error while using Outlook Web Access (OWA) but it seemed like randomly we'd have javascript errors while navigating webpages.

Our workaround was to switch over to Google Chrome but then you start finding all the things that don't work with that so we just wanted to get this figured out.

We ended up working microsoft support on this one and long story short, it looks like one of our development tools is overwriting one of the IE classes. They had us re-register a couple classes and now it's working awesome. Here's the registery changes required...

[HKEY_CLASSES_ROOT\Interface\{79EAC9C5-BAF9-11CE-8C82-00AA004BA90B}]
@="IHlinkFrame"

[HKEY_CLASSES_ROOT\Interface\{79EAC9C5-BAF9-11CE-8C82-00AA004BA90B}\NumMethods]
@="8"

[HKEY_CLASSES_ROOT\Interface\{79EAC9C5-BAF9-11CE-8C82-00AA004BA90B}\ProxyStubClsid32]
@="{A4A1A128-768F-41E0-BF75-E4FDDD701CBA}"

#    Comments [0] |
# Tuesday, September 08, 2009

VMWare - Slow Network Performance

Some days it's amazing the types of things you get involved in... We've been fighting some network issues on our VMWare box for the last couple monthes. Everything on the VMWare community kept referencing Tcp Segment Offloading or TsoEnable. After screwing around with this for a couple hours I got back to an idea someone else at work had regarding the actual VMWare system configuration.

Turns out the nic cards in VMWare were by default configured to only run at 100 mbps instead of 1000mbps. You can re-configure these without even turning off the virtual servers! Good thinking Andy! Here's a screen snap from the vmware management console and our resulting performance gain.

#    Comments [0] |
# Friday, August 14, 2009

BindingSource "Object reference not set to an instance of an object"

We've had some problems with the Binding Source component in our Windows Forms development and I wanted to jot down a note on here in case anyone else comes across this.

The problem we were having was an IDE runtime error which looks like a NullReferenceException, the specific message says "Object reference not set to an instance of an object".

Apparently this has something to do with the .datasource files that are auto-generated by visual studio for holding "default" values. You can remove these from the My Project folder and then the error goes away. Not sure why these are even required since you can still build without them.

#    Comments [1] |
# Thursday, July 16, 2009

WebClient.DownloadFile

We've had some problems on our Windows 2008 servers integrating with a third party web service. The exceptions we got were...

WebException: an unexpected error occured on a send.

IOException: authentication failed because the remote party has closed the transport stream.

These errors wouldn't occur on Windows 2000 or 2003 boxes but popped up on all our 2008 installs. Turns out security is managed a little differently on 2008 and if the partner website is really strict with authentication you need to override the authentication mechanism.

ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3

It's only line of code but it's worth understanding the back story a little better. Here's the blog entry that pointed me in the right direction.

http://blogs.msdn.com/carloc/archive/2007/02/13/webclient-2-0-class-not-working-under-win2000-with-https.aspx

#    Comments [0] |
# Wednesday, May 06, 2009

Active Reports Viewer IndexOutOfRangeException

We’ve been fighting an intermittent error with our report viewer for the last couple years that’s been just terrible to track down and I think we finally made some progress today. We use a reporting tool called Data Dynamics Active Reports to generate our Screen and Paper reports. Their tool is basically a set of Dot Net assemblies that allow you to design and code a component called an Active Report.

At runtime, you execute this Active Report and then pass it into another component called an Active Report Viewer for displaying to the user. We found occasionally our users would get stuck in an infinite loop of error messages while displaying a report. Exceptions would pop up looking like this…

(IndexOutOfRangeException) Index was outside the bounds of the array.

   at DataDynamics.ActiveReports.Viewer.-0011.-3cbd()

   at DataDynamics.ActiveReports.Viewer.-dabf.-31a1()

   at DataDynamics.ActiveReports.Viewer.-b212.-afcd()

We could never recreate this on demand, but probably five times a month we would get a hundred error messages coming in from a client site like this. Eventually the client would give up pressing “OK” on our error handler, and kill the process.

When we’ve tried to troubleshoot this in the past it’s been extremely difficult because Data Dynamics obfuscates their code so it can’t be decompiled. Obfuscation basically takes design-time code, and mixes it up so that it’s unreadable, hence the funky function names above. Unfortunately this gives you no sense of what’s actually happening in their app.

A little bit of digging on their support forums today did find a link to a similar issue by another user - http://www.datadynamics.com/forums/85506/ShowPost.aspx

Basically what they’d found was that in a Terminal Services environment, their users would click their “Print” button multiple times – the old adage, keep pressing the button until something happens. What this causes in some instances is for the second “click” to get handled by the newly created Active Report Viewer, even though it’s just in the process of being displayed. Now, if the Viewer is in a funny spot, it can try and interact with this click event while loading the document – that’s what seems to cause the issue.

Once we had a sense of what was causing the issue, it’s pretty simple to disable the ReportViewer until the report has generated its first page and sure enough this causes the issue to go away.

 

#    Comments [0] |
# Monday, April 13, 2009

Panama Canal Cruise Days 1-5

Five days after arriving on our cruise ship, I've just discovered how easy it is to get on the internet. Bad news for Andrea...

We boarded in Fort Lauderdale, and yes, average age on our cruise is around 85. None-the-less, we've been having a great time.

Our first port was half moon cay, a bahamian island currently being leased by Holland America. It's still a little farther north so the water wasn't extremely warm but I can vouch for the fact that if you forget to put suntan lotion on your legs, you'll still burn.

Next stop was Aruba where we went Scuba diving. We dove on an old refining ship and had a run in with an Eel. Later on our dive master informed us he was once bitten by one and required 28 stiches.

Yesertday we went to Curacao, another less touristy, Dutch island. We did a tour of the more remote eastern end of the island and got some good shots of the very colorful capital - Willamstad.

We're at sea now and we should hit the Panama canal early tommorow morning.

#    Comments [0] |
# Monday, March 23, 2009

Source Code Version Control

One area of concern that I come up against quite often is the issue of source code version control. At its most basic layer this is nothing more than being able to control what a version of your software consists of... version A versus version B, that sort of thing. This model works great and is well documented on smaller isolated applications but tends to blur a little when working with larger systems. Specifically we’ve had issues managing system dependencies, as well as change requirements on a larger application consisting of about roughly 50 assemblies. I’ll spend just a few minutes here putting pen to paper on how we’ve structured our development tree to accommodate this.

We’ll start at a pretty simple level; each major release is placed in a named folder based on the version name. Our first branch within that folder is named Major.Minor.Zero, Major and Minor are related to the version name while the Zero indicated this is the opening development branch. Here’s how that looks when we start a new major release... our friendly version name is Jupiler and the Major.Minor associated with this is 5.1.

Notice that within the 5.1.0 folder we have a number of sub-folders. These are what we call modules and basically relate to a VS.Net solution; each one consists of roughly 5-6 projects and can be thought of as a sub-system. These sub-systems collaborate to provide our applications functionality.

Now, when we release our major version, Jupiler, we’ll typically LOCK the 5.1.0 folder so that no further changes can be made. This code is set in stone and we want to force all new changes to be done in an isolated environment until they’re tested and ready to go. Invariable though, something critical comes up during rollout and we need to make code changes to address those concerns. When this situation arises we BRANCH into a new folder called Major.Minor.One. This code folder consists of all the new development changes required to add what we call a Service Pack to the release. Here’s how this looks.  

One interesting aspect of this layout is that we only branch the modules that require changes. These Service Pack’s generally don’t require a lot of wholesale change; they`re usually small isolated bugs/features which can be addressed in a single module. You can see in the above diagram that our 5.1.1 only has changes for Accounting, all the other modules are identical to how they were developed in 5.1.0. This gives us a few key benefits.

1.       It limits the amount of open source code.

2.       It makes it easier to identify what code is actively being changed for a service pack.

3.       It leaves the original branch open so that it can be reviewed/modified for OTHER unanticipated issues.

Final rollout of the new changes is done by releasing 5.1.1 of Accounting with all the previous versions of the other modules.

#    Comments [0] |
# Wednesday, March 11, 2009

Congratulations, you've installed dasBlog with Web Deploy!

After logging in, be sure to visit all the options under Configuration in the Admin Menu Bar above. There are 26 themes to choose from, and you can also create your own.

 

#    Comments [0] |
# Friday, March 06, 2009

MSTSC Username/Password

Not that this is necessarily a good idea. But after way too much time googling this, I think I'll clear up the fact that it IS possible call the Microsoft RDP client with a username and password parameter.

Now, everyone out there will say, yeah - we know that. You key in the username and password and if you check off the "remember credentials" checkbox it will store that data.

Well the trick is that this data is stored in a place that's not easily accessed -Windows Stored UserNames and Passwords. This area is basically a secured area of windows where passwords are stored for remote connections. You can access this by going Control Panel\User Accounts\Manager your network passwords. Here's a screen snap.

Our management infrastructure tracks this username and password in a seperate secured database, and then when we call MSTSC.exe from our software we want to pass this information. Now, a little more information on how you can pass parameters to MSTSC is in order. When you call MSTSC you can pass as a parameter a .RDP file which contains information about how you want to connect to the remote computer. Same idea as every other document type in Windows, a .RDP file is associated with MSTSC and is used to contain information for that application. Fortunately the .RDP files are basically key value pairs. Check it out...

screen mode id:i:1
desktopwidth:i:1024
desktopheight:i:768
session bpp:i:16
winposstr:s:0,1,526,61,1566,865
full address:s:SomeIpAddress
compression:i:1
keyboardhook:i:2
audiomode:i:0
redirectdrives:i:0
redirectprinters:i:1
redirectcomports:i:0
redirectsmartcards:i:1
displayconnectionbar:i:0
autoreconnection enabled:i:1
alternate shell:s:
shell working directory:s:
disable wallpaper:i:0
disable full window drag:i:1
disable menu anims:i:1
disable themes:i:0
disable cursor setting:i:0
bitmapcachepersistenable:i:1
allow desktop composition:i:1
allow font smoothing:i:0
redirectclipboard:i:1
redirectposdevices:i:0
authentication level:i:2
prompt for credentials:i:0
negotiate security layer:i:1
remoteapplicationmode:i:0
gatewayhostname:s:
gatewayusagemethod:i:4
gatewaycredentialssource:i:4
gatewayprofileusagemethod:i:0
promptcredentialonce:i:1
drivestoredirect:s:

The imporant pieces here is the "full address" line... this points to the server we're connecting to.

Well... it turns out you can also pass parameters for domain, username, and password. You just need to add them to the bottom of your .rdp file like this

domain:s:MyDomain
username:s:MyUserName
password 51:b:MyPassword

But wait a second, saving a password in plain text in a .rdp file sounds like just about the worst security hole you could use! And you're right - you can't actually pass the password parameter as straight text. It needs to be encrypted binary.

(As an aside, that's what the second part of these key/value pairs is... the s, or b, or i character. That indicates whether the data is String, Binary or Integer).

Well it turns out that taking a straight text password and encrypting it ain't too bad. I'm not going to take credit for this code but the following vb dot net code uses the windows api to do just that.

Imports System
Imports System.Text
Imports System.Runtime.InteropServices
Imports System.ComponentModel
Imports Microsoft.VisualBasic

Public Class DPAPI
<DllImport("Crypt32.dll", SetLastError:=True, CharSet:=System.Runtime.InteropServices.CharSet.Auto)> Private Shared Function CryptProtectData( _
ByRef pPlainText As DATA_BLOB, _
ByVal szDescription As String, _
ByRef pEntropy As DATA_BLOB, _
ByVal pReserved As IntPtr, _
ByRef pPrompt As CRYPTPROTECT_PROMPTSTRUCT, _
ByVal dwFlags As Integer, _
ByRef pCipherText As DATA_BLOB _
) As Boolean
End Function

<DllImport("Crypt32.dll", SetLastError:=True, CharSet:=System.Runtime.InteropServices.CharSet.Auto)> _
Private Shared Function CryptUnprotectData( _
ByRef pCipherText As DATA_BLOB, _
ByRef pszDescription As String, _
ByRef pEntropy As DATA_BLOB, _
ByVal pReserved As IntPtr, _
ByRef pPrompt As CRYPTPROTECT_PROMPTSTRUCT, _
ByVal dwFlags As Integer, _
ByRef pPlainText As DATA_BLOB _
) As Boolean
End Function

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _
Friend Structure DATA_BLOB
Public cbData As Integer
Public pbData As IntPtr
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _
Friend Structure CRYPTPROTECT_PROMPTSTRUCT
Public cbSize As Integer
Public dwPromptFlags As Integer
Public hwndApp As IntPtr
Public szPrompt As String
End Structure

Private Const CRYPTPROTECT_UI_FORBIDDEN As Integer = 1
Private Const CRYPTPROTECT_LOCAL_MACHINE As Integer = 4

Private Shared Sub InitPrompt _
( _
ByRef ps As CRYPTPROTECT_PROMPTSTRUCT _
)
ps.cbSize = Marshal.SizeOf(GetType(CRYPTPROTECT_PROMPTSTRUCT))
ps.dwPromptFlags = 0
ps.hwndApp = IntPtr.Zero
ps.szPrompt = Nothing
End Sub

Private Shared Sub InitBLOB _
( _
ByVal data As Byte(), _
ByRef blob As DATA_BLOB _
)
' Use empty array for null parameter.
If data Is Nothing Then
data = New Byte(0) {}
End If

' Allocate memory for the BLOB data.
blob.pbData = Marshal.AllocHGlobal(data.Length)

' Make sure that memory allocation was successful.
If blob.pbData.Equals(IntPtr.Zero) Then
Throw New Exception( _
"Unable to allocate data buffer for BLOB structure.")
End If

' Specify number of bytes in the BLOB.
blob.cbData = data.Length
Marshal.Copy(data, 0, blob.pbData, data.Length)
End Sub

Public Enum KeyType
UserKey = 1
MachineKey
End Enum

Private Shared defaultKeyType As KeyType = KeyType.UserKey

Public Shared Function Encrypt _
( _
ByVal keyType As KeyType, _
ByVal plainText As String, _
ByVal entropy As String, _
ByVal description As String _
) As String
If plainText Is Nothing Then
plainText = String.Empty
End If
If entropy Is Nothing Then
entropy = String.Empty
End If

Dim result As Byte()
Dim encrypted As String = ""
Dim i As Integer
result = Encrypt(keyType, _
Encoding.Unicode.GetBytes(plainText), _
Encoding.Unicode.GetBytes(entropy), _
description)
For i = 0 To result.Length - 1
encrypted = encrypted & Convert.ToString(result(i), 16).PadLeft(2, "0").ToUpper()
Next
Return encrypted.ToString()
End Function

Public Shared Function Encrypt _
( _
ByVal keyType As KeyType, _
ByVal plainTextBytes As Byte(), _
ByVal entropyBytes As Byte(), _
ByVal description As String _
) As Byte()
If plainTextBytes Is Nothing Then
plainTextBytes = New Byte(0) {}
End If

If entropyBytes Is Nothing Then
entropyBytes = New Byte(0) {}
End If

If description Is Nothing Then
description = String.Empty
End If

Dim plainTextBlob As DATA_BLOB = New DATA_BLOB
Dim cipherTextBlob As DATA_BLOB = New DATA_BLOB
Dim entropyBlob As DATA_BLOB = New DATA_BLOB

Dim prompt As _
CRYPTPROTECT_PROMPTSTRUCT = New CRYPTPROTECT_PROMPTSTRUCT
InitPrompt(prompt)

Try
Try
InitBLOB(plainTextBytes, plainTextBlob)
Catch ex As Exception
Throw New Exception("Cannot initialize plaintext BLOB.", ex)
End Try

Try
InitBLOB(entropyBytes, entropyBlob)
Catch ex As Exception
Throw New Exception("Cannot initialize entropy BLOB.", ex)
End Try

Dim flags As Integer = CRYPTPROTECT_UI_FORBIDDEN

If keyType = keyType.MachineKey Then
flags = flags Or (CRYPTPROTECT_LOCAL_MACHINE)
End If

Dim success As Boolean = CryptProtectData( _
plainTextBlob, _
description, _
entropyBlob, _
IntPtr.Zero, _
prompt, _
flags, _
cipherTextBlob)

If Not success Then
Dim errCode As Integer = Marshal.GetLastWin32Error()

Throw New Exception("CryptProtectData failed.", _
New Win32Exception(errCode))
End If

Dim cipherTextBytes(cipherTextBlob.cbData) As Byte

Marshal.Copy(cipherTextBlob.pbData, cipherTextBytes, 0, _
cipherTextBlob.cbData)

Return cipherTextBytes
Catch ex As Exception
Throw New Exception("DPAPI was unable to encrypt data.", ex)
Finally
If Not (plainTextBlob.pbData.Equals(IntPtr.Zero)) Then
Marshal.FreeHGlobal(plainTextBlob.pbData)
End If

If Not (cipherTextBlob.pbData.Equals(IntPtr.Zero)) Then
Marshal.FreeHGlobal(cipherTextBlob.pbData)
End If

If Not (entropyBlob.pbData.Equals(IntPtr.Zero)) Then
Marshal.FreeHGlobal(entropyBlob.pbData)
End If
End Try
End Function

End Class


Then in our .RDP file generator we just call out to our dot net encryptor like this.

DPAPI.Encrypt(DPAPI.KeyType.MachineKey, "MyPassword", Nothing, "psw")

Now, this is worth stressing again that exposing passwords for RDP clients can be a very dangerous idea. Someone can grab ahold of those and cause all sorts of havoc so your management of these security tools needs to be very well understood. Storage, access, and management of this data is one of the most critical components of your security strategy and anytime your exposing this information you need to be aware of the ramifications of that action.

#    Comments [1] |