aspcompat performance issue - asp.net

I am using aspcompat page attribute in ASP.NET so the com components I call can get at ASP intrinsic objects (Request, Response, Application, etc)
I have quickly created a new test project, one asp.net page and a vb6 com component.
The page does this:
for (int i = 0; i < 1000; i++)
Application["string" + i] = i.ToString();
Debug.WriteLine(string.Format("{0} done adding strings to app",Environment.TickCount));
var asp = new ASPTest.CompClass();
asp.SetProcessId();
Basically I add stuff in the application object and then call the com component.
Set context = GetObjectContext
If Not context Is Nothing Then
Set app = context("Application")
Set ses = context("Session")
Set resp = context("Response")
If Not app Is Nothing Then
OutputDebugString "" & GetTickCount & " writing response"
resp.Write "I see application from vb <br/>"
OutputDebugString "" & GetTickCount & " before "
For i = 100 To 200
resp.Write GetTickCount & " i = " & app("string" & i) & "<br/>"
Next
OutputDebugString "" & GetTickCount & " after "
End If
Else
OutputDebugString "No context"
End If
The problem is that the more stuff I put in the application the slower the vb code becomes. Not all the code though, just when I first try to invoke any method on intrinsic objects, in my example my first resp.Write call. If I add 10000 items in the app resp.Write takes ten seconds to complete, 60000 => timeout.
It seems that when I touch any intrinsic object the first time the runtime does something nasty to make the objects available to COM.
Have you ever seen this? Any suggestion is much appreciated.

This is almost certainly a problem with threading, probably having the Interop performing thread context switches with every call from the VB component to the classes in the context.
I'm not the expert here, so I might (probably wil) get corrected, but I believe you need to make sure your VB component is also STA, just like the ASP.NET page marked with the aspcompat=true sets it. If you keep all the components in the same thread, there won't be any context switches (which require very expensive marshalling).

Related

WMI/ASP Memory Issues

Ive created a server monitoring script written in classic ASP (Hosted on IIS 7.5).
It has a main page which uses jQuery to automatically reload the content (updating WMI information) It works great, but seems to have an appetite for memory!
There are 4 pages (Service status for 2 servers, printer status on a main DC and disk usage on the same server) The service status pages update every 10 seconds, the printers every 5 seconds and disk usage every minute.
The script basically works for a few hours, then i get code 500 internal server errors, once checking the IIS logs its because of it being "Out of memory" and look at the processes on the server and WmiPrvSvc.exe (NETWORK SERVICE account) is in the hundreds (500mb) and the ISS worker process (w3wp.exe) is using about 50mb (there are other w3ps.exe processes that are higher)
Once i end them both it kicks back into action… Or if i stop the page/requests for a while (varies between 30 sec and 5 mins) the WmiPrvScs ends and i don't get the problem.
Is there a way to minimise the memory usage or is there a command to properly disconnect / clear the memory?
Below is the basics of one of my pages to give you an idea of what's going on...
Thanks, Paul.
<%
strComputer = "."
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colListOfServices = objWMIService.ExecQuery("SELECT Caption, Started FROM Win32_Service WHERE StartMode = 'Auto'")
Set arrServices = Server.CreateObject("System.Collections.ArrayList")
intServices = colListOfServices.Count
For Each objService in colListOfServices
arrServices.Add objService.Caption & "#" & objService.Started
Next
arrServices.Sort()
Response.Write "<table width=""100%"" class=""tblServices"">" & vbCr
For Each strService in arrServices
If InStr(strService, ".NET Framework") = 0 AND InStr(strService, "Sophos Web Intelligence Update") = 0 AND InStr(strService, "Shell Hardware Detection") = 0 Then
' Above services, start at system startup and then stop after nothing to do
arrServiceDetails = Split(strService, "#")
strServiceName = arrServiceDetails(0)
strServiceStatus = arrServiceDetails(1)
End If
Next
Response.Write "</table>"
Set objWMIService = Nothing
Set colListOfServices = Nothing
Set arrServices = Nothing
%>
Are you calling the above code every 10 seconds?
I´m not sure, but i looks like you are creating a new WMI object / connection everytime and that adds up.
Instead try to keep the object around instead of recreating it. Something like a SingleTon class to hold the connection.
Also, try making a strongly typed class instead of the WMI query, you can do that by looking at this site: MGMTClassGen

Running a series of VBScripts within an ASP.net (vb.net) page?

I have a requirement when a user clicks a specific arrangement of radio buttons to run a series of vbscripts (and soon Perl scripts).
I have all of the vbscripts stored server side, they do not need to be on the remote system to run. Oh yes, the scripts are gathering information on remote system in our intranet.
What would be the best way. Currently I have this to run just one script, not multiple...should I keep this or dispose of this idea.
Protected Sub windowsScript(ByVal COMPUTERNAME As String)
' Create an array to store VBScript results
Dim winVariables(1) As String
Dim filePath As String = COMPUTERNAME & "\C$\Windows\somefile.txt"
'Execute PsExec on script
runPsExec(COMPUTERNAME, "systemInfo.vbs", 1)
'Import data from text file into variables
textRead(filePath, winVariables)
System.Threading.Thread.Sleep(1000)
'Delete the file on server - we don't need it anymore
runPsExec(COMPUTERNAME, "systemInfo.vbs", 2)
MsgBox("Windows OS: " & winVariables(0).ToString())
MsgBox("Service Pack: " & winVariables(1).ToString())
End Sub
Also, it is hard to see here because I do have another function "textRead" but what is going on is this particular script is stored client side and the vbscript it outputting to a text file. textRead will read the variable and send a text file back to the server to read it.
This is definitely not what I want to do.
I want to be a little more dynamic, plus with my new scripts...they don't need to be on the client at all.
Any help would be appreciated :)
I'm thinking of making some type of While loop, not sure if that would work.
It's kind of strange to do this through the browser. In my company we collect systeminfo at logontime with a vbscript logonscript and add the result to a logfile which we can access through a webapp to do research. Occasionally when the need rises we run a specific script to gather more data or change some system setting through windows SCCM.
If the goal is to provide the user with info about his system there are some good utilities around which can be run locally (but from a location on a server share).
EDIT
a simple way to start multiple processes
dim scripts_to_run, script
const COMPUTERNAME = 0, SCRIPTNAME = 1, EXTRA_PARAMS = 2
scripts_to_run = Array(_
Array("computer1","script1.vbs",1),_
Array("computer2","script1.vbs",0),_
Array("computer3","script3.vbs",3)_
)
for each script in scripts_to_run
runPsExec script(COMPUTERNAME), script(SCRIPTNAME), script(EXTRA_PARAMS)
runPsExec join(script,",")
next
sub runPsExec(p1, p2, p3)
'here coms your code shat runs the script
wscript.echo p1 & p2 & p3
end sub
or a shorter version
dim scripts_to_run, aArgs
scripts_to_run = Array(_
Array("computer1","script1.vbs",1),_
Array("computer2","script1.vbs",0),_
Array("computer3","script3.vbs",3)_
)
for each aArgs in scripts_to_run
runPsExec aArgs
next
sub runPsExec(aArgs)
'here coms your code shat runs the script
wscript.echo aArgs(0) & aArgs(1) & aArgs(2)
end sub

How to optimize this code that read from xml file

We have ther following classic asp code that read from xml file, it shows a poor performance when there are more than 10 concurrent requests to that page , can someone figure out the performance problem in this code( we know one of the the problems which is using fileSystemObject but we do not have alternatives for it!):
set filesys=server.CreateObject("Scripting.FileSystemObject")
if filesys.FileExists(sourcefile) then
set source = Server.CreateObject("Msxml2.DOMDocument")
source.validateOnParse = false
source.resolveExternals = false
source.preserveWhiteSpace = false
source.load(sourcefile)
If source.ParseError.errorCode <> 0 Then
str_head=source.selectSingleNode("/LIST/ITEM/NEWSITEM/HEADLINE//").text
str_by=source.selectSingleNode("//LIST//ITEM//NEWSITEM//PROVIDER//").text
News_date_orig = source.selectSingleNode("/LIST/ITEM/NEWSITEM/CREATED//").text
str_date= formatdatetime(source.selectSingleNode("//LIST//ITEM//NEWSITEM//CREATED//").text,1)
set bodyNode=source.selectSingleNode("/LIST/ITEM/NEWSITEM//BODY//")
styleFile=Server.MapPath("/includes/xsl/template.xsl")
Set style = Server.CreateObject("Msxml2.DOMDocument")
style.validateOnParse = false
style.resolveExternals = false
style.preserveWhiteSpace = false
style.load(styleFile)
news_full = bodyNode.transformNode(style)
if len(news_full) < 10 then
news_full = str_abstract
end if
DiscriptionKeyWord = stripHTMLtags(news_full)
DiscriptionKeyWord=StrCutOff(DiscriptionKeyWord, 200, "...")
headerTitle=str_head
Set style=nothing
Set source = nothing
end if
set filesys= nothing
The following is stripHTMLtags function:
Function stripHTMLtags(HTMLstring)
Set RegularExpressionObject = New RegExp
With RegularExpressionObject
.Pattern = "<[^>]+>"
.IgnoreCase = True
.Global = True
End With
stripHTMLtags = RegularExpressionObject.Replace(HTMLstring, "")
Set RegularExpressionObject = nothing
End Function
UPDATE: I put a timer to show the execution time for the function that read xml file and found that it takes around 3 seconds on the production server while it takes less than 1 second on my PC! What does tht mean?! I'm lost.
Option Explicit
If your scripts don't begin with Option Explict then make that change now. Then fix all the compile errors that show up. Doesn't help performance but when I see evidence that this biggest of all scripting mistakes is being made it just needs mentioning.
FileSystemObject
I doubt the performance problem is a result of the FileSystemObject, all you are doing is creating an instance and testing for the existance of a file. That is hardly likely to cause a problem.
Having said that I would ditch the FileSystemObject anyway. Just let the script throw an error if there is a problem. Use IIS Manager to map 500.100 status codes to an ASP page that presents a friendly "Something bad happened" page to the user. (500.100 is the status of a request when the script throws an exception). Also test the boolean result of the DOM load method, throw an error when parse error is not 0 also. That way you hand over all the ugly exception handling to the 500.100 handling page and your code can remain clean just dealing with the nominal path of the code.
Tidy up the paths
Perhaps there is a reason why you are using "//" a lot in your paths (but inconsistently) but I'm going to assume there isn't so we can simplify some of the paths:
Dim newsItem: Set newsItem = source.selectSingleNode("/LIST/ITEM/NEWSITEM")
Dim str_head: str_head = newsItem .selectSingleNode("HEADLINE").text
Dim str_by: str_by = newsItem .selectSingleNode("PROVIDER").text
Dim News_date_orig: News_date_orig = newsItem .selectSingleNode("CREATED").text
Dim str_date: str_date = formatdatetime(News_date_orig, 1)
Dim bodyNode: Set bodyNode = newsItem.selectSingleNode("BODY")
Cache XSLTemplate
An area where you could gain some real perfomance improvement is to cache the XSL Transform in the application object (which is possible due to XSLTemplate being a free threaded object). Like this:
Dim xslTemplate
If IsObject(Application("xsl_template")) Then
Set xslTemplate = Application("xsl_template")
Else
Set style = Server.CreateObject("Msxml2.FreeThreadedDOMDocument.3.0")
style.async = false
style.validateOnParse = false
style.resolveExternals = false
style.preserveWhiteSpace = false
style.load Server.MapPath("/includes/xsl/template.xsl")
Set xslTemplate = CreateObject("MSXML2.XSLTemplate.3.0")
xslTemplate.stylesheet = xsl
Set Application("template") = xslTemplate
End If
Dim xslProc: Set xslProc = xslTemplate.createProcessor()
xslProc.input = bodyNode
xslProc.transform()
news_full = xslProc.output
The effort to read, parse and compile the XSL transform is only done once in the whole lifetime of the application.
The most likely culprit
To be honest I suspect the most likely culprit is stripHTMLtags. This sounds like a whole load of string handling and the performance of VBScript string handling is poor. Its especially poor when code is not properly optomised to be aware of the string handling performance limitations (e.g. excessive and repeative string concatenations). Its also likely to be where the most actual VBScript occurs which is often the cause of a performance problem.
You can load the XML into a string on application startup and store it in an Application object.
Then, instead of using source.load, use source.loadXML with this string - you will not be accessing the filesystem anymore. See the documentation for loadXML.
Make sure you use a version of MSXML 3.0 or over to use this method.
Server.CreateObject("Msxml2.DOMDocument.6.0")
Update - seeing as you have thousands of these files and you believe it is infeasible to store them in memory in the Application object, you should be using a database to handle the concurrency issues you are seeing. Store the contents of the files keyed to something like their current filename/path - retrieve them from the database and load to a document using the same loadXML mechanism.

ASP Option Explicit - Paypal Express Checkout trouble

I am facing a big trouble when integrating PayPal Express Checkout in classic ASP.
The code provided by PayPal at the "PayPal Integration Wizard" works perfectly when run without Option Explicit.
When I put into my coding pages and call to the functions provided, I am facing big trouble: My existing pages all use Option Explicit.
This results in me having to manually declare all variables in the PayPal functions.
The sample PayPal functions consist of many array/list/object/index for setting up the name/value pairs necessary to call the PayPal site. It is totally not easy for me to change it over to all correct declaration, since I am not ASP expert and project deadline is tight.
Can anyone give me some advice?
It seems to be possible to mix "Option Explicit"-code with non-"Option Explicit"-code via the Execute statement.
Here's a small test I just made with VBScript (which applies to classic ASP as well):
''#vb1.vbs (i.e. "your code")
Option Explicit
Dim fso, vb2, txt
Set fso = CreateObject("Scripting.FileSystemObject")
Set vb2 = fso.OpenTextFile("vb2.vbs", 1)
txt = vb2.ReadAll
MsgBox txt ''# Message 1
Execute txt
MsgBox foo ''# Message 2
and
''# vb2.vbs (i.e. "their code")
j = 100
Function foo
k = 100
foo = j + k
End Function
Results in:
Message 1: (equal to the contents of vb2.vbs)
Message 2: 200
I have no idea if this is the best way to do it, but currently I can think of no better way. Give it a try.
Beware of namespace clashes through global variables or function names in "their code".

What's wrong with this ASP recursive function?

When I call this function, everything works, as long as I don't try to recursively call the function again. In other words if I uncomment the line:
GetChilds rsData("AcctID"), intLevel + 1
Then the function breaks.
<%
Function GetChilds(ParentID, intLevel)
Set rsData= Server.CreateObject("ADODB.Recordset")
sSQL = "SELECT AcctID, ParentID FROM Accounts WHERE ParentID='" & ParentID &"'"
rsData.Open sSQL, conDB, adOpenKeyset, adLockOptimistic
If IsRSEmpty(rsData) Then
Response.Write("Empty")
Else
Do Until rsData.EOF
Response.Write rsData("AcctID") & "<br />"
'GetChilds rsData("AcctID"), intLevel + 1
rsData.MoveNext
Loop
End If
rsData.close: set rsData = nothing
End Function
Call GetChilds(1,0)
%>
*Edited after feedback
Thanks everyone,
Other than the usual error:
Error Type: (0x80020009) Exception occurred.
I wasn't sure what was causing the problems. I understand that is probably due to a couple of factors.
Not closing the connection and attempting to re-open the same connection.
To many concurrent connections to the database.
The database content is as follows:
AcctID | ParentID
1 Null
2 1
3 1
4 2
5 2
6 3
7 4
The idea is so that I can have a Master Account with Child Accounts, and those Child Accounts can have Child Accounts of their Own. Eventually there will be Another Master Account with a ParentID of Null that will have childs of its own. With that in mind, am I going about this the correct way?
Thanks for the quick responses.
Thanks everyone,
Other than the usual error:
Error Type: (0x80020009) Exception
occurred.
I wasn't sure what was causing the problems. I understand that is probably due to a couple of factors.
Not closing the connection and attempting to re-open the same connection.
To many concurrent connections to the database.
The database content is as follows:
AcctID | ParentID
1 Null
2 1
3 1
4 2
5 2
6 3
7 4
The idea is so that I can have a Master Account with Child Accounts, and those Child Accounts can have Child Accounts of their Own. Eventually there will be Another Master Account with a ParentID of Null that will have childs of its own. With that in mind, am I going about this the correct way?
Thanks for the quick responses.
Look like it fails because your connection is still busy serving the RecordSet from the previous call.
One option is to use a fresh connection for each call. The danger there is that you'll quickly run out of connections if you recurse too many times.
Another option is to read the contents of each RecordSet into a disconnected collection: (Dictionary, Array, etc) so you can close the connection right away. Then iterate over the disconnected collection.
If you're using SQL Server 2005 or later there's an even better option. You can use a CTE (common table expression) to write a recursive sql query. Then you can move everything to the database and you only need to execute one query.
Some other notes:
ID fields are normally ints, so you shouldn't encase them in ' characters in the sql string.
Finally, this code is probably okay because I doubt the user is allowed to input an id number directly. However, the dynamic sql technique used is very dangerous and should generally be avoided. Use query parameters instead to prevent sql injection.
I'm not too worried about not using intLevel for anything. Looking at the code this is obviously an early version, and intLevel can be used later to determine something like indentation or the class name used when styling an element.
Running out of SQL Connections?
You are dealing with so many layers there (Response.Write for the client, the ASP for the server, and the database) that its not surprising that there are problems.
Perhaps you can post some details about the error?
hard to tell without more description of how it breaks, but you are not using intLevel for anything.
How does it break?
My guess is that after a certain number of recursions you're probably getting a Stack Overflow (ironic) because you're not allocating too many RecordSets.
In each call you open a new connection to the database and you don't close it before opening a new one.
Not that this is actually a solution to the recursion issue, but it might be better for you to work out an SQL statement that returns all the information in a hierarchical format, rather than making recursive calls to your database.
Come to think of it though, it may be because you have too many concurrent db connections. You continually open, but aren't going to start closing until your pulling out of your recursive loop.
try declaring the variables as local using a DIM statement within the function definition:
Function GetChilds(ParentID, intLevel)
Dim rsData, sSQL
Set ...
Edit: Ok, I try to be more explicit.
My understanding is that since rsData is not declared by DIM, it is not a local variable, but a global var. Therefore, if you loop through the WHILE statement, you reach the .Eof of the inner-most rsData recordset. You return from the recursive function call, and the next step is again a rsData.MoveNext, which fails.
Please correct me if rsData is indeed local.
If you need recursion such as this I would personally put the recursion into a stored procedure and handle that processing on the database side in order to avoid opening multiple connections. If you are using mssql2005 look into something called Common Table Expressions (CTE), they make recursion easy. There are other ways to implement recursion with other RDBMS's.
Based on the sugestions I will atempt to move the query into a CTE (common table expression) when I find a good tutorial on how to do that. For now and as a quick and dirty fix, I have changed the code as follows:
Function GetChilds(ParentID, intLevel)
'Open my Database Connection and Query the current Parent ID
Set rsData= Server.CreateObject("ADODB.Recordset")
sSQL = "SELECT AcctID, ParentID FROM Accounts WHERE ParentID='" & ParentID &"'"
rsData.Open sSQL, conDB, adOpenKeyset, adLockOptimistic
'If the Record Set is not empty continue
If Not IsRSEmpty(rsData) Then
Dim myAccts()
ReDim myAccts(rsData.RecordCount)
Dim i
i = 0
Do Until rsData.EOF
Response.Write "Account ID: " & rsData("AcctID") & " ParentID: " & rsData("ParentID") & "<br />"
'Add the Childs of the current Parent ID to an array.
myAccts(i) = rsData("AcctID")
i = i + 1
rsData.MoveNext
Loop
'Close the SQL connection and get it ready for reopen. (I know not the best way but hey I am just learning this stuff)
rsData.close: set rsData = nothing
'For each Child found in the previous query, now lets get their childs.
For i = 0 To UBound(myAccts)
Call GetChilds(myAccts(i), intLevel + 1)
Next
End If
End Function
Call GetChilds(1,0)
I have working code with the same scenario.
I use a clientside cursor
...
rsData.CursorLocation = adUseClient
rsData.Open sSQL, conDB, adOpenKeyset, adLockOptimistic
rsData.ActiveConnectcion = Nothing
...
as pointed out in other responses, this is not very efficient, I use it only in an admin interface where the code is called infrequently and speed is not as critical.
I would not use such a recursive process in a regular web page.
Either rework the code to get all data in one call from the database, or make the call once and save it to a local array and save the array in an application variable.

Resources