惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Attack and Defense Labs
Attack and Defense Labs
The GitHub Blog
The GitHub Blog
C
Check Point Blog
博客园_首页
MongoDB | Blog
MongoDB | Blog
N
Netflix TechBlog - Medium
F
Full Disclosure
Microsoft Security Blog
Microsoft Security Blog
爱范儿
爱范儿
Recent Announcements
Recent Announcements
阮一峰的网络日志
阮一峰的网络日志
G
GRAHAM CLULEY
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
T
Threat Research - Cisco Blogs
C
Cybersecurity and Infrastructure Security Agency CISA
V
Vulnerabilities – Threatpost
K
Kaspersky official blog
博客园 - 司徒正美
S
Schneier on Security
T
The Exploit Database - CXSecurity.com
Project Zero
Project Zero
云风的 BLOG
云风的 BLOG
Cisco Talos Blog
Cisco Talos Blog
Know Your Adversary
Know Your Adversary
雷峰网
雷峰网
V
V2EX - 技术
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Spread Privacy
Spread Privacy
罗磊的独立博客
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
S
Security Affairs
SecWiki News
SecWiki News
Schneier on Security
Schneier on Security
O
OpenAI News
Jina AI
Jina AI
PCI Perspectives
PCI Perspectives
Cyberwarzone
Cyberwarzone
Y
Y Combinator Blog
Apple Machine Learning Research
Apple Machine Learning Research
B
Blog RSS Feed
I
InfoQ
D
Docker
P
Palo Alto Networks Blog
Recorded Future
Recorded Future
M
MIT News - Artificial intelligence
博客园 - Franky
B
Blog
Scott Helme
Scott Helme
博客园 - 叶小钗
D
DataBreaches.Net

博客园 - 小呆也行

数据库分区(一) SQL SERVER 分区 向大数据进军 SqlServer中查看数据库所有表的表空间和索引空间信息 C#winform部署中自定义配置文件 SQL 遍历父子关系表(二叉树)获得所有子节点 所有父节点(转) url问题 - 小呆也行 - 博客园 COM+的使用(转) Sql中查找数据库中,所有包含字段的表名 死亡机器(转) 厚黑口才学大全(读后感) ASP向ASP.AET迁移要注意的问题(转) Linq的模糊查询 - 小呆也行 - 博客园 0.4-0.3==0.1 听懂面试官问题背后的潜台词 聪明人把知道说出来,而智者则不声张 我一进教室就震惊了 注册assembly的问题 学习.NET 事例网站
ASP TO ASP.NET migration,a new approach (Reprinted)
小呆也行 · 2010-06-12 · via 博客园 - 小呆也行

Introduction

Even though it’s possible to execute asp and asp.net pages on the same server and even in the same directory,

the communication between the two systems is difficult. For example, the Session and Application objects are

not shared.

This coexistence in quasi-sealed execution containers limits the simplicity one could expect from a migration

from ASP to ASP.NET. Many solutions exist to this problem, but this memo proposes a new one that consists in

the creation of an ASP execution environment inside the ASP.NET infrastructure.

Existing solutions

Renaming pages to .aspx / compatibility mode

The oldest solution has been a part of ASP.NET from the start. It consists in renaming asp pages to give them

the .aspx extension and setting up ASP compatibility mode (<%@ Page aspcompat=true %>) if the pages

use STA mode COM components. In this case, the constraints are heavy because the differences between the

two environments are too important for the ASP code to run without modifications.

The differences are precisely the same that have to be addressed by code migration tools. Among them:

• Update all internal and external inbound hyperlinks to .aspx

• Arrays start at 0

• Replace Request.QueryString(“values”)(1) expressions with Request.QueryString(“values”)(0)

(careful with checkboxes)

• Eliminate html blocs from fonctions and procedures. This code is illegal:

Sub DisplayCount
for i = 1 to 10
%
><%=i%><br><%
next
End Sub

• Parentheses become mandatory around procedure arguments, whereas they were forbidden before.

• Visual Basic.NET is strongly typed.

• Parameters are passed by value instead of by reference.

• Objects don’t have default properties: you can’t write RS(“Column”) anymore; you have to write RS

(“Column”).Value.

One of the key reasons not to use this method is that it leads to leaving badly adapted and optimized ASP code

in the .NET environment.

Alternate shared Session objects

Such solutions appeared very soon in the .NET history. They replace the intrinsic Session object with a new

object that stores its values into a database, and that exposes both a COM and a .NET interface. The problem

of this kind of approach is that the ASP and/or the ASP.NET applications have to be modified to use the new

Session object. Furthermore, storing the Session into a database has a performance impact in some cases. The

alternate Session objects are not always clean implementations and can lead to other compatibility or

performance issues.

Microsoft proposes such an object in a MSDN article: How to Share Session State Between Classic ASP and

ASP.NET.

This approach has the great advantage of leaving the ASP code in its natural execution environment, requiring

little change in the code except for the use of the Session. Thus, regressions are not a big risk.

ASP Interop, a new approach

This new approach enables the ASP scripts to execute in an environment that is as close as possible to the

original ASP environment. Almost no code modification is necessary from the ASP and ASP.NET sides. Even

references to the Session can be kept unchanged in the source code.

The MsScriptControl control

To execute an ASP page, IIS uses the Microsoft scripting engine. This engine is one of the simplest ways to

build a scriptable application. All you have to do is instantiate a COM component, MsScriptControl, and feed it

the intrinsic application-specific COM objects that will be accessible to the scripts. For IIS, these objects are

Response, Request, Server, Application and Session.

All we’re doing here is make ASP.NET scriptable the same way ASP made IIS scriptable in the first place.

Instead of exposing the IIS intrinsic objects, we’ll expose .NET intrinsic objects in a COM wrapper.

IIS configuration

For the ASP scripts to be directed to ASP.NET, we need to change the IIS configuration so that the Web

application uses the ASP.NET ISAPI filter for the .asp extension. This is done from the IIS MMC console, in the

web site’s property sheet:

 

Then, we have to tell ASP.NET which HttpHandler to use for the asp extension. This can be made by adding this

line to the web.config or machine.config file:

<add verb=*” path=*.asp” type=”AspInterop.AspHandlerFactory,AspInterop” />

Exposing the intrinsic objects

The Session and other intrinsic .NET objects that we want to expose to the scripting engine are unfortunately

not ready to be exposed as COM components. One has to write wrappers around. This also enables us to make

their API closer to their ASP equivalents, so that the original scripts don’t have to be modified in most

situations.

To expose the objects as COM components, we sign the assembly and register it.

The object model

The proxy classes that we expose to the scripting engine are:

• AspApplication

• AspCookie

• AspCookieCollection

• AspError

• AspRequest

• AspResponse

• AspServer

• AspSession

Of course, we expose these classes to the scripting engine under their ASP names (Application, Cookie, etc.).

The particular implementations of each of these objects make them more than simple proxies. For example, the

lack of default properties in the .NET world forbids us to expose an object such as Request.Form at the same

time as a collection and as a string (as far as I know; perhaps .NET COM wrappers enable the specification of a

default property; to investigate). This is one of the very few compatibility issues that we’ll encounter with this

tool.

Please note that exposing the .NET Session object to ASP enables us to provide ASP scripts with the advanced .

NET session modes (database session, or session servers). One could even expose the Cache object to ASP

scripts.

From ASP to VBScript

To make the ASP page executable, we need to transform the mixed HTML and script into pure VBScript. This is

what the CreateScript method does. This method is today far from being optimized or even complete. It just

transforms HTML blocks into Response.Write instructions, while keeping the original line numbers in sync

with the original script (this is to simplify error treatment, as we’ll see). It only understands script blocks

delimited by <% %> and <%= %>, and ignores <script runat=server> blocks, @Page directives and

#includes. It only treats VBScript for now, and would need to be rewritten for Javascript or other script

languages. It is clearly the part of the system that needs the most changes and ameliorations, but it is enough

as a proof of concept.

Finally, it’s worth noting that Response.Write is often used in ASP to transmit potentially null parameters (for

example in database applications). This is a problem for COM Marshaling, that triggers an exception if the

parameter is transmitted as a string (the COM null is not the null .NET object, and is thus inhomogeneous with

string). Reflexion brings the solution to this problem through the InvokeMember method, which addresses

the default property of a COM object if an empty string is provided as the method name to invoke:

Ot.InvokeMember(“”, BindingFlags.GetProperty, null, literal, new Object[] {});

GetDefaultMEmbers and GetProperty, as opposed to InvokeMember, do not use IDispatch and are thus

unable to properly access the COM object that hides under the literal that’s been passed to Response.Write.

The Response.Write method first tries to use the literal as a string, then as a native type (using IsPrimitive),

and finally invokes the default property if everything else failed.

Error treatment

To enable the scripting engine’s error messages to display, we must attach a delegate to MSScriptControl to

handle errors: AspErrorHandler.

This function searches errors in the original script as well as in the parsed script the line that caused the error.

It is to make this search easier that CreateScript conserves the original line numbers when interpreting an

ASP page.

Test and Validation

To test and validate the system, I’ve added two ASP test pages (test.asp and exec.asp), and one ASP.NET page

(test.aspx). These pages test most ASP features (including ADO database interaction), and validate the

successful bidirectional communication between ASP and ASP.NET using Application, Session and Cookies.

Test.asp is a very typical ASP script, but it works just as well, and with no modification to the code both with

the original ASP engine and with our new engine.

Limitations

The first limitation comes from the very nature of the system: to work properly, and expose native .NET objects

to the COM scripting engine, all parameters (and thus all output) must use COM marshaling, which is probably

a big performance hit in comparison with the original engine, which stays in the COM world throughout the

whole page lifecycle.

The inexistence of default properties for .NET objects will force the users to change some ASP scripts for them

to work with the new engine. For example, to enumerate the cookies, one will have to write

for each cookiename in Request.CookieCollection

instead of

for each cookieName in Request.Cookies

This could perhaps be corrected by using non-trivial COM declarations of the .NET objects. It is a question to

investigate.

The engine is for the moment limited to <% %> blocks and to VBScript, and it doesn’t handle the global.asa

file or include files.

I also would have like to implement a cache system for the ASP scripts. But is it feasible to serialize COM

objects into the ASP.NET cache? We could still cache the parsed ASP pages. One would also have to investigate

if it would be interesting for performance to keep a pool of MSScriptControl objects.

It should be noted that the performance issues (that are still to be measured) are probably not a very big issue

as the common scenario for such a component is a temporary one: people will use ASP Interop to progressively

migrate a project to ASP.NET. The performance hit will only last while the migration is not finished and will

even attenuate as the project evolves to completion.

Conclusion

This system is only a proof of concept in its present state. Still, it is by far the easiest migration path from ASP

to ASP.NET. If works surprisingly well, with very few ill effects, and it is still very light and easy to set up.

There a lot to do to make it a tool that can safely be used in a production environment, though.

Source code

The source code can be downloaded from:

http://www.dotnetguru.org/articles/Reflexion/AspVersAspDotNet/images/NotDotNet.zip

An addition to the original paper:

ASP Interop as a way to use legacy ASP classes from .NET

Exactly in the same way that the framework provides a tool to generate proxy classes to call Web Services as if

they were native local .NET objects, we could provide a tool that generates a proxy class to call legacy ASP,

VBScript or Jscript functions and classes from the .NET environment.

This would enable complex migration scenarios that no session-sharing tool can provide.

Here’s a working example of a proxy class that successfully executes an ASP function from .NET:

代码

namespace NotDotNet.AspInterop {
/// <summary>
/// Calling an ASP function from .NET using the Script Control
/// </summary>
public class TestInterop {
private TestInterop() {
}
// Calls the Add function from Add.asp
public static int CallAdd(int a, int b) {
// Prepare the script control
ScriptControlClass scc = new ScriptControlClass();
scc.AllowUI 
= false;
scc.Language 
= "vbscript";
scc.UseSafeSubset 
= false;
// Read the source code
FileStream fs = null;
StreamReader sr 
= null;
string asp = "";
try {
fs 
= new FileStream(HttpContext.Current.Server.MapPath("~/Add.asp"),
FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
sr 
= new StreamReader(fs, Encoding.Default);
asp 
= sr.ReadToEnd();
}
catch (Exception e) {
throw new FileLoadException("Couldn't read""~/Add.asp", e);
}
finally {
sr.Close();
fs.Close();
}
scc.AddCode(asp);
// Add intrinsec variables
AspMarshaler Return = new AspMarshaler("Return");
scc.AddObject(Return.Name, Return, 
true);
AspMarshaler A 
= new AspMarshaler("A", a);
scc.AddObject(A.Name, A, 
true);
AspMarshaler B 
= new AspMarshaler("B", b);
scc.AddObject(B.Name, B, 
true);
// Call the ASP function
scc.ExecuteStatement("Return.Value=Add(A.Value, B.Value)");
// Return the results from the Asp function
return (int)Return.Value;
}
}
}
TestInterop.cs

The exemple calls the following trivial ASP function, defined in add.asp:

Function Add(ByVal a, ByVal b)
Add 
= a + b
End Function

The code that calls this function from .NET is in testadd.aspx.cs and is very simple, similar to a Web Service

call:

private void btnAdd_Click(object sender, EventArgs e) {
int a = int.Parse(tbA.Text);
int b = int.Parse(tbB.Text);
lblResult.Text 
= TestInterop.CallAdd(a, b).ToString();
}

The proxy class is defined in TestInterop.cs. The interesting part of the class is the code that prepares the

parameter as intrinsic COM objects for the script engine:

代码

// Add intrinsec variables
AspMarshaler Return = new AspMarshaler("Return");
scc.AddObject(Return.Name, Return, 
true);
AspMarshaler A 
= new AspMarshaler("A", a);
scc.AddObject(A.Name, A, 
true);
AspMarshaler B 
= new AspMarshaler("B", b);
scc.AddObject(B.Name, B, 
true);
// Call the ASP function
scc.ExecuteStatement("Return.Value=Add(A.Value, B.Value)");
// Return the results from the Asp function
return (int)Return.Value;

The AspMarshaler object is a very simple object that has a name and a value property, and is exposed as a

COM object to the scripting engine. The parameters and the return value are exposed as intrinsic objects to the

scripting engine so that it can transmit the values of the .NET variables to the ASP function.

The scripting engine provides all the necessary data about the contents of a given script file (a feature similar

to reflection) to build an automated proxy-generating tool.

I think this migration scenario has not even been considered by our users, even though it would be an

extremely easy one.

源PDF文件: asp2aspnet.pdf

示例代码:  NotDotNet.zip