2010-12-16

SoapExceptionWrapper

I wrote this code quite a while ago, but since it’s quite reusable (and very simple) here it is:

using System;
using System.Collections.Generic;
using System.Text;
using System.Web.Services.Protocols;
using System.Xml;

namespace SupportLibrary.ExceptionHandling
{

    /// <summary>
    /// Wrapper Class for Soap Exception from JBoss.
    /// Parses the XML in the Detail XML Node, exposing StatusCode and StatusDescription as properties.
    /// </summary>
    /// <example>
    /// int errorCode = -1;
    /// try
    /// {
    ///     WebService.DoSomething();
    /// }
    /// catch(SoapException soapex)
    /// {
    ///     SoapExceptionWrapper wrapper = new SoapExceptionWrapper(soapex);
    ///     int errorCode = wrapper.StatusCode;
    ///     string errorDescription = wrapper.StatusDescription;
    /// }  
    /// </example>
    public class SoapExceptionWrapper
    {
        private SoapException _ex;

        /// <summary>
        /// The XmlNode containing the Detail information about the error.
        /// </summary>
        public XmlNode DetailXml
        {
            get
            {
                return _ex.Detail.FirstChild;
            }
        }

        /// <summary>
        /// Status code from SoapException Detail XML.
        /// </summary>
        public int StatusCode
        {
            get
            {
                string codeStr = "-1";
                if(DetailXml != null && DetailXml.ChildNodes.Count > 0)
                    codeStr = DetailXml.SelectSingleNode("StatusCode").InnerText;
                int codeInt = -1;
                int.TryParse(codeStr, out codeInt);
                return codeInt;
            }
        }

        /// <summary>
        /// Status description from SoapException Detail XML.
        /// </summary>
        public string StatusDescription
        {
            get
            {
                if (DetailXml != null && DetailXml.ChildNodes.Count > 0 && DetailXml.SelectSingleNode("StatusDescription") != null && DetailXml.SelectSingleNode("StatusDescription").InnerText != "")
                {
                    return DetailXml.SelectSingleNode("StatusDescription").InnerText;
                }
                return _ex.Message;
            }
        }

        /// <summary>
        /// Constructor using default values.
        /// </summary>
        /// <param name="soapex"></param>
        public SoapExceptionWrapper(SoapException soapex)
        {
            _ex = soapex;
        }
    }
}

2010-09-27

Generic cache manager

The following class is a generic cache manager. It requires a reference to System.Web.

public static class CacheManager
{
    public static T GetObject<T>(string key)
    {
        if (System.Web.HttpRuntime.Cache.Get(key) != null) return (T)System.Web.HttpRuntime.Cache.Get(key);
        return default(T);
    }

    public static void AddObject<T>(T customObject, string key, TimeSpan duration)
    {
        if (System.Web.HttpRuntime.Cache.Get(key) == null)
            System.Web.HttpRuntime.Cache.Insert(key, customObject, null, System.Web.Caching.Cache.NoAbsoluteExpiration, duration);
    }
}

2010-09-23

SQL Server 2008: No connection could be made because the target machine actively refused it

An old problem which still causes me some trouble now and again.

So I got the following error:

No connection could be made because the target machine actively refused it. Microsoft SQL Server Error 10061

So first thing to check is wether or not the server is set up to accept remote connections.

  1. Open up SQL Server Management Studio (SSMS).
  2. Right-click on the server instance, select Properties…
  3. Click on the “Connections” option on the left side.
  4. Verify that the “Allow remote connections to this server” option is checked.

If it was not enabled, you might try to restart the sql server service by running “NET STOP mssqlserver” and “NET START mssqlserver” in a command window. You need to restart the service for any configuration changes to take effect.

Second thing you might check is if the server has enabled TCP/IP.

  1. Open SQL Server Configuration Manager.
  2. Expand the SQL Server Network Configuration node.
  3. Make sure that TCP/IP is enabled.

Remember again that you must restart Sql Server for changes to be effective.

Final thing is to go into the Surface Area configuration and enable the functionality you need. This used to be a separate configuration tool in SQL Server 2005, but now it is integrated into SSMS.

  1. Open up SQL Server Management Studio (SSMS).
  2. Right-click on the server instance, select Facets…
  3. Select the “Surface Area Configuration” facet from the dropdown at the top.
  4. You may at least want to set the “AdHocRemoteQueriesEnabled” to true, and maybe some of the other options as well.

Remember to restart SQL Server.

2010-09-13

Pregnancy Ticker Screen Saver

I have created a HTML page to be used with the HTML Screen Saver from http://myweb.tiscali.co.uk/djmclean/htmlscreensaver.html.

The actual screen saver must first be installed from the link above.

After it has been installed, create a HTML Page and paste the following markup into it, and then save it somewhere (preferably in a dedicated new directory).

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body bgcolor="#0080C0" topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0" scroll="no">
<div id="tickerDiv" style="width:1600px;height:1000px; background-image:url('scrback.jpg'); background-repeat: no-repeat; background-position:center center; border-width: 0; margin: 0 0 0 0;" >
<table id="kwsTickerLayoutTable" border="0" cellspacing="2" cellpadding="1" style="background-color:#FFEEFF;text-align:left;position:absolute;">
<tr><td rowspan="3" style="background-color:green;"><a id="kwsTickerCountdownUrl" href="" style="border:0;"><img id="kwsTickerCountdownImage" width="100px" alt="" src="" style="border: 1px solid #EEDDEE;" /></a></td><td style="font-style:normal; font-family: Arial; font-size: x-large; font-weight: bold; font-variant: normal; color: #008080;"><label id="kwsTickerCountdownText1">test</label></td></tr>
<tr><td style="font-style:normal; font-family: Arial; font-size: x-large; font-weight: bold; font-variant: normal; color: #008080;"><label id="kwsTickerCountdownText2">test</label></td></tr>
<tr><td style="font-style:normal; font-family: Arial; font-size: x-large; font-weight: bold; font-variant: normal; color: #008080;"><label id="kwsTickerCountdownText3">test</label></td></tr>
</table>
</div>
</body>
<script language="javascript" type="text/javascript">
    var now = new Date(); //today
    var dob = new Date(2011, 2, 9); //date of birth
    var doc = new Date(2010, 5, 1); //start date of pregnancy
    var one_day = 1000 * 60 * 60 * 24; //milliseconds in one day
    var days_total = Math.ceil((dob.getTime() - now.getTime()) / (one_day)); //number of days left
    var weeks = Math.floor(days_total / 7); //number of weeks left
    var days = days_total % 7; //number of days in addition to the weeks left
    var days_gone_total = Math.ceil((now.getTime() - doc.getTime()) / one_day); //number of days gone
    var weeks_gone = Math.floor(days_gone_total / 7); //number of weeks gone
    var days_gone = days_gone_total % 7; //number of days in addition to the weeks gone
    var lab1 = document.getElementById('kwsTickerCountdownText1'); //get label 1
    var lab2 = document.getElementById('kwsTickerCountdownText2'); //get label 2
    var lab3 = document.getElementById('kwsTickerCountdownText3'); //get label 3
    var img = document.getElementById('kwsTickerCountdownImage'); //get image element
    var url = document.getElementById('kwsTickerCountdownUrl'); // get url element of image element
    lab1.innerHTML = (weeks_gone + 1) + 'th week';
    if (days_gone > 0) { lab2.innerHTML = weeks_gone + ' weeks and ' + days_gone + ' days on the way.'; } else { lab2.innerHTML = weeks_gone + ' weeks on the way.'; }
    if (days > 0) { lab3.innerHTML = 'Only ' + weeks + ' weeks and ' + days + ' days left!'; } else { lab3.innerHTML = 'Only ' + weeks + ' weeks left!'; }
    img.setAttribute('src', 'http://images.3dpregnancy.com/en/2D/200/' + weeks_gone + '-weeks-pregnant.jpg');
    url.setAttribute('href', 'http://3dpregnancy.parentsconnect.com/calendar/' + weeks_gone + '-weeks-pregnant.html');

    var tickerDiv = document.getElementById('tickerDiv');
    tickerDiv.style.width = screen.width;
    tickerDiv.style.height = screen.height;

    window.setTimeout("Tick()", 500, "javascript");
</script>
<script language="javascript" type="text/javascript">
    function Tick() {
        var tabl = document.getElementById('kwsTickerLayoutTable');
        var x = Math.floor(Math.random() * (screen.width - tabl.offsetWidth)) + 'px';
        tabl.style.left = x;
        var y = Math.floor(Math.random() * (screen.height - tabl.offsetHeight)) + 'px';
        tabl.style.top = y;
        window.setTimeout("Tick()", 2000, "javascript");
    }
</script>
</html>

If you want to have a background image, save a JPG-file called “scrback.jpg” into the same directory. If you don’t want a background image (if you are concerned about burn-in etc.), you may need to remove the following text: background-image:url('scrback.jpg') from the markup above.

Lastly, go into the screen saver options on your computer and configure the HTML file you created as the one you want your HTML screen saver to use.

Pregnancy ticker

The following HTML is a pregnancy ticker. The HTML fetches an image from http://www.3dpregnancyticker.com. Click on the image to go to the web site. To put in your own due date you will need to change the values for the “dob” and “doc” variables. It may be used as a desktop item on Windows. I have also used it on a HTML Screen Saver.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body>
<div style="height:100%;width:100%;vertical-align:middle;text-align:center;background-color:MidnightBlue;">
<table id="kwsTickerLayoutTable" border="0" cellspacing="2" cellpadding="1" style="background-color:#FFEEFF;text-align:left;">
<tr><td rowspan="3" style="background-color:green;"><a id="kwsTickerCountdownUrl" href="" style="border:0;"><img id="kwsTickerCountdownImage" width="100px" alt="" src="" style="border: 1px solid #EEDDEE;" /></a></td><td style="font-style:normal; font-family: Arial; font-size: x-large; font-weight: bold; font-variant: normal; color: #008080;"><label id="kwsTickerCountdownText1">test</label></td></tr>
<tr><td style="font-style:normal; font-family: Arial; font-size: x-large; font-weight: bold; font-variant: normal; color: #008080;"><label id="kwsTickerCountdownText2">test</label></td></tr>
<tr><td style="font-style:normal; font-family: Arial; font-size: x-large; font-weight: bold; font-variant: normal; color: #008080;"><label id="kwsTickerCountdownText3">test</label></td></tr>
</table>
</div>
</body>
<script language="javascript" type="text/javascript">
    var now = new Date(); //today
    var dob = new Date(2011, 2, 9); //date of birth
    var doc = new Date(2010, 5, 1); //start date of pregnancy
    var one_day = 1000 * 60 * 60 * 24; //milliseconds in one day
    var days_total = Math.ceil((dob.getTime() - now.getTime()) / (one_day)); //number of days left
    var weeks = Math.floor(days_total / 7); //number of weeks left
    var days = days_total % 7; //number of days in addition to the weeks left
    var days_gone_total = Math.ceil((now.getTime() - doc.getTime()) / one_day); //number of days gone
    var weeks_gone = Math.floor(days_gone_total / 7); //number of weeks gone
    var days_gone = days_gone_total % 7; //number of days in addition to the weeks gone
    var lab1 = document.getElementById('kwsTickerCountdownText1'); //get label 1
    var lab2 = document.getElementById('kwsTickerCountdownText2'); //get label 2
    var lab3 = document.getElementById('kwsTickerCountdownText3'); //get label 3
    var img = document.getElementById('kwsTickerCountdownImage'); //get image element
    var url = document.getElementById('kwsTickerCountdownUrl'); // get url element of image element
    lab1.innerHTML = (weeks_gone + 1) + 'th week';
    lab2.innerHTML = weeks_gone + ' weeks and ' + days_gone + ' days on the way.';
    if (days > 0) { lab3.innerHTML = 'Only ' + weeks + ' weeks and ' + days + ' days left!'; }
    else { lab3.innerHTML = 'Only ' + weeks + ' weeks left!'; }
    img.setAttribute('src', 'http://images.3dpregnancy.com/en/2D/200/' + weeks_gone + '-weeks-pregnant.jpg');
    url.setAttribute('href', 'http://3dpregnancy.parentsconnect.com/calendar/' + weeks_gone + '-weeks-pregnant.html');
</script>
</html>

2010-06-14

SQL Server 2008 Client Tools Install Pain

In this blog post http://nomisit.wordpress.com/2009/04/21/installing-sql-server-2008-client-tools-what-a-pain/ it is described how you need to upgrade Visual Studio 2008 to SP1 if you are installing any of these…

  • BIDS
  • Management tools (Basic or Full version)
  • Integration Services
  • … and you have previously installed VS2008, then you will need to upgrade it to SP1.

    I did install a trial version of VS2008 but now I have uninstalled everything, but still getting the same failed requirement.

    So now I am trying to install SP1, even if I don’t know what products it could be upgrading since I removed them all. Perhaps it is something like a C++ runtime or something that was left behind by the uninstaller?

    The SP1 installer seemed to be stuck on “WebDesignerCore_KB950278”. But proceeded after quite a while (20 minutes, maybe more).

    Then it took a long time to install “VS90sp1-KB945140-X86-ENU”.

    I got a Fatal Error at the end of the install, so now I don’t know what was installed or not. So either try to reinstall SP1 or try to install the client tools?

    Tried to install client tools, and to my surprise it succeeded!

    2010-05-19

    Wrapping Web Service Proxy objects to Common Cargos using Serialization

    In my previous post I described a way to wrap web service proxy objects to common cargo objects using reflection. This method works only for objects with only value type properties.

    Update: The performance of this code may not be the best.

    internal U wrapToCargoBySerialization<T, U>(T source, U target)
    {
        UTF8Encoding encoding = new UTF8Encoding(true);

        XmlRootAttribute rootAttribute = new XmlRootAttribute();
        XmlSerializer xmlSerializerSource = new XmlSerializer(typeof(T), rootAttribute);
        MemoryStream stream = new MemoryStream();
        xmlSerializerSource.Serialize(stream, source);
        string xml = encoding.GetString(stream.ToArray());
        xml = xml.Replace("<?xml version=\"1.0\"?>", string.Empty);

        MemoryStream ms = new MemoryStream(encoding.GetBytes(xml));

        XmlSerializer xmlSerializerTarget = new XmlSerializer(typeof(U),rootAttribute);

        return (U)xmlSerializerTarget.Deserialize(ms);
    }

    Usage example:
    CommonObjects.Customer cust;
    cust = wrapToCargoBySerialization(wsCustomer, cust);

    Note that I had to remove the <?xml … /> declaration before deserializing.

    A prerequisite for using this method is that the objects have the same structure. To achieve this I simply copy the web service objects from the web reference to a common cargo project. The common objects are used for passing information between layers in the application.

    2010-04-29

    Windows Installer Cleanup Utility

    Freeing up space on your hard disks is an ever ongoing battle for some people, for instance if you at some point decided on a too small system partition (“10 Gigs must surely be enough?”).

    So in the Windows directory (on Windows XP at least) there is a folder called “Installer” where many install files will be found. It may be tempting to just delete all files here, freeing up many gigs of space, but that could cause problems later, for instance if you want to upgrade some program that needs the old version to be uninstalled first.

    So this is where the Windows Installer Cleanup Utility comes into play. Read this excellent blog post on where to get it and how to use it: http://blogs.msdn.com/heaths/archive/2007/01/31/how-to-safely-delete-orphaned-patches.aspx

    Essentially after installing the utility, you may run a command that will delete orphaned installation files, that is install files for programs that are no longer on your system.

    I managed to free up enough space to install MS VS 2008 Professional… :)

    2010-04-13

    Syncroniziong files with FreeCommander and Compare It!

    Up until now I have mostly used SourceSafe and TFS for comparing files. Recently I had a situation where I had to do a so called “Baseless Merge” in TFS. This worked quite well for most files. Then there were some files that were not as easy to merge. So a manual merge was required. Not being quite happy with the file comparing offered by VS, I searched and found a nice option:

    • Use FreeCommander to find all files that have differences.
    • Use Compare It! to compare and syncronize the contents of files.

    To be able to use the “Compare left and right sides” option in FreeCommander, you need to go into Extras –> Settings in FreeCommander, in the Programs section, and set “Compare files” to “C:\Program Files\Compare It!\wincmp3.exe” assuming that’s where Compare It! is installed.

    FreeCommander may be found at http://www.freecommander.com.

    Compare It! may be found at http://www.grigsoft.com.

    I think using these two great tools together is synergy in action!

    2010-04-09

    Chinese comments

    I have been getting some comments, I am guessing they are in Chinese. This may seem unnecessary to point out, but anyway here goes:

    I DON’T UNDERSTAND CHINESE!

    So please stop commenting in any languages except English, or in some rare cases where I have blogged in Norwegian where this language may be used (I also understand Swedish and Danish and a bit of German).

    2010-02-26

    Common Table Expression for Database Structure

    When copying data from one database to another, breaking constraints is always a problem. If you do not insert data in a certain sequence, you will get foreign key violations. So I tried to work out a CTE that starts with all tables that have no foreign keys, then the tables referencing them, then the once referencing them again, and so on… So came up with this:

    WITH CTE (name,object_id, lvl)
    AS
    (
        select name, object_id, 0 AS lvl from sys.tables
        where object_id NOT IN (select parent_object_id from sys.foreign_keys)
       
    UNION ALL

        select tbls.name, tbls.object_id , lvl + 1 from sys.tables tbls
        join sys.foreign_keys keys on tbls.object_id = keys.parent_object_id
        join CTE on CTE.object_id =  keys.referenced_object_id
    )
    SELECT * FROM CTE
    OPTION (MAXRECURSION 10000)

    Note that this will not work if you have self-referencing tables, ie. tables that have a foreign key pointing to its own primary key. Also the same tables may appear many times because they reference the same tables. And also if the same table have many foreign keys it will appear many times.

    The conclusion is that this does not solve my problem, but it was fun to create the CTE anyway.

    2010-02-16

    Experiences on upgrading EPiServer 4.51 to 4.62B part 2

    Ok, so the upgrade has been done, but will the project compile?

    Sadly the answer is no.

    I got the exact same problems described here: http://world.episerver.com/templates/forum/pages/thread.aspx?id=19575&epslanguage=en. It took some time for me to understand the answer to the part they did give an answer to. They didn’t answer the second question at all.

    Problem 1: WsrpHelper doesn’t exist any more

    My solution, which saves me from having to go through 8-10 places and correct the code, was to create a new class inside the WsrpPortal.aspx.cs file:

    class WsrpHelper
    {
        public static IConsumerEnvironment ConsumerEnvironment
        {
            get { return ElektroPost.Wsrp.Consumer.ConsumerContext.ConsumerEnvironment; }
            set { ElektroPost.Wsrp.Consumer.ConsumerContext.ConsumerEnvironment = value; }
        }

        public static void EnsureConsumerEnvironment()
        {
            if (ElektroPost.Wsrp.Consumer.ConsumerContext.ConsumerEnvironment == null)
            {
                ElektroPost.Wsrp.Consumer.ConsumerContext.ConsumerEnvironment = ConsumerFactory.ConsumerEnvironmentInstance();
            }
        }
    }

    Problem 2: LanguageManager.GetContextLanguage() is obsolete

    It says in the error message that you should use LanguageContext.Current as a replacement, but that would create a type conversion exception.

    I am guessing that you can to use LanguageContext.Current.CurrentUILanguageID as a replacement for GetContextLanguage() (please do correct me if I’m wrong).

    DISCLAIMER:
    I haven’t been able to test my solution, since the webs I am working on don’t actually use the portal framework. My code compiles now, so the web is up and running, and I am happy for the time being. If anyone finds any flaw in my solutions please don’t hesistate to leave a comment.

    2010-02-15

    The EPiServer Offline Package is a ZIP-file

    So if you for instance need to just upgrade the database, not the whole EPiServer installation, you can just rename the offline upgrade package from .pkg to .zip, and you will find all the files used by the upgrade wizard inside.

    Ypou can create an offline upgrade package from and to the versions you want using the EPiServer Manager, as mentioned in my previous post.

    The database scripts are located in the “Upgrade” folder, and if you sort them ascending by name and run them in that order, you should be able to upgrade the database “manually”.

    Scenarios where this method may be used may be

    • you did/tested the upgrade in your development environment and then deployed the upgraded files
    • you have already upgraded EPiServer on a staging server and the production database (which is an earlier version) should be restored on the staging server

    The EPiServer Manager does other things than just copying files, like registering components in the GAC, so use the EPiServer Manager whenever possible. Also, remember to always have nescessary backups available.

    2010-02-12

    Experiences from upgrading EPiServer from version 4.51 to 4.62B

    Ok, so previously I have upgraded my project files and solutions from Visual Studio 2003 to 2008, and have upgraded to master pages. I wrote about this in an earlier post: http://stgaup.blogspot.com/2010/02/upgrading-episerver-to-masterpages.html.

    So now the next step. Upgrading the EPiServer version from 4.51 to 4.62B.

    Since I am going to upgrade at least 2 sites, and someone in IT Operations needs to do the same upgrade on the production systems, I thought it would be a good idea to create an offline install. I had to google a bit and eventually fond out that to create an offline package, you need to:

    1. Start EPiServer Manager
    2. On the Tools menu, select “Create offline installation…”
    3. Click Next
    4. Select “Upgrade”
    5. Select From version in my case 4.51)
    6. Select To Version (4.62B)
    7. Specify directory where the package should be created.
    8. Click “Create”.

    I had no problems with this part of the job.

    Next I had to do the upgrade:

    1. Backup all files and the EPiServer database.
    2. Right-click the web site to upgrade in the EPiServer Manager, and select “Upgrade…”.
    3. Click Next.
    4. Select “Offline” and then browse to the package created before.
    5. Click Next.
    6. Review upgrade info, then click “Upgrade”.

    The next thing happening was that the upgrade was being done, and the progress bar was showing some progress, until about 20% on the way. Then I got an error message: “Failed to register ASP.NET client scripts on this site”

    It turns out that the EPiServer Manager is not able to install when there are versions of the .NET Framework installed after version 2.0. So the dirty trick you need to do is to remove the 3.0 and 3.5 folders from the WINDOWS\Microsoft .NET\Framework folder because EPiServer Manager will try to locate aspnet_regiis.exe (and maybe other command line utilities) in those folders, and it’s not there. This thread was helpful: http://world.episerver.com/Templates/Forum/Pages/Thread.aspx?id=17376&epslanguage=en.

    Ok, so after I had moved those folders, it seemed that the upgrade worked, until I tried to browse the web:

    EPiServer 4.62.0.533 can only be used with database version 100, current version is 90. Make sure both database and assemblies are upgraded correctly.

    I didn’t get any errors while upgrading. Everything seemed to go well, but it didn’t. The EPiServer Manager failed to upgrade the database.

    I am assuming at this point that the database upgrade failed because the web was using Windows Integrated security with SQL Server. I thought it could work because I had made sure that my windows user account had owner rights to the database, but sadly it failed.

    Tip: Don’t use Windows Integrated Security with EPiServer (when upgrading) even though Microsoft recommends that as the most secure way of accessing SQL Server. If you do, you will (probably) need to set up your site with impersonation, and turn off anonymous access.

    Ok, so then I tried to run the upgrade once more after setting a SQL Server username/password. I then got the message:

    The site is already up to date (4.62.0.533) – no new versions available.

    So now time for my next dirty trick, one that I have used before, which is to copy the old EPiServer.dll back into the bin folder of the web site. (Of course I had a backup.)

    So new upgrade attempt. First had to restart the EPiServer Manager, because it still thoght the web was version 4.62B. After restarting it said 4.51, as I intended it to.

    This time the upgrade worked as it should, and the web is up and runnig again, now og EPiServer 4.62B.

    IMPORTANT: Copy back the .NET 3.0 and 3.5 Framwork files to their correct location!

    NEXT STEP: Upgrade to CMS 5? Am I brave enough?

    2010-02-10

    My Second SmallBasic program

    This one draws a sinus curve (well a cosinus curve):

    GraphicsWindow.Title = "Hello World!"
    GraphicsWindow.BackgroundColor = "Yellow"
    GraphicsWindow.Width = 320
    GraphicsWindow.Height = 200
    GraphicsWindow.Show()

    oldx = 0
    oldy = Math.Cos(0) * 100 + 100

    For x = 1 To 320
      y = Math.Cos(x/10) * 100 + 100
      GraphicsWindow.DrawLine(oldx,oldy,x,y)
      oldx = x
      oldy = y
    EndFor

    My first SmallBasic program

    Ok, my first was “Hello World”, but this is my first using the GraphicsWindow:

    GraphicsWindow.Title = "Hello World!"
    GraphicsWindow.BackgroundColor = "Yellow"
    GraphicsWindow.Width = 320
    GraphicsWindow.Height = 200
    GraphicsWindow.Show()

    oldx = Math.Sin(0) * 100 + 160
    oldy = Math.Cos(0) * 100 + 100

    For i = 0.1 To 2 * Math.Pi Step 0.1
      x = Math.Sin(i) * 100 + 160
      y = Math.Cos(i) * 100 + 100
      GraphicsWindow.DrawLine(oldx,oldy,x,y)
      oldx = x
      oldy = y
    EndFor

    What does it do? It draws a circle. Great huh? Oh I could have used GraphicsWindow.DrawCircle? Amazing stuff :P

    Btw SmallBasic may be downloaded from http://msdn.microsoft.com/en-us/devlabs/cc950524.aspx.

    2010-02-05

    Upgrading EPiServer to MasterPages

    Initial Status:
    I have already upgraded the EPiServer solution from Visual Studio 2003 / .NET 1.1 to Visual Studio 2008 / .NET 2.0/3.5. First upgraded to VS2005/.NET 2.0 by opening the projects in VS2005 and using the wizard, then converting the project to a web application. Then did the same again, opening the project in VS 2008. Everything seemed to work fine after the upgrade. So next step is to upgrade from using EPiServer:DefaultFramework to using master pages.

    Next steps:
    I found the (very good I might add) article: http://world.episerver.com/Articles/Items/Experiences-from-migrating-to-EPiServer-461-and-ASPNET-20/ and tried to follow the steps described from the section called “Upgrading your custom templates to ASP.NET 2.0”. However there seems to be some little information missing:

    1. In addition to changing the <@ Control … to <@ Master … you must also in code-behind change the inheritance so that the master page inherits from System.Web.UI.MasterPage in stead of EPiServer.WebControls.ContentFramework.
      This will cause some problems:
      1. If you have any references to CurrentPage, you will need to fix it. I fixed it by creating a new property on the master page called CurrentPage:

        public PageData CurrentPage
        {
            get
            {
                PageBase pb = (PageBase)this.Page;
                return pb.CurrentPage;
            }
        }

      2. Also if you are using any commands that are not prefixed and are using functionality from the previous base class you will need to fix them:
        1. Translate(…) => EPiServer.Global.EPLang.Translate(…)
          I solved this by creating a private function in the master page code-behind, which saved me from changing code in multiple places:
        2. private string Translate(string key)
          {
              return EPiServer.Global.EPLang.Translate(key);
          }

        3. GetPage(…) => EPiServer.Global.EPDataFactory.GetPage(…)
          Again I solved it by creating a private function with the same name:
        4. private PageData GetPage(PageReference pageLink)
          {
              return EPiServer.Global.EPDataFactory.GetPage(pageLink);
          }

    2. The process of converting from the 1.1 style of declaring the controls in code-behind to the 2.0 way using the .designer.cs file is not always working as is should. I had to go into the code-behind file and remove declarations of controls, and then made a slight change to the front master page file, and the saved it, so that the controls using the runat=”server” attribute were (automatically) declared in the designer.cs file. Also some events vere explicitly declared in code-behind (opposed to the new way of using “AutoEventWireup=True”. I just removed them as they were doing nothing anyway.

    3. Also had a small problem due to some additions to the asp.net control gallery and poor naming conventions. The old EPiServer Content Framework files declared a conrtol called simply “Menu”. This name crashes with the “System.Web.UI.WebControls.Menu” web control, so I just changed the name.

    And now my web is up and running with EPiServer 4.51 (on VS2008) and using Master Pages (on only one page so far: default.aspx). After changing all aspx-web forms so that they use the new master page, the next step will be to upgrade EPiServer to 4.62B, and thanks to the previously linked article this should hopefully be a piece of cake.

    2010-01-29

    Moving log files

    I am using log4net and other logging in my apps, and the logs are filling up the disks of the test server, so I created a Windows Scheduled Task to move logs to an archive disk, and also delete very old logs from the archive. I move logs older than 2 weeks to the archive and delete logs older than a year from that archive. I created this vb-script (started from the scheduled task) to do the job:

    'VBScript that moves old log files from C:\LogFiles to E:\LogArchive

    'Folders
    Const FOLDER = "C:\LogFiles" 
    Const BACKUP_FOLDER = "E:\LogArchive" 

    'Objects
    Dim objFSO, objFolder, objFolder2, objFile 
    Set objFSO = CreateObject("Scripting.FileSystemObject") 
    Set objFolder = objFSO.GetFolder(FOLDER)

    'Loop and move
    For Each objFile In objFolder.Files 
      If objFile.DateLastModified < DateAdd("w", -2, Now) Then 
        objFile.Move BACKUP_FOLDER & "\" & objFile.Name 
      End If 
    Next

    'Delete very old files from target BACKUP_FOLDER
    Set objFolder2 = objFSO.GetFolder(BACKUP_FOLDER)
    For Each objFile In objFolder2.Files 
      If objFile.DateLastModified < DateAdd("w", -52, Now) Then 
        objFile.Delete
      End If 
    Next

    Plain old ASP-like code. No Types, everything is just Variant types.