Archive for the ‘.Net 1.1’ Category

.Net Framework Service Pack 1 Postback Problem

I applied the .Net Service Pack 1 to a Windows 2003 server last night and the web application served from it stopped submitting postbacks. When the login page was sent to the client (IE 6 on XP, 98, and 2003 that’s all I had to test with at the time), the javascript was preventing the postback from the submit button. The validators all worked, but the page just would not post back. Click the button…nothing. The development machine with XP Pro and the Service Pack 1 did not have the same problems. I ended up rolling back the patch to get things working as it was late and I didn’t find any solutions right away, but this morining I found some other possible workarounds.

There is a discussion of the problem and several potential solutions on the Channel 9 site.

Posted on:
Posted in .Net 1.1 | Comments Off on .Net Framework Service Pack 1 Postback Problem

Custom Text File Publisher for Exception Management Application Block

I needed to create a publisher for exceptions to a text file (XML would be better but the requirement was for text…). I based my publisher on this article I found on C# Corner. But it has one problem associated with using files, that unfortunately loomed large. Concurrency. To a certain extent the code handled the problem by tossing the error into the Event Log if the provider threw an error, but this was not acceptable for my requriement. So I needed to modify the code to handle the issue. Essentially, I modified the overriden Publish procedure to trap the error related to the StreamWriter and then sleep for a set amount of time and try again. After too many failures at writing to the file the error eventually gets written to the event log (so it is not lost).

privateconst int WriteAttempts = 5;
private const int SleepTime = 500;

void IExceptionPublisher.Publish(Exception exception, NameValueCollection additionalInfo, NameValueCollection configSettings)
{

    // Read and set up configuration
    SetConfig(configSettings);

    // Create fileName
    string logFilePath = CreateLogFilePath();

    // Create Message
    string logMessage = CreateLogMessage(exception, additionalInfo);

    int attempts = 0;
    bool written = false;

    // Write the entry to the log file.
    while(!written && attempts <= WriteAttempts)
    {
       
try
       
{
            // Append to file if exists or create new file
           
using ( FileStream fs = File.Open(logFilePath, FileMode.Append,FileAccess.Write))
            {
               
using (StreamWriter sw = new StreamWriter(fs))
                {
                    sw.Write(logMessage);
                    written =
true;
                }
            }
        }
       
catch
       
{
            attempts++;
            Thread.Sleep(SleepTime);
         }
    }
    if(attempts > WriteAttempts)
    {
       
throw(exception);
    }

}

Posted on:
Posted in .Net 1.1, Security | Comments Off on Custom Text File Publisher for Exception Management Application Block

VBCommenter

After a little consternation, got VBCommenter PowerToy working today. Had a problem after installing that was solved on their message board.

It seems to be very useful, but really slows down compiles (of course I already need more RAM so this doesn’t help). Looks like I’ll have to let it build XML files separate from regular compiles.

Now I need to get up to speed on XSLT to turn those XML files into something readable for non-techies. My project is getting a new person to help write documentation and I am sure the info I generate with this tool will help her out significantly.

Posted on:
Posted in .Net 1.1, Visual Studio | Comments Off on VBCommenter

Viewstate Error and Macintoshes?

The public site I maintain is gathering 500 errors of the dreaded “The viewstate is invalid for this page and might be corrupted” type. Strangely enough, the only users seeing this error are Macintosh users, using IE! The problem is not isloated to a single page, it seems to be happening on any page, but not necessarily every page for that user. In some cases it is clear that the user was getting around fine (they were logged in as members to the site), and later the problem occurred.

I cannot find anything on the web except a solitary Usenet post of a person describing the exact same problem.

Here are the agent strings of the browsers recorded when the error occurred:

Mozilla/4.0 (compatible; MSIE 5.0; Mac_PowerPC)
Mozilla/4.0 (compatible; MSIE 5.17; Mac_PowerPC)
Mozilla/4.0 (compatible; MSIE 5.15; Mac_PowerPC)
Mozilla/4.0 (compatible; MSIE 5.16; Mac_PowerPC)

According to the log files, MacPPC users are just under 1% of the site users, so this problem is clearly not a showstopper, but I hate for any users to have a problem. I am hoping to get a Mac set up to test the problem locally, but the agent string doesn’t give me any Mac OS versions….

Posted on:
Posted in .Net 1.1, ASP.NET | Comments Off on Viewstate Error and Macintoshes?

Burned by Trace

I found out the hard way that when using the trace on an ASP.Net page, the interaction of the trace observer is not totally separate from the page. I am working on a page where performance is a big issue, so I have been using Trace extensively to find the weak spots and shore them up. Essentially the page is searching both the Indexing Service and an Oracle database and finding where the two datasets intersect. Because the results are a grid of links, the user can click the links and then use the Back button to return to the results. Of course I was getting the dreaded “Warning: Page has Expired” message after using the back button. The page with the grid is the result of an HTTP post, so the post has expired. Clicking the Refresh button was the only way to get the data back initially. This is bad, as the page already was having performance problems, so rendering it again was not really an option. So I tried setting the page caching options to this:


<%@ OutputCache Duration=”120″ VaryByParam=”None” Location=”Client”%>


This should cause the page to cache on the client. No dice. No matter what I set, the http_cache_control header always reported “no-cache” in the trace results. I tried making the header changes in code. Again, no change. Still reporting “no-cache”. The simple solution to the problem is to turn off tracing on the page. Well, it turns out that with Trace=”true” in the @Page directive, the http_cache_control header is set to “no-cache” no matter what. When the Tracing is off, the “Warning: Page has Expired” message disappears and the page is back in control of setting the headers.


Also, a related tidbit I discovered when trying to solve this is that the page timeout is essentially set to infinity when compiling in debug mode.

Two Good Solutions for Radio Buttons in a Repeater

Basically, the repeater ignores the GroupName attribute and gives each radiobutton in each row a unique name so they no longer operate as a group. This is an acknowledged bug by Microsoft.

A short, elegant solution posted on the Usenet.

A free control to solve the same problem.

I posted about a similar problem previously with checkboxes and the Repeater, but it was a very different solution than the two I found here.

Posted on:
Posted in .Net 1.1, ASP.NET | Comments Off on Two Good Solutions for Radio Buttons in a Repeater

Query the Indexing Service with Ixsso and ASP.Net

As I am continuing to migrate our ASP app to ASP.Net, it has finally come time to address the Indexing Service search. It’s a big feature in the application, and the transition of this piece needs to be seamless. We want to stick with Ixsso for the Indexing Service as opposed to using the Oledb driver. Ixsso is considered to be the faster of the two technologies, even using COM interop (See various of Hilary Cotter’s comments in microsoft.public.inetserver.indexserver). Code snippets for using Ixsso with ASP.Net are pretty sparse compared to using Oledb, so I figured I should post mine.  First, I used the IDE to create a reference to the ixsso Control Library dll and let the IDE make the .Net wrapper for the COM object (christened Cisso by the IDE).

Imports Cisso
Imports System.Security.Principal
Imports System.Data.OleDb

 

Private Function GetIndexResults(ByVal Query As String) As DataTable
Dim Q As New CissoQueryClass
Dim util As CissoUtilClass
Dim da As New OleDbDataAdapter
Dim ds As New DataSet(“IndexServerResults”)

Q.Query = Query
Q.SortBy = “rank[d]”
Q.Columns = “filename, rank, write”
Q.Catalog = “query://DocumentServer/Resumes”
Q.MaxRecords = 1000
util.AddScopeToQuery(Q, “\”, “deep”)
Q.LocaleID = util.ISOToLocaleID(“EN-US”)

Dim impContext As WindowsImpersonationContext = impersonateAnonymous()
da.Fill(ds, Q.CreateRecordset(“nonsequential”), “IndexServerResults”)

Q = Nothing
util = Nothing
impContext.Undo()

Return myDS.Tables(“IndexServerResults”)

End Function


The impersonateAnonymous function is described in a previous post of mine. In our case the anonymous user on the machine has appropriate privledges to query the remote Indexing Service, but the ASP.Net worker process does not so impersonation is in order for the function. That part is probably optional depending on the situation. The rest of it is not very tricky. I tried to fill the DataTable directly without the DataAdapter, but that didn’t work. The CreateRecordset function of the CissoQueryClass returns an ADO recordset and I couldn’t find a cast that worked. The DataAdapter seems to be doing the casting work during the call to Fill.

Sharing Timeout Management with ASP.Net and ASP

I am in a situation where my application is mixed classic ASP and ASP.Net. I am currently migrating the classic ASP app page-by-page into ASPX as I am developing new functionality. When I have to touch an old page, I convert it.

In an attempt to make them appear to be one app to the user, I need to manage the timeout of the two applications to appear as one. The classic ASP app was using Session variables for this, but obviously that would not work easily in a mixed environment. Luckily, the ASP app is not heavily dependent on Session variables; it only uses a few that are set during login, and a couple on particular pages. So after logging in the login redirects to a classic ASP page to set the few necessary Session variables, and that page redirects to the home page.

  1. Login using the ASPX login page
  2. Create a cookie containing context
  3. Redirect to asp page setting session variables
  4. Redirect to ASPX home page

Initially, I thought it would be easy to manage the timeouts, just use the cookie generated by the ASPX login page. The classic ASP pages can check the cookie and update its expiration if needed. Of course, it was not that simple. The cookie that is created by the ASPX pages contains the authentication ticket generated by the current http context. Its expiration is essentially the same as the cookie, based on the time set in web.config. The problem lies with the classic ASP pages. They can update the cookie’s expiration, but not the ticket. If a user spends too much time using classic ASP pages, the ticket in the cookie will be expired next time the user visits an ASPX page, even if the cookie is good. The ASPX pages consider a user timed out if either the cookie has expired or the ticket has expired. They will need to login again without actually timing out simply due to using classic ASP pages for a little too long.

My solution results from the ratio of classic ASP to ASPX pages. Right now it is about 5 classic ASP per ASPX page. Using this ratio I changed the authentication ticket timeout to be 5 times as long as the cookie timeout. In order to make this work the ASPX application needs to manage the cookie created by ASPX pages instead of allowing it to occur manually. At the login page, I allow the ASPX cookie to be created as normal using my own CreateAuthCookie.

Public Shared Function CreateAuthCookie(ByVal UserID As String) As HttpCookie
    Const TicketLife As Double = 4
    Dim Timeout As String = ConfigurationSettings.AppSettings("Timeout").ToString
Dim principalText As String Dim buffer As New IO.MemoryStream Dim formatter As New Runtime.Serialization.Formatters.Binary.BinaryFormatter
    formatter.Serialize(buffer, HttpContext.Current.User)
    buffer.Position = 0
    principalText = Convert.ToBase64String(buffer.GetBuffer())
    'create a forms ticket
Dim ticket As New FormsAuthenticationTicket(1, HttpContext.Current.User.Identity.Name, _
DateTime.Now, DateTime.Now.AddMinutes(Double.Parse(Timeout) * TicketLife), _
False, principalText)

    'Encrypt the ticket
    Dim encTicket As String = FormsAuthentication.Encrypt(ticket)
    'This is the cookie used by both ASP and ASP.Net, ASP never retrieves the value
    Dim TheCookie As New HttpCookie(FormsAuthentication.FormsCookieName)
    TheCookie.Path = FormsAuthentication.FormsCookiePath
    TheCookie.Value = encTicket
    TheCookie.Expires =    DateTime.Now.AddMinutes(Double.Parse(Timeout))

    Return TheCookie
End Function

In the Global.asax, I added code to take the cookie, get the ticket from it and use the ticket to set the HttpContext.Current.User and update its expiration if necessary. If the cookie or the ticket has expired here it will automatically redirect to the login page as a function of ASP.Net.

Private Sub Global_AcquireRequestState(ByVal sender As Object, ByVal e As EventArgs) _
Handles MyBase.AcquireRequestState Dim cookie As HttpCookie = Request.Cookies.Get(FormsAuthentication.FormsCookieName) If Not cookie Is Nothing Then 'Cookie found, decrypt the value and recreate the authentication ticket
' and user context for the page
Dim ticket As FormsAuthenticationTicket Try If cookie.Value = "" Then
'For some reason the cookie exists but there is no value,
' remove the cookie try again
cookie.Expires = DateTime.Now.AddMinutes(-15) Response.Redirect("../login/login.aspx", False) Else ticket = FormsAuthentication.Decrypt(cookie.Value) End If FormsAuthentication.RenewTicketIfOld(ticket) Dim buffer As New IO.MemoryStream(Convert.FromBase64String(ticket.UserData)) Dim formatter As New Runtime.Serialization.Formatters.Binary.BinaryFormatter HttpContext.Current.User = CType(formatter.Deserialize(buffer), IPrincipal) Catch ex As Exception
'Could not set the ticket, remove the cookie cookie.Expires = DateTime.Now.AddMinutes(-15) Response.Redirect("../login/login.aspx", False) End Try End If End Sub

All my ASPX pages inherit from my own base page instead of System.Web.UI.Page. In the PreRender event for my base page my CreateAuthCookie gets set again and added to the Response. My base page has already taken the UserID from the HttpContext and set it as a page property.

Private Sub Page_PreRender(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles MyBase.PreRender Dim AuthCookie As HttpCookie = CreateAuthCookie(Me.User_ID.ToString) Response.Cookies.Add(AuthCookie) End Sub

The classic ASP pages use an include file that just increments the cookie timeout. It also checks the status of the ASP session and resets it if the cookie is still good.


Dim xQString if Request.QueryString <> "" then 
xQString = "?" & Request.QueryString
end if
If
Request.Cookies("EnCoreUser") = "" then
'No cookie, session expired
Session.Abandon
Response.Cookies("EnCoreUser").Expires = DateAdd("n", -15, Now())
response.redirect "../login/login.aspx?ReturnUrl=" & _
Server.URLEncode(Request.ServerVariables("URL") & xQString)
else
'Got aspx cookie, check asp session
if Session("SessionId") <> Session.SessionID then
if Request.Cookies("asp") = "" then
response.redirect "../login/login.aspx?ReturnUrl=" & _
Server.URLEncode(Request.ServerVariables("URL") & xQString)
else
'Cookie is good but classic ASP session ended. Recreate the
' session without forcing another login.
Response.Redirect "../login/verify.asp?userid=" & user_id & "&url=" & _
Server.URLEncode(Request.ServerVariables("URL") & xQString)
end if
else
'All is well, so reset the cookie
Dim sTempx sTempx = CStr(Request.Cookies("EnCoreUser"))
Response.Cookies("EnCoreUser").Path = "/"
Response.Cookies("EnCoreUser").Expires = DateAdd("n", 35, Now())
Response.Cookies("EnCoreUser") = sTempx
end if
end if

As the ratio classic ASP pages to ASPX pages changes, all I need to do is reduce the multiplier to shorten the ticket length accordingly. Eventually, of course, all this work will get tossed out when there are no more classic ASP pages.

Posted on:
Posted in .Net 1.1, ASP.NET | Comments Off on Sharing Timeout Management with ASP.Net and ASP

Forms Authentication Problem

An ASP.Net site we created is having infrequent problems with logins using forms authentication. Essentially what happens is that the user attempts to login and is successful, but then is redirected back to the login page immediately. So it looks like an infinite loop of logins. We have been able to deduce that the cookie is related to the problem. If the user deletes their cookies in IE the problem goes away. The problem is very intermittent, so it is very difficult to reproduce. It is not generating 500 errors or errors in the logs. From extensive Googling, the best I can come up with is the fact that we allowed the cookie to persist across sessions, and the problem is related to that. So I changed the createPersistentCookie parameter to false:

FormsAuthentication.SetAuthCookie(nResult.ToString, False)

Of course, solving the problem is only a wait-and-see in this case, since I can’t reproduce the problem directly. I thought our login code was pretty straightforward, letting ASP.Net do as much of the work as possible.


Imports System.Web.Security.FormsAuthentication
....
.... 'txtEmail, txtPassword are textboxes on the form, lblMessage is a label control Public Sub Login_Click(ByVal snd As System.Object, ByVal e As System.EventArgs) _
Handles LoginButton.Click
Dim NotRegistered As String = " is not a registered email address. “ & _
“Please use the Create A Profile link to register." Dim nResult As Integer
If Page.IsValid Then Dim sPassword As String





sPassword = HashPasswordForStoringInConfigFile(txtPassword.Text, "sha1") nResult = LoginResult(txtEmail.Text, sPassword) 'Validate against the database If nResult = -1 Then 'Not a registered user, display error message lblMessage.Text = txtEmail.Text & NotRegistered ElseIf nResult = -2 Then 'Bad password, set error message lblMessage.Text = "The password for " & txtEmail.Text & _
" is incorrect" ElseIf nResult > 0 Then 'Registered user, nResult is their ID number If Request.QueryString("ReturnUrl") <> "" Then 'Redirect to requsted page RedirectFromLoginPage(nResult.ToString, False) Else 'Go to My Jobs by default SetAuthCookie(nResult.ToString, False) Response.Redirect("../MyJobs/My_Jobs.aspx") End If
End if End If End Sub

Checkboxes and the Repeater

I used a repeater for the first time in my app, and in this instance it is essentially a list of people with checkboxes to the left of their names so the user can select one or more of them and perform an operation (like delete, etc.). When using a datagrid, this is really easy because you can bind the person’s ID as a hidden column, making it very easy to retrieve the primay key. This is not the case with the repeater or datalist as far as I can tell. To top that off, the asp:checkbox control does not have a value attribute where I could store the primary key, like the HTML version. But I needed the asp:checkbox functionality, not the HTML checkbox.

Again, this seemed like a common problem so I decided to perform a Google search. No good solution jumped out at me, until I read this thread from microsoft.public.dotnet.framework.aspnet.webcontrols. It is basically a guy griping about the lack of a value property for the asp:checkbox and a MS Rep apologizing for it. At this point I realized neither of these guys in the thread were “thinking in .Net” (and neither was I when I started my search). Everything is now object-oriented. If a class does not have a feature you need, add it! So that is what I did, and it works great:



Public Class RepeaterCheckBox
    Inherits CheckBox
    Property Value() As String
        Get
            Return CType(ViewState("value"), String)
        End Get

        Set(ByVal Input As String)
            ViewState("value") = Input
        End Set
    End Property
End Class


The important thing to remember here is to make sure the value is going into viewstate so it persists.

Posted on:
Posted in .Net 1.1, ASP.NET | Comments Off on Checkboxes and the Repeater