Friday, 5 September 2008

Disable Event Validation for an ASP.NET control

I blogged yesterday about fixing the 'Invalid postback or callback...' problem in an ASP.NET web page. The posted solution involved programatically turning off the EnableEventValidation property for the entire page. I mentioned that there are some security issues with this approach and that caution is advised when turning off security features in any code that one writes.

Well, with a bit more digging, I have now managed to turn off event validation at the control level rather than the page level. This means that you can ensure that the page is still validated, but that a specific control does not participate in event validation. From a security perspective, this is a significant improvement on the previous solution (but still not perfect).

Very briefly, the problem we are trying to solve is that ASP.NET 2.0 validates postbacks. It does this by storing the unique ID of a control (for example a drop down list) and all of it's possible values in a hash. When the client does a postback, the runtime checks that the unique ID and value combination submitted by the client exist in the hash. If the client has changed the unique id, or changed/added any values the server will not know about it and will not be able to validate the postback. This is to prevent malicious code from spoofing a postback. (see http://odetocode.com/Blogs/scott/archive/2006/03/20/3145.aspx for more info)

With things like AJAX playing about with javascript under the covers, the potential for this happening is increased.

To prevent postback validation at a control level, one must create a custom web control. Imagine that you want to turn off event validation for a drop down list, but the drop down list exposes no property/method to turn off validation.

This can be overcome by creating a custom drop down list and ommiting the [SupportsEventValidation] attribute from the class. This attribute is in fact ommited by default, so all you actually need to do is create a custom control which inherits from System.Web.Ui.WebControls.DropDownList, remove any custom implementations (by default, Visual Studio creates an override for the RenderContents method and the Text property) and you're done!

Now use this control in place of a standard drop down list and hey presto... no more errors :-)

Thursday, 4 September 2008

Invalid Postback or callback argument. A code behind solution to this problem...

I have recently been taking my first tentative steps into the world of AJAX, and believe me, it's a mine field out there!!

A problem that was really bugging me was getting the infamous error:

Invalid Postback or callback argument . Event validation is enabled using in configuration or <%@ Page EnableEventValidation="true" %>in a page. For security purposes, this feature verifies that arguments to Postback or callback events originate from the server control that originally rendered them. If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the Postback or callback data for validation.

As I did not write the code I was working with, I couldn't identify exactly which controls were causing the problem, nor did I know exactly which pages were affected. As a result adding the
EnableEventValidation="False" to the @Page tag of all the aspx files was not possible.

Luckily however, all of the pages on the site derive from a custom Page class (which inherits from System.Web.UI.Page).

This meant that I could override the EnableEventValidation property from the code behind rather than relying on finding all the page tags.

The code in the Page class goes a bit like this:

public override bool EnableEventValidation
{
get {return false;}
set {base.EnableEventValidation = value;}
}

This simply ensures that event validation is always false, regardless of what the @Page tag says!

I hope this helps someone, but it is worth pointing out one or two issues here:
  • EventValidation is a security feature added to ASP.NET 2.0 to prevent postback spoofing. Turning it off could leave your website vulnerable to injection attacks.
  • Overriding the EnableEventValidation at the base class level will ignore anything set in the @page directive. Developers may therefore believe they have told the page to do one thing and be very confused when it does not behave as expected. Event worse, they may never notice that event validation is turned off, which could pose unidentified issues when the website is live.


For more information, you could try http://aspnet.4guysfromrolla.com/demos/printPage.aspx?path=/articles/122006-1.aspx which pointed me in the right direction for this solution. Also
http://odetocode.com/Blogs/scott/archive/2006/03/20/3145.aspx and http://odetocode.com/Blogs/scott/archive/2006/03/21/3153.aspx are very useful.

Collapsing the AjaxToolkit CollapsiblePanelExtender from server code

Well, this one had me going for quite some time so I just thought I'd share my solution with you :-)

First off, i'm tired of typing CollapsiblePanelExtender, so from here on in it's a CPE!

Secondly setting the Collapsed and ClientState on the CPE properties do not work a unless the page/control containing the CPE is refreshed with a postback, which in this scenario it is not.

Imagine, you have page called MyPage.aspx. This has a control called MyControl.ascx on it. MyControl.ascx has a CPE on it called MyCPE. MyCPE has a usercontrol on it (called MyDetails.ascx). MyDetails.ascx has a number of controls on it, but one of them is a 'Cancel' button. You want this to collapse the panel (This is in addition to the CollapseControl already assigned to the CPE). Phew, that's quite a heirarchy, but it's simpler than the one I had to work with!!

There are two main stages to this solution:

First - Wire up events
Wire up the OnClick event of the aforementioned 'Cancel' button to the parent page/control. So if the page MyPage.aspx has a control on it called MyControl.ascx, and this control has MyCPE on it. MyCPE has MyDetails.ascx on it, and MyDetails.ascx has the cancel button on it.

MyControl needs to have an event handler which is fired when the OnClick event is fired on MyDetails.ascx. To acheive this, modify your code something like;

MyControl.ascx.cs

Add the following line to the Page_Load event:

this.MyDetails.CanclButtonHasBeenClicked += CancelButtonClicked

Add the following empty event handler (we'll populate it later):

private void CancelButtonClicked(object sender, EventArgs e)
{
}

MyDetails.ascx.cs

Publicly expose the Cancel OnClick event by adding a public event handler to the class

Public EventHandler CancelButtonHasBeenClicked;

Fire your new event handler when the cancel button is clicked. Add the following to the actual event handler for the cancel button:

CancelButtonHasBeenClicked(sender, e);

Second - Collapse the CPE

Ok, now your parent user control (MyControl.ascx) is notified when the cancel button is clicked on the CPE (via the MyDetails.ascx control). Now you just need to inject some javascript to collapse the CPE. Add the following code to the empty CancelButtonClicked event handler created above:

string JavaScriptCode="$find('MyCPEBehaviour').collapsePanel();";
ScriptManager.RegisterStartupScript(Page, Page.GetTpye(),"MyScript",JavaScriptCode,true);

Thanks to James Ashley who pointed me on the right direction on that one!

Finally, add the behaviour id to your CPE in MyControl.ascx by adding the following to the CPE tag:

BehaviorID="MyCPEBehaviour"

Job Done. Now your CPE should collapse when you click on the 'Cancel' button on MyDetails.ascx within the CPE on MyControl.ascx.

Hopefully this waffle makes some sense and helps someone one day...

Tuesday, 16 October 2007

NCover generates an unhandled exception

Recently whilst setting up a continuous integration environment using cruise control, I came across a problem when running NCover (Version 1.5.5) from NCoverExplorer. I entered all the usual information about profiling applications, etc... and it gave me an error.

Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
Server stack trace: at NUnit.Core.SimpleTestRunner.CountTestCases(ITestFilter filter) at NUnit.Core.DelegatingTestRunner.CountTestCases(ITestFilter filter) at NUnit.Core.DelegatingTestRunner.CountTestCases(ITestFilter filter) at System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(IntPtr md, Object[] args, Object server, Int32 methodPtr, Boolean fExecuteInContext, Object[]& outArgs)
at System.Runtime.Remoting.Messaging.StackBuilderSink.SyncProcessMessage(IMessage msg, Int32 methodPtr, Boolean fExecuteInContext)
Exception rethrown at [0]: at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) at NUnit.Core.TestRunner.CountTestCases(ITestFilter filter) at NUnit.Util.ProxyTestRunner.CountTestCases(ITestFilter filter) at NUnit.Util.AggregatingTestRunner.CountTestCases(ITestFilter filter) at NUnit.Util.AggregatingTestRunner.Run(EventListener listener, ITestFilter filter) at NUnit.ConsoleRunner.ConsoleUi.Execute(ConsoleOptions options) at NUnit.ConsoleRunner.ConsoleUi.Main(String[] args)

This had me baffled for hours. I am using Windows Server 2003 x64 Standard Edition, so I trotted off down a route of 'It has to be because I am using a 64bit windows OS'. After installing the new version of NCover (v2.0.2) which promised to work on a 64bit platform and a newer version of NUnit (2.4.2), it still did not work. I was at my whits end.

The solution was simple however.... My "application arguments" section in the NCoverExplorer NCover runner contained a path and file name to my NUnit configuration as it was not located in the default working folder. This path contained spaces and I did not enter it in quotes (I assumed the NCover Runner would be clever enough to do this for me). So, I put this in quotes and hey presto - success!

I hope this helps prevent someone else going through quite so much pain as I did!

Friday, 31 August 2007

How to run a process at a specified time on Windows Mobile 5.0

I had been banging my head against a brick wall for weeks trying to figure out how to get a process to run in an infinate loop reliably and with good performance on a mobile device. What I really wanted was a windows scheduled task, but usefully this is one of the features not available in windows mobile.

The solution I came across was the CeRunAppAtTime API. This needs a System Time object and the path to an exe. The code is below, but essentially it is a wrapper class which exposes a single static method. This method sets up a path to the program and a time to start the process at (relative to the current time), and calls the API. This class should be able to be added to any mobile solution to allow it to start a process at a specified time.

In my context, this was called as the last command in the program (after everything else had been completed) and it invoked itself so that it ran again after a specified amount of time.

Perhaps this is not an optimum solution, but it solved my problem :-)

public class Processes
{
///


/// Structure representing a system time.
/// This is used to define the time at the application
/// should next be run.
///
private struct SystemTime
{
#region Private properties

private short _year;
private short _month;
private short _dayOfWeek;
private short _day;
private short _hour;
private short _minute;
private short _second;
private short _milliseconds;

#endregion

#region Public Accessors

///


/// Represents the Year component of this SystemTime
///
public short Year
{
get { return _year; }
set { _year = value; }
}

///


/// Represents the Month component of this SystemTime
///
public short Month
{
get { return _month; }
set { _month = value; }
}

///
/// Represents the DayOfWeek component of this SystemTime
///
public short DayOfWeek
{
get { return _dayOfWeek; }
set { _dayOfWeek = value; }
}

///
/// Represents the Day component of this SystemTime
///
public short Day
{
get { return _day; }
set { _day = value; }
}

///
/// Represents the Hour component of this SystemTime
///
public short Hour
{
get { return _hour; }
set { _hour = value; }
}

///
/// Represents the Minute component of this SystemTime
///
public short Minute
{
get { return _minute; }
set { _minute = value; }
}

///
/// Represents the Second component of this SystemTime
///
public short Second
{
get { return _second; }
set { _second = value; }
}

///
/// Represents the Milliseconds component of this SystemTime
///
public short Milliseconds
{
get { return _milliseconds; }
set { _milliseconds = value; }
}

#endregion

///


///
Public constructor
///
The DateTime to populate the SystemTime object with
public
SystemTime(DateTime value)

{

_year = (Int16)value.Year;
_month = (
Int16)value.Month;
_dayOfWeek = (
Int16)value.DayOfWeek;
_day = (
Int16)value.Day;
_hour = (
Int16)value.Hour;
_minute = (
Int16)value.Minute;
_second = (
Int16)value.Second;
_milliseconds = (
Int16)value.Millisecond;

}

}

/// A reference to the API call which allows the scheduling
/// of a process.
/// The success of the invokation
[
DllImport("coredll.dll", CallingConvention = CallingConvention.Winapi)]
private
static extern bool CeRunAppAtTime(string pwszAppName, ref SystemTime lpTime);

/// Start an application at a specified time.
/// The application to start, whether to start it and the
/// number of seconds to elapse before starting it are
/// all defined in the App.Config file
public static void StartApp()
{
string ProgramPath = @"c:\myProgramPath\MyProgram.exe"; SystemTime timeToLaunch = new SystemTime(DateTime.Now.AddSeconds(30);

CeRunAppAtTime(ProgramPath, ref timeToLaunch);
}

}

Note that this code requires a reference to System.Runtime.InteropServices.

NOTE: This does not work in the standard Visual Studio emulator, but it does work on a real device.

Friday, 10 August 2007

A problem has occurred with filesys.exe

So, there I was happily coding away on my Windows Mobile 5.0 Pocket PC project. I made a small change to the code, pressed F5 and suddenly BANG, it stops working. I get an error on the emulator entitled "A problem has occurred with filesys.exe", and visual studio tells me that it has lost communication with the device.

Oops I thought, VS has got it's knickers in a twist and needs to be re-started. No joy. Soft re-set the device. No Joy. Reboot my dev machine. No Joy. Now I'm becomming a little frustrated. Hard reboot the device. No Joy.

I tried all sorts of combinations of actions, but what finally worked was the following....

Delete the source code from my local machine (It's in source control still though!)
Close all programs.
Uninstall the Windows Mobile 5.0 Pocket PC SDK
Uninstall Active Sync
Restart the machine.
Install Windows Mobile 5.0 Pocket PC SDK
Install Active Sync
Restart the machine again
Get the source code back from source control
Open my solution
Run the project

And Hey presto - it's working again.

I have no idea what went wrong, and quite frankly I don't care! It works again and as long as the episode is not repeated, I'll be happy for that.

If anyone can shed any light on the problem however, i'd be interested to know why it happened, and more importantly what can be done to prevent it happening again.

Monday, 6 August 2007

Welcome

Well, finally I have decided to get a blog. Watch this space for nothing in particular about things what may be interesting....