Create or clear Hashtable instance for each new request of Report in SSRS - vba

I have a report done via VS2008 SSRS that is uploaded to my Reporting Services Server, that report is called when the event GenerateReport is called (on button click)
Now, I have the following code:
Public Shared Dim gruposVolTotal As New System.Collections.HashTable()
Public Shared Dim gruposSeleccion As New System.Collections.HashTable()
Public Shared Dim gruposModVenta As New System.Collections.HashTable()
Dim grupoActual as String = ""
Public Shared Dim acumuladoSeleccion as Double = 0
Public Shared Dim acumuladoVolTotal as Double = 0
Public Shared Dim acumuladoModVenta as Double = 0
Public Shared Dim acumuladoAnterior as Double = 0
Function Acumulador(ByVal MiGrupo as String, ByVal value as Double, ByVal Campo as String) As Double
If (GrupoActual <> MiGrupo) Then
If (Campo = "ModVenta") then
If (not gruposModVenta.Contains(MiGrupo)) then
acumuladoModVenta =acumuladoModVenta + value
gruposModVenta.Add(MiGrupo,acumuladoModVenta )
Return acumuladoModVenta
Else
Return gruposModVenta(MiGrupo)
End If
End If
If (Campo = "RunningValue") then
acumuladoVolTotal=acumuladoVolTotal + value
If (not gruposVolTotal.Contains(MiGrupo)) then
acumuladoAnterior = acumuladoVolTotal + acumuladoAnterior
gruposVolTotal.Add(MiGrupo, acumuladoAnterior)
End If
Return gruposVolTotal(MiGrupo)
End If
If (Campo = "VolumenSeleccion") then
If(not gruposSeleccion.Contains(MiGrupo)) then
acumuladoSeleccion=acumuladoSeleccion + value
gruposSeleccion.Add(MiGrupo, acumuladoSeleccion)
Return acumuladoSeleccion
Else
return gruposSeleccion(MiGrupo)
End If
End If
End If
End Function
What is the problem with this:
If you execute this on VS2008 the results are correct if I change pages (remember on VS2008) the elements persists and that is good.
If you execute this via web application, the first time will execute correctly but the second time if you choose different parameters on the report the values will stay as the 1st time you execute it.
This is because the variables and elements of the hashtables are saved in session so until I dont close the webpage and enter it again the values of these Hashtables and variables will stay the same after the first time you execute it.
If you change the scope of the variables and Hashtables to public, private or directly dim (private) each request (change of pages aka break page) will reset the value of the variables and hashtables.
So the Idea is to utilize the code I posted but what Im asking if is there any way to reset or clear the elements of the hashtables each time I call the report and change the parameters.
I saw in a forum that another user created a new hashtable for each request but those Hashtables wont be garbage collected and that means a lot of memory usage.
Let me know if you need more information.
EDIT:
You change the pages via ReportViewer Control.
I tried using the following code:
Protected Overrides Sub OnInit()
gruposVolTotal.Clear()
gruposSeleccion.Clear()
gruposModVenta.Clear()
grupoActual = ""
acumuladoSeleccion = 0
acumuladoVolTotal = 0
acumuladoModVenta = 0
acumuladoAnterior = 0
End Sub
But it seems it deletes it for every change of page and not each time I open the report.

Related

Poor performance on SELECT * FROM Win32_Printer

I'm updating some routines that help identify and store the printers available for a user when they access our application over Citrix. When the printers are created they are flagged with a name like;
In citrix;
PrinterName (from MyTerminalID) in session 209
In RDP;
PrinterName (redirected 209)
run locally we just get the PrinterName
That's all fine but we've switched from using the "system.drawing" method of identifying printers which is somewhat dangerous and identifies every single printer on the citrix node to using the System.Management query SELECT * FROM Win32_Printer
I've modified this with a condition e.g., DeviceID Like '%(from MyTerminalID)%' AND DeviceID LIKE '%in session 123' but the first time I execute the query it can take a number of seconds (15+) before it comes back with the results. One I have the results it's very quick.
Now the question is, how can I make it go faster ?
I'm only interesting in getting the subset of printers that either relate to myTerminalID AND my session or my session (if RDP, but the citrix version is the important bit).
Is the query running like a proper SQL query so will use the condition to reduce the time to run or is it more of a pseudo query where it's going to pull back the whole data set and then apply the filtering.
Is there a way to reduce the amount of data that it needs to go and get (e.g., if I do a SELECT DeviceID rather than a SELECT *).
re-running the query is much faster <1 second. In some ways it doesn't matter if it takes 15+ seconds to pull back the list of printers as that's currently used for set up but there is also another routine that checks if the previously selected printers are still available to the user on the same terminal (even if a different user was the one that set them up). I would also like to try and potentially filter out "non-printers" like the fax etc., and to do that I need to look at some of the detail of the properties.
About your current scenario, you could try to set two policies:
- Use Universal printing only
- Do not auto-create client printers
then force the update of the group policy (client side).
Maybe you want that client and server printers mirror each other, but just for testing. (Note, however, that I've seen much worse than 15 seconds in WMI enumerations in different situations. Note also that I'm no expert in Citrix Sessions/UPS.)
WMI SQL (WQL) is a subset of the ANSI SQL standard. See:
Querying with WQL - WQL (SQL for WMI) Keywords
Of course it returns a set of records (objects) that match the constraints defined in the query. Is it an efficient system? You may hear very different opinions on this matter.
The first query is slower because new objects are created at this time.
If not otherwise instructed, WMI caches these objects for subsequent queries.See EnumerationOptions.Rewindable Property
About filtering the results, see Use the Like Operator to Simplify Your WQL Queries
In the test that I made, I use this filter to rule out "non-printers" printers:
_Query.Condition = "NOT DeviceID LIKE '%fax%' AND NOT DeviceID LIKE '%xps%' AND NOT DeviceID LIKE '%PDF%'"
The following test has been performed on a machine that has 7 printers, 3 of which are "non-printers": Fax, XPS Driver and PDF Driver. The other 4 are "real" printers: 2 are local and 2 are network printers.
QUERY MIN MAX
One Printer: 96ms - 181ms
All Printers: (7) 107ms - 190ms
Filtered: (4) 108ms - 229ms
50 iterations measured using a StopWatch
Dim _SW As Stopwatch = Stopwatch.StartNew()
Dim _Printers As List(Of Printer) = GetSystemPrinters()
Console.WriteLine(_SW.ElapsedMilliseconds.ToString())
This is the code and object used:
Public Function GetSystemPrinters() As List(Of Printer)
Dim _Printers As New List(Of Printer)()
Dim _ConnOptions As New ConnectionOptions()
_ConnOptions.Authentication = AuthenticationLevel.Connect
_ConnOptions.Impersonation = ImpersonationLevel.Impersonate
'If needed => .UserName, .Password, .Authority
_ConnOptions.Timeout = EnumerationOptions.InfiniteTimeout
Dim _Scope As New ManagementScope("\\" + Environment.MachineName + "\root\CIMV2", _ConnOptions)
_Scope.Connect()
Dim _Query As New SelectQuery("SELECT * FROM Win32_Printer") 'Or "SELECT * FROM CIM_Printer"
'Create a filter to rule out some "non-printers"
_Query.Condition = "NOT DeviceID LIKE '%fax%' AND NOT DeviceID LIKE '%xps%' AND NOT DeviceID LIKE '%PDF%'"
Dim _Options As New EnumerationOptions()
_Options.Timeout = EnumerationOptions.InfiniteTimeout
'Forward only query => no caching
_Options.Rewindable = False
'Pseudo-async result
_Options.ReturnImmediately = True
Dim _searcher As New ManagementObjectSearcher(_Scope, _Query, _Options)
For Each _objPrinter As ManagementObject In _searcher.Get()
Dim _Printer As New Printer()
_Printer.PrinterName = _objPrinter.Properties("Name").Value.ToString()
_Printer.PrinterPort = _objPrinter.Properties("PortName").Value.ToString()
_Printer.PrinterDriver = _objPrinter.Properties("DriverName").Value.ToString()
_Printer.PrintProcessor = _objPrinter.Properties("PrintProcessor").Value.ToString()
_Printer.DeviceID = _objPrinter.Properties("DeviceID").Value.ToString()
_Printer.Status = CType(_objPrinter.Properties("PrinterStatus").Value, PrinterStatus)
_Printer.IsLocalPrinter = If(_objPrinter.Properties("Local").Value.ToString() = "True", True, False)
_Printer.IsNetworkPrinter = If(_objPrinter.Properties("Network").Value.ToString() = "True", True, False)
_Printer.IsDefaultPrinter = If(_objPrinter.Properties("Default").Value.ToString() = "True", True, False)
Dim _PrinterProps As New List(Of PrinterProperties)()
For Each _oBJData As PropertyData In _objPrinter.Properties
_PrinterProps.Add(New PrinterProperties() With {
.PropertyName = _oBJData.Name,
.PropertyValue = If(_oBJData.Value IsNot Nothing, _oBJData.Value.ToString(), ""),
.PropertyValueType = _oBJData.Type.ToString(),
.PropertyIsArray = _oBJData.IsArray
})
Next
_Printer.PrinterProperties = _PrinterProps
_Printers.Add(_Printer)
Next
Return _Printers
End Function
Relative Objects:
Public Enum PrinterStatus As Integer
Other = 1
Unknown = 2
Ready = 3
Printing = 4
Warmup = 5
StoppedPrinting = 6
Offline = 7
End Enum
Public Class Printer
Public Property PrinterName() As String
Public Property PrinterPort() As String
Public Property PrinterDriver() As String
Public Property PrintProcessor() As String
Public Property DeviceID() As String
Public Property Status() As PrinterStatus
Public Property IsDefaultPrinter() As Boolean
Public Property IsLocalPrinter() As Boolean
Public Property IsNetworkPrinter() As Boolean
Public Property PrinterProperties() As List(Of PrinterProperties)
End Class
Public Class PrinterProperties
Public Property PropertyName() As String
Public Property PropertyValue() As String
Public Property PropertyValueType() As String
Public Property PropertyIsArray() As Boolean
End Class

Get extended file information details

How can one obtain the details of a windows file using VB.net?
The type of details I mean are those found when I right click on a file, say an MS word doc, then click "Properties" and select the "Details" tab.
I know some can be obtained via FileInfo, but not all, such as "Tags" for example.
Thanks
For that stuff you need to use Shell32. From the COM tab, find and add Microsoft Shell Controls and Automation. Here is code to create a list of property-values for a given file:
' class to hold the goodies
Friend Class ShellInfo
Public Property Name As String
Public Property Value As String
Public Sub New(n As String, v As String)
Name = n
Value = v
End Sub
Public Overrides Function ToString() As String
Return Name
End Function
End Class
Then a function to fill it up
Private Function GetXtdShellInfo(filepath As String) As List(Of ShellInfo)
' ToDo: add error checking, maybe Try/Catch and
' surely check if the file exists before trying
Dim xtd As New List(Of ShellInfo)
Dim shell As New Shell32.Shell
Dim shFolder As Shell32.Folder
shFolder = shell.NameSpace(Path.GetDirectoryName(filepath))
' its com so iterate to find what we want -
' or modify to return a dictionary of lists for all the items
Dim key As String
For Each s In shFolder.Items
' look for the one we are after
If shfolder.GetDetailsOf(s, 0).ToLowerInvariant = Path.GetFileName(file).ToLowerInvariant Then
Dim ndx As Int32 = 0
key = shfolder.GetDetailsOf(shfolder.Items, ndx)
' there are a varying number of entries depending on the OS
' 34 min, W7=290, W8=309 with some blanks
' this should get up to 310 non blank elements
Do Until String.IsNullOrEmpty(key) AndAlso ndx > 310
If String.IsNullOrEmpty(key) = False Then
xtd.Add(New ShellInfo(key,
shfolder.GetDetailsOf(s, ndx)))
End If
ndx += 1
key = shfolder.GetDetailsOf(shfolder.Items, ndx)
Loop
' we got what we came for
Exit For
End If
Next
Return xtd
End Function
Using it is simple:
Dim xtd As List(Of ShellInfo) = GetXtdShellInfo("C:\Temp\Capri.jpg")
For Each s As ShellInfo In xtd
Console.WriteLine("{0}: {1}", s.Name, s.Value)
Next
The return should be a list of ShellInfo items where the Name is the property name such as Name, BitRate, Album and the associated Value will be that returned by Shell32. e.g
Name: Capri.jpg
Size: 15.2 KB
Item type: Image File
Date modified: 7/20/2014 12:19 PM
Date created: 7/20/2014 12:17 PM
Date accessed: 7/20/2014 12:17 PM
(etc)
The actual number returned will vary depending on the OS ver
As noted in the comment Microsoft Shell Controls and Automation is renamed as Microsoft Shell Folder View Router (in Windows 8.1).
Also, the first 35 properties are fairly well known and more common, but with Win7 there are about 291. Under Windows 8, the max is 309 with some blank spots and deep into the list some property indices are changed.
See this answer related question How to read the bit rate information from a .mov video file header
Dim shellAppType = Type.GetTypeFromProgID("Shell.Application")
Dim shellApp = Activator.CreateInstance(shellAppType)
Dim folder = shellApp.NameSpace("c:\users\frank")
Dim folderitem = folder.parsename("yourfile.jpg")
Dim value = folder.GetDetailsOf(folderitem, 24) 'eg. 24 retrieves File Comments.
Just use something like :
Dim MyFileInfos as System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(PathToYourFile)
Then get infos using MyFileInfo.* (whatever you need, use IntelliSense).
Have a nice day

Function call from Arraylist element not working

I am trying to get a function for a class assignment to work however as the program hits the specific line in question it dies off and nothing after this line will execute. The program does not lock up, just the current execution path dies.
I have tried running debugging but much the same happens. Once I hit the link that should call a function from the object stored in the Arraylist element the break point at the actual function that should be called is not hit and nothing further happens.
Public Structure Appliances
' Create New Appliance object
Public Sub New(name As String, pusage As Double)
aName = name
aPUsage = pusage
End Sub
' Create New Washer Object
Public Sub New(name As String, pusage As Double, wusage As Double)
aName = name
aPUsage = pusage
aWUsage = wusage
End Sub
' Functions
Public Function getAName()
Return aName
End Function
Public Function getAPUsage()
Return aPUsage
End Function
Public Function getAWUsage()
Return aWUsage
End Function
Dim aName As String ' Appliance Name
Dim aPUsage As Double ' Appliane Power Usage
Dim aWUsage As Double ' Appliance Water Usage
End Structure
...
Public Class Form1
...
Dim appList As New ArrayList() ' Create an arraylist appliance objects
Public appTemp As Appliances ' To store appliance objects until they can be added to the arraylist
...
Private Function getAppInfo()
getAppInfo = Nothing
Do While fInStream.Peek() <> -1
s = fInStream.ReadLine() ' Get a line from the file and set s to it
Dim words As String() = s.Split(New Char() {","c}) ' Split the line contents along commas and set those parts into words
words(0) = words(0).Replace("_", " ") ' Reaplce underscores with spaces
If (words.Count = 3) Then ' If words contains the washer appliance
appTemp = New Appliances(words(0), Double.Parse(words(1)), Double.Parse(words(2)))
appList.Add(appTemp)
Else ' For all other appliances
appTemp = New Appliances(words(0), Double.Parse(words(1)))
appList.Add(appTemp)
End If
Loop
End Function
Private Function setUsage(name As String)
setUsage = Nothing
' Find appliance
For i = 0 To appList.Count
If (name = appList(i).getAName()) Then
If (name = "Washer") Then
s = appList(i).getWUsage() ' !!!This is the line where the execution dies at, nothing after this line is processed and the function call is not completed
txtbGPH.Text = s
End If
MsgBox("Test 1")
Exit For
ElseIf (i = appList.Count) Then
MsgBox("Appliance could not be found")
End If
Next
End Function
End Class
Use a List(Of X) instead of ArrayList if you are going to insert only one type:
Dim appList As New List(Of Appliances)
And I recommend you to declare your temp var inside the methods unless is necessary. Anyway, in this case you don't need it, you can add your var in this way:
appList.Add(New Appliances(words(0), Double.Parse(words(1))))
With this use (using lists) you won't need to use arraylistObj.Item(i).Method() and you can simply use the common way:
s = appList(i).getWUsage()
Nevermind, I figured it out just now. I did not know that arraylists are not "arraylists" but a collection. I thought maybe it would act like other collection oriented objects and that you have to use a .item(i) to access the elements, which turns out to be the case.
txtbGPH.text = appList.item(i).getAWusage()
produces the proper behavior and the rest of the code after the problematic line indicated in the OP executes as does the break point set at the called function.

Threading Exception: The number of WaitHandles must be less than or equal to 64

The title is to make this easy to find for others having this error. I'm new to Threading, so this is really giving me heck. I'm getting this runtime error that crashed Cassini. This is code that I'm maintaining originally developed as a website project in VS 2003 and converted to a VS 2008 website project.
Important Info:
The number of objects in the manualEvents array is 128 in this case.
products is an array of Strings
Need to support .NET 2.0
For Each product As String In products
If Not product.Trim().ToUpper().EndsWith("OBSOLETE") Then
calls += 1
End If
Next
Dim results(calls - 1) As DownloadResults
'Dim manualEvents(calls - 1) As Threading.ManualResetEvent '128 objects in this case.
Dim manualEvents(0) As Threading.ManualResetEvent
manualEvents(0) = New Threading.ManualResetEvent(False)
'NOTE: I don't think this will work because what is not seen here, is that
' this code is being used to populate and cache a long list of products,
' each with their own category, etc. Am I misunderstanding something?
'initialize results structures
'spawn background workers
calls = 0
For Each product As String In products
If Not product.Trim().ToUpper().EndsWith("OBSOLETE") Then
Dim result As New DownloadResults
'manualEvents(calls) = New Threading.ManualResetEvent(False)
'Moved above For Each after declaration of variable
result.params.product = product
result.params.category = docType
'result.ManualEvent = manualEvents(calls)
result.ManualEvent = manualEvents(0)
result.Context = Me._context
results(calls) = result
Threading.ThreadPool.QueueUserWorkItem(AddressOf ProcessSingleCategoryProduct, results(calls))
Threading.Interlocked.Increment(calls) 'Replaces below incrementation
'calls += 1
End If
Next
Threading.WaitHandle.WaitAll(manualEvents) 'CRASHES HERE
Thread Helper Function (for the sake of completion)
Public Shared Sub ProcessSingleCategoryProduct(ByVal state As Object)
Dim drs As DownloadResults = CType(state, DownloadResults)
Dim adc As New cADCWebService(drs.Context)
drs.docs = adc.DownloadADC(drs.params.category, drs.params.product)
drs.ManualEvent.Set()
End Sub
You don't need an array of 128 manual events to check for completion of all 128 threads.
Create only one manual reset event and a plain integer starting at 128. Decrement that integer using Interlocked.Decrement at the end of ProcessSingleCategoryProduct, and only signal the event when the count reaches zero:
if (Interlocked.Decrement(ByRef myCounter) = 0) myEvent.Set();
Then declare only one Threading.ManualResetEvent as opposed to an array of them, and you can call WaitOne instead of WaitAll on it, and you are done.
See also usr's comment for an easier alternative in case you have .NET 4.

queing jobs in threadpool vb.net

i have 20,000 items in a queue, and i want to process them using the threadpool.
will this be the best way to do it?
for i as integer = 0 to 19999
ThreadPool.QueueUserWorkItem (PerformTask, ListTask(i))
next
Sub PerformTask(i as string)
' do the work here
end sub
How can i return or set ui control from the PerformTask sub?
You cannot.
However, you can allocate a container (array, list) with a different slot for each result, and write into it. Alternatively, you could pass an object into the worker method that holds both the input and the result. I’d use this method:
Class TaskObject
Dim Input As String
Dim Result As Whatever
End Class
Dim tasks As TaskObject() = New TaskObject(20000) { }
For i as Integer = 0 to tasks.Length - 1
ThreadPool.QueueUserWorkItem(PerformTask, tasks(i))
next
Sub PerformTask(arg As Object)
Dim task As TaskObject = DirectCast(arg, TaskObject)
' do the work here
end sub
Unrelated: you should always enable Option Strict in your projects. No exception. Your code has type errors that the compiler should detect.