|
Asp.Net Viewstate Viewer - Felix John COLIBRI.
|
- abstract : the structure and display of the Viewstate hidden field
incorporated in Asp.Net exchanges
- key words : ASP.NET - ViewState - Stateless protocol - Viewer
- software used : Windows XP, Asp.Net 1.1 - Delphi 2005
- hardware used : Pentium 2.800Mhz, 512 M memory, 250 G hard disc
- scope : Asp Net 1.1, 2.0, Mono, Delphi 8, 2005, 2005
- level : Asp.Net / Delphi developer
- plan :
1 - ViewState Viewer
This simple utility displays the content of the "viewstate" field, which is a
state carrier of any Asp.Net page.
The HTTP protocol is "stateless": the Server does not maintain the state of a
Client request, since usually there are many Clients, and managing the state
of each of them would overburden the Server and make any scalability
impossible. So the Client must include state information with each request,
and the Server sends back this state to the Client.
In Asp.Net, this state information in encoded in a viewstate field. The content
is coded. So the purpose of this paper is to analyze the viewstate content.
Also let us mention that this paper presents ASP.NET 1.1 viewstate analysis.
The content of ASP.NET 2.0 viewer is slightly different (see the
Reference section)
2 - The Viewstate Analyzer Source Code
2.1 - The Viewstate structure
The Viewstate field is not crypted. But it has a 2 level coding:
- first all HTML punctuations, like "<", "+", "=" are encoded using a %nn
notation. For instance, "+" is coded as "%2B" (where $2B is the Hex value of
the ASCII code of ".")
- then the text is "base 64" encoded: to fit all 256 possible byte value in 7
bits, there is a transcoding from 8 to 7 bits.
The resulting decoded string follows a "compact xml" structure, where the
elements are all between "<" and ">" and the elements of the tags are triples,
pairs, lists arrays and indexes.
The IEBNF (Indented Extended BNF) grammar of this coding is
viewstate= element .
element = triple | pair | list | index .
triple= 't<' element ';' element ';' element '>' .
pair= 'p<' element ';' element '>' .
list= 'l<' { element ';' } .
index= 'i<' value '>' .
value= ANY_BUT ( ';' | '>' ) .
|
In a schematic way, the structure looks like:
Here is the content of a viewstate field (from the Asp.Net example presented
below):
__VIEWSTATE=dDwxMTg3NDQ2OTMwO3Q8O2w8aTwxPjs%2BO2w8dD
w7bDxpPDE%2BOz47bDx0PHQ8O3A8bDxpPDA%2BO2k8MT47aTwyPj
tpPDM%2BO2k8ND47aTw1PjtpPDY%
2BO2k8Nz47aTw4Pjs%2BO2w8cDxBbXN0ZXJkYW07QW0%2BO3A8Qm
FyY2Vsb25hO0JhPjtwPEZyYW5rZn
VydDtGcj47cDxHZW5ldmE7R2U%2BO3A8TG9uZG9uO0xvPjtwPE1pb
GFubztNaT47cDxOZXcgWW9yaztO
eT47cDxQYXJpcztQYT47cDxTeWRuZXk7c3k%2BOz4%2BOz47Oz47P
j47Pj47PjxlNdt3PuVJT7gpuLcE
0Er9eNDy
|
2.2 - The Delphi Source Code
The routine which removes the %nn part is the following:
function f_convert_special_characters(p_string: String): String;
var l_hex_string: ShortString;
l_index: Integer;
begin
l_index:= 1;
Result:= '';
while l_index<= Length(p_string) do
begin
if p_string[l_index]= '%'
then
begin
// -- change specials %26 into &
l_hex_string:= '$00';
l_hex_string[2]:= p_string[l_index+ 1];
l_hex_string[3]:= p_string[l_index+ 2];
Result:= Result+ Chr(StrToInt(l_hex_string));
Inc(l_index, 2);
end
else Result:= Result+ p_string[l_index];
Inc(l_index);
end; // while
end; // f_convert_special_characters
|
dDwxMTg3NDQ2OTMwO3Q8O2w8aTwxPjs+O2w8dDw7bDxpP
DE+Oz47bDx0PHQ8O3A8bDxpPDA+O2k8MT47
aTwyPjtpPDM+O2k8ND47aTw1PjtpPDY+O2k8Nz47aTw4P
js+O2w8cDxBbXN0ZXJkYW07QW0+O3A8QmFy
Y2Vsb25hO0JhPjtwPEZyYW5rZnVydDtGcj47cDxHZW5ld
mE7R2U+O3A8TG9uZG9uO0xvPjtwPE1pbGFu
bztNaT47cDxOZXcgWW9yaztOeT47cDxQYXJpcztQYT47c
DxTeWRuZXk7c3k+Oz4+Oz47Oz47Pj47Pj47
PjxlNdt3PuVJT7gpuLcE0Er9eNDy
|
We are using the following CLASS to decode the base 64 string:
c_base_64_uuencode= class(c_basic_object)
m_trace_uuencode: Boolean;
Constructor create_base_64_uuencode(p_name: String);
procedure trace_uuencode(p_text: String);
procedure convert_stream_to_base_64_strings(p_c_stream: tStream;
p_c_base_64_strings: tStrings);
procedure convert_base_64_strings_to_stream(p_c_base_64_strings: tStrings;
p_c_stream: tStream;
p_raise_exception_or_pad: Boolean);
end; // c_base_64_uuencode
|
The detail of the decode method is in the .zip
And here is the result of decoding our viewstate:
t<1187446930;t<;l<i<1>;>;l<t<;l<i<1>;>;l<t<t<;
p<l<i<0>;i<1>;i<2>;i<3>;i<4>;i<5>;
i<6>;i<7>;i<8>;>;l<p<Amsterdam;Am>;p<Barcelona;Ba>;
p<Frankfurt;Fr>;p<Geneva;Ge>;
p<London;Lo>;p<Milano;Mi>;p<New
York;Ny>;p<Paris;Pa>;p<Sydney;sy>;>>;>;;>;>>;>>;
><e5Ûw>åIO¸)¸·ÐJýxÐò|
|
The triple / pair / list / index structure is already noticeable in this
content.
To decode the "compact xml" format, we are using a recursive parsing routine
which is the following:
procedure analyze_recursive(p_level: Integer; p_title: String);
procedure add_result(p_text: String);
begin
if Trim(p_text)<> ''
then m_c_result_list.Add(f_spaces(p_level* 2)+ p_text);
end; // add_result
procedure analyze_triplet;
// -- "t<xxx;yyy;zzz>"
begin
Inc(l_index, 2);
analyze_recursive(p_level+ 1, ' t1 ');
check_and_skip(';');
analyze_recursive(p_level+ 1,' t2 ');
check_and_skip(';');
analyze_recursive(p_level+ 1,' t3 ');
// -- here should be on "<"
Inc(l_index);
end; // analyze_triplet
procedure analyze_pair;
// -- "p<xxx;yyy>"
begin
Inc(l_index, 2);
analyze_recursive(p_level+ 1,'p1');
// -- here on ";"
check_and_skip(';');
analyze_recursive(p_level+ 1,'p2');
// -- here on ";"
check_and_skip('>');
end; // analyze_pair
procedure analyze_list;
// -- "l<xxx;yyy;zzz;>"
var l_list: String;
begin
Inc(l_index, 2);
if (l_index+ 1<= l_length) and (m_viewstate_string[l_index+ 1]= '<')
then begin
repeat
analyze_recursive(p_level+ 1,'list');
check_and_skip(';');
until (l_index>= l_length) or (m_viewstate_string[l_index]= '>');
l_list:= '';
end
else begin
l_list:= f_string_skip_until_match(m_viewstate_string, '>', l_index);
end;
// -- here on ">"
Inc(l_index);
end; // analyze_list
procedure analyze_index;
var l_index_string: String;
begin
Inc(l_index, 2);
l_index_string:= f_string_skip_until(m_viewstate_string, '>', l_index);
add_result(l_index_string);
// -- here on ">"
check_and_skip('>');
end; // analyze_index
procedure analyze_until_semi_colon;
var l_content: String;
begin
l_content:= f_string_skip_until_in(m_viewstate_string, [';', '>'], l_index);
add_result(l_content);
end; // analyze_until_semi_colon
begin // analyze_recursive
if f_is_triplet
then analyze_triplet else
if f_is_pair
then analyze_pair else
if f_is_list
then analyze_list else
if f_is_index
then analyze_index
else analyze_until_semi_colon;
end; // analyze_recursive
|
and the test of the triplet, pair, list or index parts are simply tested by
checking the "x<" presence. This is the triple detection function:
function f_is_triplet: Boolean;
begin
Result:= (l_index+ 1<= l_length)
and (m_viewstate_string[l_index]= 't')
and (m_viewstate_string[l_index+ 1]= '<')
end; // f_is_triplet
|
The (partial) trace of the analysis is:
>[ 1] analyze_recursive (start)t
>[ 1] analyze_triplet t
>[ 3] analyze_recursive ( t1 )1
>[ 3] analyze_until_; 1
<[ 13] analyze_until_; 1187446930
<[ 13] analyze_recursive
|
And the collected viewstate content is (partial):
1187446930
1
1
0
1
2
...
Amsterdam
Am
Barcelona
Ba
|
2.3 - simple example
Let's analyze the viewstate content of a simple page request and submit. Our
page will simply
- display a Combobox with towns where we hold our Asp.Net trainings
- and a TextBox for the user e-mail
- When the user clicks the "submit" button, we will send him back the next
session date.
This mock-up page is a fake one, but of course, our trainings are not !
Here is how we build the Asp.Net application:
|
start Delphi
|
|
select "file | new | asp.net application"
|
|
Delphi presents the path / file / server dialog
|
|
type the requested information:
|
|
then, in the top right tree project manager, right click the WebForm1.aspx
line, and rename the file name "a_asp_net_trainings.aspx"
|
|
in the top left structure tree, rename the CLASS
|
|
compile to make sure everything is set up correctly
|
|
Delphi compiles, starts Cassini, loads Internet Explorer which displays
our (empty) application
|
|
close Internet Explorer
|
|
open up NotePad and type the following training locations:
Am=Amsterdam
Ba=Barcelona
Fr=Frankfurt
Ge=Geneva
Lo=London
Mi=Milano
Ny=New York
Pa=Paris
sy=Sydney
|
and save the file in the Asp.Net directory under "towns.txt"
|
|
drop a DropDownList on the Form, and rename it "town_dropdownlist_"
|
|
load the towns in town_dropdownlist_ in the Init event:
- declare the method and import System.Io:
interface
type
Tasp_net_training_form =
class(System.Web.UI.Page)
// ...
private
procedure load_combobox_from_file(p_c_dropdown_list: DropDownList;
p_full_file_name: String);
end; // Tasp_net_training_form
implementation
uses System.IO;
|
- write the loading code:
procedure Tasp_net_training_form.load_combobox_from_file(
p_c_dropdown_list: DropDownList;
p_full_file_name: String);
var l_c_stream_reader: StreamReader;
l_town_name_and_value: array of String;
l_town_name: String;
l_town_value: String;
begin
if System.IO.File.Exists(p_full_file_name)
then begin
l_c_stream_reader :=
StreamReader.Create(System.IO.File.OpenRead(p_full_file_name));
try
while l_c_stream_reader.Peek > -1 do
begin
l_town_name_and_value :=
l_c_stream_reader.ReadLine.Split(['=']);
l_town_name := l_town_name_and_value[0];
l_town_value := l_town_name_and_value[1];
p_c_dropdown_list.Items.Add(ListItem.Create(l_town_value,
l_town_name));
end;
finally
l_c_stream_reader.Close;
end; // try finally
end; // file exists
end; // load_combobox_from_file
|
- call this method in the OnInit event:
const k_town_file_name= 'towns.txt';
procedure Tasp_net_training_form.OnInit(e: EventArgs);
begin
InitializeComponent;
inherited OnInit(e);
load_combobox_from_file(town_dropdownlist_,
Request.PhysicalApplicationPath+ k_town_file_name);
town_dropdownlist_.Items.FindByText('New York').Selected := True;
end; // OnInit
|
|
|
add some text to the .HTML in the PageLoad event:
procedure Tasp_net_training_form.Page_Load(sender: System.Object;
e: System.EventArgs);
begin
if IsPostBack
then Response.Write('<BR><BR>PostBack :')
else Response.Write('<BR><BR>Welcome '+ k_town_file_name);
end; // Page_Load
|
|
|
add a Button to the Form
procedure Tasp_net_trainings_form.Button1_Click(sender: System.Object;
e: System.EventArgs);
var l_date_time: System.DateTime;
begin
l_date_time:= System.DateTime.Now.AddDays(30);
Response.Write('Next Asp.Net session: '+ Convert.ToString(l_date_time));
end; // Button1_Click
|
|
|
compile and run
|
|
Delphi starts Cassini, loads Internet Explorer which displays our
application:
|
|
select "Sydney", for instance, and click "Button"
|
|
the submission is sent to Cassini which answers back:

|
Now the viewstate analysis part:
|
to capture the answer viewstate, in Internet Explorer select "display |
source"
|
|
the ascii content of the .HTML is displayed in Notepad, included the
viewstate field (hilighted) :
|
|
copy the viewstate in the ClipBoard, start the viewstate analyzer
application, paste the viewstate in the upper Memo, and click "decode":
|
|
click "analyze"
|
|
the content of the viewstate is displayed:

|
2.4 - Improvements
Just a couple of points:
- using Internet Explorer "view source" only displays the incoming
viewstates. To capture the outgoing viewstate, we should capture the
viewstate in the .Pas file on the Server.
The other solution (which we generally use) is to capture the packets
flowing between the Internet Explorer and the IIS or Cassini Server.
In fact we have incorporated the c_viewstate_analyzer in our
Cassini Packet Capture project, so we can monitor all
file transfers between the Client and the Server
Here is the full data exchange (the viewstates have been split in several
lines, and NOT decoded in this snapshot):
0 ====================================== 345
  -> | GET
/p_asp_net_trainings/a_asp_net_trainings.aspx HTTP/1.1
  -> | Accept: image/gif, image/x-xbitmap, image/jpeg,
image/pjpeg, application/x-shockwave-flash, */*
  -> | Accept-Language: fr
  -> | Accept-Encoding: gzip, deflate
  -> | User-Agent: Mozilla/4.0 (compatible; MSIE 6.0;
Windows NT 5.1; SV1; .NET CLR 1.1.4322)
  -> | Host: localhost:81
  -> | Connection: Keep-Alive
  -> |
  -> |
--------------------------------------279
<-   | HTTP/1.1 200 OK
<-   | Server: Cassini/1.0.0.0
<-   | Date: Thu, 16 Mar 2006 07:45:21 GMT
<-   | X-AspNet-Version: 1.1.4322
<-   | Set-Cookie:
ASP.NET_SessionId=ea40eceyu5j4s1mexanyan55; path=/
<-   | Cache-Control: private
<-   | Content-Type: text/html; charset=utf-8
<-   | Content-Length: 1343
<-   | Connection: Close
<-   |
<-   |
--------------------------------------1344
<-   | <BR><BR>Welcome towns.txt
<-   | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML
4.01 Transitional//EN">
<-   |
<-   | <html>
<-   | <head>
<-   | <title></title>
<-   | </head>
<-   | <body ms_positioning="GridLayout">
<-   | <form name="_ctl0" method="post"
action="a_asp_net_trainings.aspx" id="_ctl0">
<-   | <input type="hidden" name="__VIEWSTATE"
value="dDwxMTg3NDQ2OTMwO3Q8O2w8aTwxPjs+O2w
8dDw7bDxpPDE+Oz47bDx0PHQ8O3A8bDxpPDA+O2k8M
T47aTwyPjtpPDM+O2k8ND47aTw1PjtpPDY+O2k8
Nz47aTw4Pjs+O2w8cDxBbXN0ZXJkYW07QW0+O3A8Qm
FyY2Vsb25hO0JhPjtwPEZyYW5rZnVydDtGcj47c
DxHZW5ldmE7R2U+O3A8TG9uZG9uO0xvPjtwPE1pbGF
ubztNaT47cDxOZXcgWW9yaztOeT47cDxQYXJpcz
tQYT47cDxTeWRuZXk7c3k+Oz4+Oz47Oz47Pj47Pj47
PjxlNdt3PuVJT7gpuLcE0Er9eNDy" />
<-   |
<-   | <select name="town_dropdownlist_"
id="town_dropdownlist_" style="Z-INDEX: 1; LEFT: 14px; POSITION: absolute;
TOP: 14px">
<-   | <option value="Am">Amsterdam</option>
<-   | <option value="Ba">Barcelona</option>
<-   | <option value="Fr">Frankfurt</option>
<-   | <option value="Ge">Geneva</option>
<-   | <option value="Lo">London</option>
<-   | <option value="Mi">Milano</option>
<-   | <option selected="selected" value="Ny">New
York</option>
<-   | <option value="Pa">Paris</option>
<-   | <option value="sy">Sydney</option>
<-   |
<-   | </select>
<-   | <input type="submit" name="Button1"
value="Button" id="Button1" style="Z-INDEX: 2; LEFT: 254px; POSITION:
absolute; TOP: 14px" />
<-   | </form>
<-   | </body>
<-   | </html>
<-   |
1 ====================================== 985
  -> | POST
/p_asp_net_trainings/a_asp_net_trainings.aspx HTTP/1.1
  -> | Accept: image/gif, image/x-xbitmap, image/jpeg,
image/pjpeg, application/x-shockwave-flash, */*
  -> | Referer:
http://localhost:81/p_asp_net_trainings/a_asp_net_trainings.aspx
  -> | Accept-Language: fr
  -> | Content-Type: application/x-www-form-urlencoded
  -> | Accept-Encoding: gzip, deflate
  -> | User-Agent: Mozilla/4.0 (compatible; MSIE 6.0;
Windows NT 5.1; SV1; .NET CLR 1.1.4322)
  -> | Host: localhost:81
  -> | Content-Length: 417
  -> | Connection: Keep-Alive
  -> | Cache-Control: no-cache
  -> | Cookie:
ASP.NET_SessionId=ea40eceyu5j4s1mexanyan55
  -> |
  -> |
__VIEWSTATE=dDwxMTg3NDQ2OTMwO3Q8O2w8aTwxPjs%2BO2w8dD
w7bDxpPDE%2BOz47bDx0PHQ8O3A8bDxpPDA%2BO2k8MT47aTwyPj
tpPDM%2BO2k8ND47aTw1PjtpPDY%
2BO2k8Nz47aTw4Pjs%2BO2w8cDxBbXN0ZXJkYW07QW0%2BO3A8Qm
FyY2Vsb25hO0JhPjtwPEZyYW5rZn
VydDtGcj47cDxHZW5ldmE7R2U%2BO3A8TG9uZG9uO0xvPjtwPE1p
bGFubztNaT47cDxOZXcgWW9yaztO
eT47cDxQYXJpcztQYT47cDxTeWRuZXk7c3k%2BOz4%2BOz47Oz47
Pj47Pj47PjxlNdt3PuVJT7gpuLcE
0Er9eNDy&town_dropdownlist_=sy&Button1=Button
--------------------------------------215
<-   | HTTP/1.1 200 OK
<-   | Server: Cassini/1.0.0.0
<-   | Date: Thu, 16 Mar 2006 07:45:30 GMT
<-   | X-AspNet-Version: 1.1.4322
<-   | Cache-Control: private
<-   | Content-Type: text/html; charset=utf-8
<-   | Content-Length: 1377
<-   | Connection: Close
<-   |
<-   |
--------------------------------------1378
<-   | <BR><BR>PostBack :Next Asp.Net session:
15/04/2006 08:45:30
<-   | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML
4.01 Transitional//EN">
<-   |
<-   | <html>
<-   | <head>
<-   | <title></title>
<-   | </head>
<-   | <body ms_positioning="GridLayout">
<-   | <form name="_ctl0" method="post"
action="a_asp_net_trainings.aspx" id="_ctl0">
<-   | <input type="hidden" name="__VIEWSTATE"
value="dDwxMTg3NDQ2OTMwO3Q8O2w8aTwxPjs+O2w8dDw7bDxpP
DE+Oz47bDx0PHQ8O3A8bDxpPDA+O
2k8MT47aTwyPjtpPDM+O2k8ND47aTw1PjtpPDY+O2k8Nz47aTw4P
js+O2w8cDxBbXN0ZXJkYW07QW0+O
3A8QmFyY2Vsb25hO0JhPjtwPEZyYW5rZnVydDtGcj47cDxHZW5ld
mE7R2U+O3A8TG9uZG9uO0xvPjtwP
E1pbGFubztNaT47cDxOZXcgWW9yaztOeT47cDxQYXJpcztQYT47c
DxTeWRuZXk7c3k+Oz4+Oz47Oz47P
j47Pj47PjxlNdt3PuVJT7gpuLcE0Er9eNDy" />
<-   |
<-   | <select name="town_dropdownlist_"
id="town_dropdownlist_" style="Z-INDEX: 1; LEFT: 14px; POSITION: absolute;
TOP: 14px">
<-   | <option value="Am">Amsterdam</option>
<-   | <option value="Ba">Barcelona</option>
<-   | <option value="Fr">Frankfurt</option>
<-   | <option value="Ge">Geneva</option>
<-   | <option value="Lo">London</option>
<-   | <option value="Mi">Milano</option>
<-   | <option value="Ny">New York</option>
<-   | <option value="Pa">Paris</option>
<-   | <option selected="selected"
value="sy">Sydney</option>
<-   |
<-   | </select>
<-   | <input type="submit" name="Button1"
value="Button" id="Button1" style="Z-INDEX: 2; LEFT: 254px; POSITION:
absolute; TOP: 14px" />
<-   | </form>
<-   | </body>
<-   | </html>
|
- the exercise clearly demonstrates that the viewstate only contains
- whatever the .HTML protocol does not transfers
- anything the Asp.Net developper added to the viewstate (not displayed)
In our case, the .HTML already contained the DropDownList items (in red):
<BR><BR>PostBack :Next Asp.Net session: 15/04/2006 07:33:42
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title></title>
</head>
<body ms_positioning="GridLayout">
<form name="_ctl0" method="post"
action="a_asp_net_trainings.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE"
value="dDwxMTg3 ... gpuLcE0Er9eNDy" />
<select name="town_dropdownlist_" id="town_dropdownlist_"
style="Z-INDEX: 1; LEFT: 14px; POSITION: absolute; TOP: 14px">
<option value="Am">Amsterdam</option>
<option value="Ba">Barcelona</option>
<option value="Fr">Frankfurt</option>
<option value="Ge">Geneva</option>
<option value="Lo">London</option>
<option value="Mi">Milano</option>
<option value="Ny">New York</option>
<option value="Pa">Paris</option>
<option selected="selected" value="sy">Sydney</option>
</select>
<input type="submit" name="Button1" value="Button" id="Button1"
style="Z-INDEX: 2; LEFT: 254px; POSITION: absolute; TOP: 14px" />
</form>
</body>
</html>
|
but .HTML could not code the "key=value" list which is an Asp.Net feature.
So this part was placed in the viewstate (in blue, partial)
- we could improve the decoding of the viewstate. For instance, we could try
to link the displayed values to the Form controls.
3 - Download the Sources
We will not Here are the source code files:
The .ZIP file(s) contain:
- the main program (.DPR, .DOF, .RES), the main form (.PAS, .DFM), and any
other auxiliary form
- any .TXT for parameters, samples, test data
- all units (.PAS) for units
Those .ZIP
- are self-contained: you will not need any other product (unless expressly
mentioned).
- for Delphi 6 projects, can be used from any folder (the pathes are RELATIVE)
- will not modify your PC in any way beyond the path where you placed the .ZIP
(no registry changes, no path creation etc).
To use the .ZIP:
- create or select any folder of your choice
- unzip the downloaded file
- using Delphi, compile and execute
To remove the .ZIP simply delete the folder.
The Pascal code uses the Alsacian notation, which prefixes identifier by
program area: K_onstant, T_ype, G_lobal, L_ocal, P_arametre,
F_unction, C_lasse etc. This notation is presented in the Alsacian Notation
paper.
As usual:
- please tell us at fcolibri@felix-colibri.com if you found some errors, mistakes, bugs or had
some problem downloading the file. Resulting corrections will be helpful
for other readers
- we welcome any comment, criticism, enhancement, other sources or reference
suggestion. Just send an e-mail to fcolibri@felix-colibri.com.
- or more simply, enter your (anonymous or with your e-mail if you want an
answer) comments below and clic the "send" button
- and if you liked this article, talk about this site to your fellow
developpers, add a link to your links page ou mention our articles in
your newsgroup posts when relevant. That's the way we operate: the more
traffic and Google references we get, the more articles we will write.
4 - References
We first found the Viewstate structure in the following page:
5 - The author
Felix John COLIBRI works at the Pascal
Institute. He programs in Pascal since 1979, and is mainly active in the area
of custom software
development and training, and is a frequent speaker at Borland
Developer Conferences. His web site features
tutorials, technical papers about programming with full downloadable source
code, and the description and calendar of forthcoming Delphi,
Interbase, Asp.Net, Ado.Net and OOP / UML training sessions.
|