Are que getting null or empty with some Request.ServerVariables
When you convert your ASP application to run on Windows Azure it is a good
to put attention to the methods that are used to get the user IP Address.
Normally the recommendation will be to use Request.UserHostAddress however
our friend Alex has found that this property can return null or empty.
After some research Alex found that there are several scenarios under which
you must check both the REMOTE_ADDR and the HTTP_X_FORWARD_FOR server variables:
More info:
http://forums.asp.net/t/1138908.aspx and
http://meatballwiki.org/wiki/AnonymousProxy
A possible code snipped that can provide a value for the client address can be:
public static string ReturnIP()
{
var request = System.Web.HttpContext.Current.Request;
var ServerVariables_HTTP_X_FORWARDED_FOR = (String)request.ServerVariables["HTTP_X_FORWARDED_FOR"];
var ServerVariables_REMOTE_ADDR = (String)request.ServerVariables["REMOTE_ADDR"];
string ip = "127.0.0.1";
if (!string.IsNullOrEmpty(ServerVariables_HTTP_X_FORWARDED_FOR) &&
!ServerVariables_HTTP_X_FORWARDED_FOR.ToLower().Contains("unknown"))
{
ServerVariables_HTTP_X_FORWARDED_FOR = ServerVariables_HTTP_X_FORWARDED_FOR.Trim();
string[] ipRange = ServerVariables_HTTP_X_FORWARDED_FOR.Split(',');
ip = ipRange[0];
}
else if (!string.IsNullOrEmpty(ServerVariables_REMOTE_ADDR))
{
ServerVariables_REMOTE_ADDR = ServerVariables_REMOTE_ADDR.Trim();
ip = ServerVariables_REMOTE_ADDR;
}
return ip;
}
In the previous code the HTTP_X_FORWARDED_FOR value is examined first and if it is not null or unknown then ip address of the client
is gotten from there.
Windows Azure is a great platform and the escalatity oportunities are great,
and deployment time is also great.
You can have all your website up and running in just 10-15minutes.
But… and yes there is always a but.
Sometimes you can have a WebSite that is not that static, that as a matter of fact
you are changing its views constantly. Specially if some ideas are not finished.
And yes you can test locally, but there is also a situation where you might want to have that flexibility.
Well looking around I found a very interesting solution by
Maatern Balliauw. http://blog.maartenballiauw.be/post/2009/06/09/A-view-from-the-cloud-(or-locate-your-ASPNET-MVC-views-on-Windows-Azure-Blob-Storage).aspx
What he proposes is to use windows azure storage as a virtual file system, so you can with simple tools
like the Windows Azure Explorer modify your web pages without the need of going through a lengthy republish process.
So go ahead and keep enyoing Azure
Typical ASP applications were built as a layer of simple ASP with some
COM+ components that did the heavy lifting.
Now, when you migrate your ASP application to ASP.NET and you also migrate your
COM+ components to .NET then you might encounter some issues with security.
One common issue is impersonation.
Sometimes the COM+ were created to use the current user account.
And there is a slight
difference between ASP and ASP.NET:
“Impersonation is when ASP.NET executes code in the context of an authenticated and authorized client. By default, ASP.NET does not use impersonation and instead executes all code using the same user account as the ASP.NET process, which is typically the ASPNET account. This is contrary to the default behavior of ASP, which uses impersonation by default. In Internet Information Services (IIS) 6, the default identity is the NetworkService account.”
That will cause errors in your ASP.NET application like:
To solve this issue you must use ASP.NET Impersonation, and to enable impersonation go to the web.config file and add:
<identity impersonate=”true”/>
For more info on impersonation see: http://msdn.microsoft.com/en-us/library/aa292118(v=vs.71).aspx
I had a situation where the IIS reported that the targetFramework=4.0 attribute
was not supported.
I’m not sure why it started reporting it, but I fixed it with this:
%windir%\Microsoft.NET\Framework\v4.0.21006\aspnet_regiis.exe -i
During a migration from ASP to ASP.NET one of the biggest hurdles is finding a way to deal with the include files.
ASP was a interpreted environment whether ASP.NET is compiled and this difference is very important because you need to find ways more natural in .NET to do some things you used to do in ASP.
For example in ASP you used to have a set of common functionality that was included in your files. What will you do with that?
For ASP ASP.NET in VB.NET is a lot easier. One of the things you can do is move all those common subs and functions to a Module.
Now if what you have is a ASP.NET Web Site, then just your new modules to the App_Code folder and voila your pages are now able to see that code.
For a ASP.NET Web Application is just a little differente. What you have to do is move your common variables, constants, subs and functions to a Module, but that is not enough to make that code reachable from your mark up, so you have two alternatives:
1. Add an %@import Namespace=”QualfiedNamespaces.Module1” statement for each of your modules.
2. Modify your web.config file and add under system.web something like:
<system.web>
<pages>
<namespaces>
<add namespace="WebApplication1.Module1"/>
</namespaces>
</pages>
That will add an implicit import for all your pages.
For C# it can be a little more complicated. Because you do not have modules like in VB.NET, what you can do is use extension methods, to have a similiar syntax.
ASP.NET has gone a long way. And it might be the right time to start moving your sites to ASP.NET.
But what happens if you have a big investment in JSP. Will I lose all my JSP skills?
Well ASP.NET is not the same as JSP but the MVC view engine concept can help you retain some of the JSP flavor.
A good MVC View Engine project is SharpTiles this project provides a partial implementation of JSTL and Tiles.
Just take a look at the the SharpTiles. Just as they say SharpTiles if for you if “You are a Java Developer and you don’t like .aspx”
Now all we need to do is setup the VB6 code that we will use for migration. To do that follow these steps:
1. On the Source Code Explorer toolback click on the Add Files button:
2. Click the Add Folder button and select the folder with your VB6 files
3. After you select the folder, a list of files found will be shown. Just remove any unneccesary files. For example files like MSSCCPRJ.SCC should be removed. And press OK
Now you have to commit your code the Source Code Repository
4. On the Source code Explorer Right click on Source Control Folder (for this example is MergeExample) and select Check In Pending Changes..
5. Write an apropiate comment and press Check In
As vb6 migration experts in our company we deal everyday with a lot of issues around Interop and serialization.
One important thing to note is the concept of “Bittable Types”. I’m not making up terms. Those terms actually exist. Just see this link in MSDN.
In a few words, a bittable type is a type that has the same representation in managed and unmanaged code.
Why in earth is that important at all?
Because if you are calling that great C++ DLL implemented some years ago that just works ok, you won’t be able to pass a NON-Bittable type because that DLL will expect a binary representation different from that in the .NET virtual machine.
This is also an issue in other scenarios like:
- Serializing content to files
- Sending messages through messaging mechanisms like named-pipes or sockets.
Well, we have just introduced the problem so now let’s think on a nice solution for this problem.
Well Bittable Types are:
The following types from the System namespace are blittable types:
So now let’s look at a couple of non-BITTABLE types
DateTime
To test this differences let’s make a small test in VB6 and write a Date value to a file:
Private Sub SaveDateToFile()
Open "C:\test1.bin" For Binary Access Write As #1
Dim d1 As Date
d1 = "1/1/2009"
Put #1, , d1
Close #1
End Sub
Now let’s make a quick program in Vb.NET
Sub Main()
Dim f As System.IO.FileStream = System.IO.File.Open("C:\test2.bin", IO.FileMode.Create, IO.FileAccess.Write)
Dim fw As New System.IO.BinaryWriter(f)
Dim d As Date
d = Convert.ToDateTime("1/1/2009")
Dim val As Long = d.ToBinary()
fw.Write(val)
fw.Close()
Main2()
End Sub
If we compare these files we will have:
So the values are obviously different. This is because VB6 Date are stores with the OLE Automation DateFormat
So let’s change the C# code for something like:
Sub Main2()
Dim f As System.IO.FileStream = System.IO.File.Open("C:\test3.bin", IO.FileMode.Create, IO.FileAccess.Write)
Dim fw As New System.IO.BinaryWriter(f)
Dim d As Date
d = Convert.ToDateTime("1/1/2009")
fw.Write(d.ToOADate())
fw.Close()
End Sub
And now when we compare the files we will have:
So to make your Date values compatible with VB6 format you must user the DateTime method .ToOADate. Now if you are calling a DLL that expects a Date value in the same format used by VB6 then you will have to do this:
Dim d As Date
d = Convert.ToDateTime("1/1/2009")
Dim handle As System.Runtime.InteropServices.GCHandle = System.Runtime.InteropServices.GCHandle.Alloc(d.ToOADate(), Runtime.InteropServices.GCHandleType.Pinned)
Dim memory_address As IntPtr = handle.AddrOfPinnedObject()
Try
APICall(memory_address)
Finally
d = DateTime.FromOADate(System.Runtime.InteropServices.Marshal.ReadInt64(memory_address))
handle.Free()
End Try
String
Most of the time you wont have to deal with String marshalling because adding marshaling tags to your API call solves most of the problems, but if you arent that luckyly then you might do something like:
IntPtr ptrToStringVar = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(strVar);
try
{
APICall(ptrToStringVar);
}
finally
{
strVar = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(ptrToStringVar);
System.Runtime.InteropServices.Marshal.FreeHGlobal(ptrToStringVar);
}
NOTE: if you have an API that might return an string with /0 characters you must call the API with System.Runtime.InteropServices.Marshal.PtrToStringAnsi(ptrToStringVar,size), if you do that the Framework will take in consideration the size bytes at the ptrToStringVar memory address.
Double and Singles
At least between VB6 and VB.NET the double and single types follows the same format. Well, at least, that is the result of my tests.
Try it yourself, the following shows a simple test for double variables:
VB6
Private Sub SaveDoubleToFile()
Open "C:\test1.bin" For Binary Access Write As #1
Dim d1 As Double
d1 = 1.123
Put #1, , d1
Close #1
End Sub
Sub Main()
SaveDoubleToFile
End Sub
.NET
Module Module1
Sub Main()
Dim f As System.IO.FileStream = System.IO.File.Open("C:\test2.bin", IO.FileMode.Create, IO.FileAccess.Write)
Dim fw As New System.IO.BinaryWriter(f)
Dim d As Double
d = 1.123
fw.Write(d)
fw.Close()
End Sub
End Module
So you could make an api call in those cases with something like:
Dim handle As System.Runtime.InteropServices.GCHandle = System.Runtime.InteropServices.GCHandle.Alloc(d, System.Runtime.InteropServices.GCHandleType.Pinned)
Dim ptr As System.IntPtr = handle.AddrOfPinnedObject()
Try
APICall(ptr)
Finally
handle.Free()
End Try
One of our clients wanted to change the CreateObject function migration for a function of their own. So they wanted all cases like:
Dim x As Object
Set x = CreateObject("Excel.Application")
To be migrated to something like:
Excel.Application x = (Excel.Application) Utils.MyCreateObject("Excel.Application", "");
Our migratio vb6migration tool provides a new cool feature called CustomMaps. This feature allows you to provide some simple but useful changes to the way things get migrated.
For this case follow these steps:
1. Open the Visual Basic Upgrade Companion.
2. In the Tools Menu choose:
3. Create a new CustomMaps File and an an entry like the following:
Notice the Source name is VBA.Interaction.CreateObject. To find out this name you can look in your VB6 IDE, right click on the CreateObject and select goto Definition.
and for the target name just put the implementation that you what, for example you can write a function like:
class Utils
{
public static object MyCreateObject(string className,params object[] ignoreRestParams)
{
return Activator.CreateInstance(Type.GetType(className));
}
}
and set the SourceName to Utils.MyCreateObject (or NameSpace.Utils.MyCreateObject to use the fully qualified name). You just need to set the New Reference Name column because we will not change the definition of the function.
If you have your ASP.NET application for example in c:\inetpub\wwwroot\WebApplication1 and you want to programmatically get that path just use something like:
string AppPath = Request.PhysicalApplicationPath;