VB.Net Method appears to be called multiple times when it actually isn't, and returns inconsistent results - vb.net-2010

I have been staring at this problem all day and I'm completely baffled by what I'm seeing.
There are two issues occurring, and unfortunately, one of them only happens in Production so I can't test it the way I'd like.
I will give all the background and relevant information up front with the code at the end. Some of what I say in these next couple sections won't make much sense until you review the code.
Background info:
(I have triple verified all this information)
This class is being called from a TFS 2010 WWF build template. It relies on a library I've created in another tool to deploy files to our Universe environment using UniDK
The deployment itself is working fine, the problem is with the logging and return codes.
The build is marked as "Success" if the class below returns a code of 0, "Partial Success" with a return code of 1, and "Failed" if there is any other return code.
The files are only being deployed one time (objDeploy.DeployFiles() is only called once)
serverInfo.ServerCount = 2
serverInfo.ServerActive for the second environment (counter = 1) is False
To help track down the issues, I have added additional logging in ProcessResults() to output the values of the different collections to a separate file, but I haven't had an opportunity to run it with the additional code
Symptoms:
In Production, it is exiting with a return code of 1 (exitCode = 1)
This is what is returned by the results string:
Results for server name Deployment successful!
*********************** Results for server name Deployment successful!
*********************** Results for server name Deployment errors, please review the log
*********************** Results for server name Deployment successful!
*********************** Results for server name Deployment successful!
***********************
3. In QA, we have the "results for server name" message 6 times, but each time says the deployment is successful
4. Everything in the deployment log file shows that all files deployed returned with a code of 0 (This means that Result40Collection, BackupErrorCollection, and BadErrorCollection should be empty. I will explain in a moment why this is especially significant)
What I expect to happen:
exitCode = 0
Build = succeeded
results:
Results for server name Deployment successful!
***********************
What I expect to happen based on the results in the TFS build log:
In this section, I'm ignoring the fact that there are multiple entries being returned and only focusing on the one that says there were errors
exitCode = 2
Build = Failed
results:
Results for server name Deployment errors, please review the log
***********************
Code:
Imports System
Imports Microsoft.TeamFoundation.Build.Client
Imports System.Activities
Imports RMUtilities
<BuildActivity(HostEnvironmentOption.All)>
Public NotInheritable Class DeployU2Files
Inherits CodeActivity
#Region "Arguments"
' In Arguments
Property inServerDataSet As InArgument(Of DataSet) ' Dataset containing the server information
Property inSourcesDirectory As InArgument(Of String) ' Full path to the Source directory being deployed
Property inBuildName As InArgument(Of String) ' Name of the build, to be used for backups
Property inLogDirectory As InArgument(Of String) ' Path to the log folder
' Out Arguments
Property outExitCode As OutArgument(Of Integer) ' Resulting error code, 0 is good
Property outResult As OutArgument(Of String) ' Result string
#End Region ' "Arguments"
#Region "Variables"
' Variables passed in from the build
Dim dsServerDataSet As DataSet
Dim strSourcesDirectory As String
Dim strBuildName As String
Dim strLogDirectory As String
' Variables used by the build
Dim serverInfo As XMLReader
Dim fileList As U2FileListParser
' Result variables
Dim exitCode As Integer = 0
Dim results As String = ""
#End Region '"Variables"
Protected Overrides Sub Execute(context As System.Activities.CodeActivityContext)
' Sets the working variables
dsServerDataSet = context.GetValue(Me.inServerDataSet)
strSourcesDirectory = context.GetValue(Me.inSourcesDirectory)
strBuildName = context.GetValue(Me.inBuildName)
strLogDirectory = context.GetValue(Me.inLogDirectory)
' Creates the base objects needed for the deployment
Try
serverInfo = New XMLReader(dsServerDataSet)
fileList = New U2FileListParser(strSourcesDirectory)
Catch ex As NullReferenceException
Throw New NullReferenceException("Invalid XML Dataset", ex)
Exit Sub
Catch ex As Exception
Throw New Exception("Error processing file list: " & ex.Message, ex)
End Try
' First, determine if there are files to deploy
Dim fileCount As Integer
Try
With fileList
fileCount = .DeployList.Count + .PreDeployList.Count + .PostDeployList.Count
End With
Catch ex As Exception
Throw New ArgumentException("No files to deploy")
End Try
If fileCount = 0 Then Throw New ArgumentException("No files to deploy")
' Then, check to make sure there are servers to deploy to
If serverInfo.ServerCount = 0 Then
Throw New ArgumentException("No servers listed in XML file to deploy to")
End If
' Iterates through each server in the XML file
For counter = 0 To serverInfo.ServerCount - 1
' Sets the current environment
serverInfo.ChosenEnvironment = counter
' Checks to make sure the server is active. If it isn't, it's skipped
If serverInfo.ServerActive Then
' Creates new logging object to log all output to a file with the name of the server being deployed to
Dim logger = New RMLogging(strLogDirectory & "\" & serverInfo.ServerHostName & ".log")
logger.Header = "Automated deploy" & vbCrLf & _
"Build Number: " & strBuildName & vbCrLf & _
"Date: " & DateTime.Now.ToString("MMM ddd d yyyy hh:mm:ss tt")
' Creates the deployment object
Dim objDeploy As New U2Deploy(serverInfo, fileList, logger, strBuildName)
' Deploys the files to the environment, then checks the results to make sure they
objDeploy.DeployFiles()
' This will determine the success level of the deployment, and also parses the message for the log
ProcessResults(objDeploy, serverInfo.ServerHostName)
' If there was a problem writing the log, then add the full text of the log to the results
If objDeploy.FullLog.Length > 0 Then
results &= objDeploy.FullLog & vbCrLf
results &= "**********************************" & vbCrLf
End If ' objDeploy.FullLog.Length > 0
' Disposes the objects
logger = Nothing
objDeploy.Clear()
objDeploy = Nothing
End If ' serverInfo.ServerActive
Next ' counter = 0 To serverInfo.ServerCount - 1
SetResults(exitCode, results, context)
End Sub
''' <summary>
''' Will change the exite code based on the results of the deployment
''' </summary>
''' <param name="objDeploy">U2Deploy object that contains the collections</param>
''' <remarks></remarks>
Private Sub ProcessResults(objDeploy As U2Deploy, serverName As String)
Dim currentErrorCode As Integer = 0
results &= "Results for " & serverName & vbCrLf
If objDeploy.Result40Collection.Count() > 0 Then
currentErrorCode = 1
results &= "Type 40 errors, please review the log" & vbCrLf
End If ' objDeploy.Result40Collection.Count() > 0
If objDeploy.BackupErrorCollection.Count > 0 Then
currentErrorCode = 1
results &= "File backup errors, please review the log" & vbCrLf
End If ' objDeploy.BackupErrorCollection.Count > 0
If objDeploy.BadErrorCollection.Count > 0 Then
currentErrorCode = 2
results &= "Deployment errors, please review the log" & vbCrLf
End If
If currentErrorCode = 0 Then results &= "Deployment successful!" & vbCrLf
results &= "***********************" & vbCrLf
If currentErrorCode > exitCode Then exitCode = currentErrorCode
End Sub
' Sets the outgoing message and exit code. This is used by the workflow to add messages to the buld itself
Private Sub SetResults(ByVal exitCode As Int32, message As String, ByRef context As CodeActivityContext)
context.SetValue(Me.outExitCode, exitCode)
context.SetValue(Me.outResult, message)
End Sub
End Class
UPDATE:
I've been able to run this in QA twice with verbose logging turned on, and here are the results (Again, totally inconsistent). I am using VS2013 only to view and run the builds, any code changes to the classes used by the build are done within VS2010.
Run 1:
Run 2:

I actually just had a resolution to this last night. The problem was actually with the documentation I had read on the WWF process. This code was actually executing properly, and returned the proper values, but because of the incorrect instructions, it was taking the wrong path in the workflow, which made it appear that this code was wrong.
It took me a while for this error to occur again with the diagnostic logging turned on, and once I saw it, I knew immediately what the problem was.
The root cause was that I was under the impression that WriteBuildError would mark the build as Failed, instead it marked it as Partially Succeeded, which put me on the wrong troubleshooting path.

Related

Wanted to Transfer the file from one location to another location using Button in VB.net

I have an Excel file (say xyz.xlsx) on my server but want to fetch that file and save locally on my desktop, when ever i press the "Move" Button present on my form.Please guide me on this, that how it can be done. i am right now using visual studio 2017
You have two easy options.
If you want to do this as a training exercise you look into the System.IO classes.
System.IO.File.Move(source$, destination$) will suffice.
You can improve this with some error checking such as
System.IO.Directory.Exists(sourcePath$)
System.IO.Directory.Exists(destPath$)
You can then play around with string formatting and error handling as you please.
If All you are doing is copy a file and that is your entire software, I would suggest doing it in CMD instead.
This same approach can be called from VB if needed.
Process.Start("ROBOCOPY " & source$ & " " & destination$ & " " & flags)
The advantage of doing this is it is a separate process to your main code, which means you can leave a very large file copying over a long period of time without hanging up your own code.
''' <summary>
''' Copies the file at destination Source to the folder DestinationPath (and creates the folder if it doesn't exist)
''' </summary>
''' <param name="Source">Format C:\Users\username\documents\randomstuff\unnecessaryfolder\newfolder\myfile.txt</param>
''' <param name="DestinationPath">Format (and Default path) C:\Users\username\Desktop\ </param>
Public Sub MoveFile(Source As String, DestinationPath As String)
If DestinationPath = "" Then
DestinationPath = "C:\Users\" & My.User.Name & "\Desktop\" 'default path
End If
Dim FileName
Dim src() As String = Source.Split("\")
FileName = src(src.Count - 1) 'returns the name of the file in the full path
Try
If Not IO.File.Exists(Source) Then Throw New Exception("Wrong file, you plonka!")
If Not IO.Directory.Exists(DestinationPath) Then IO.Directory.CreateDirectory(DestinationPath)
IO.File.Copy(Source, DestinationPath & FileName)
Catch ex As Exception
Throw ex
End Try
End Sub

Passing Parameter to Crystal Reports XI from Visual Studio 2015

I am running into problems with the passing of parameters to an externally created Crystal Reports XI report from the WinForms application I'm building in Visual Studio 2015 Community Edition. No matter what I try to do, the report doesn't seem to get the value unless I manually select it at the prompt (which shouldn't even be popping up) when the report is being displayed. I'm using the same code I've used in a previous application (although that one was built in VS2008), but I've tried a number of "alternate" versions of the code in my attempts to get this working. Here's the code that I'm currently using:
Imports CrystalDecisions.CrystalReports.Engine
Imports CrystalDecisions.Shared
Module modReports
Private WithEvents DocumentToPrint As New Printing.PrintDocument
Private Sub ShowReport(ByVal LID As Integer, ByVal InHouse As Boolean)
Dim Report As New ReportDocument
Dim ReportParameters As ParameterFieldDefinitions = Nothing
Dim Parameter As ParameterFieldDefinition = Nothing
Dim ApplicationValue As ParameterDiscreteValue = Nothing
Dim ReportValues As ParameterValues = Nothing
Dim ReportViewer As New frmReport
Dim Response As DialogResult = DialogResult.Cancel
PrintingReport = True
Report.Load(CRYSTAL_REPORT_FILE_PATH & "ExampleReport.rpt")
Report.Refresh()
Report.VerifyDatabase()
ReportParameters = Report.DataDefinition.ParameterFields
Parameter = ReportParameters.Item("PrintAll")
ReportValues = New ParameterValues
ApplicationValue = New ParameterDiscreteValue
'Parameter.CurrentValues.Clear()
'ReportValues.Clear()
ReportValues = Parameter.CurrentValues
If LID = 7777 Then
ApplicationValue.Value = True
Else
ApplicationValue.Value = False
End If
ReportValues.Add(ApplicationValue)
Parameter.ApplyCurrentValues(ReportValues)
Response = MessageBox.Show("Do you want to send this report directly to the printer?", "SEND TO PRINTER", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question)
If Response = DialogResult.No Then
With ReportViewer
.rptViewer.ReportSource = Nothing
.rptViewer.ReportSource = Report
.WindowState = FormWindowState.Maximized
.rptViewer.RefreshReport()
' Set zoom level: 1 = Page Width, 2 = Whole Page, 25-100 = zoom %
.rptViewer.Zoom(1)
.rptViewer.Show()
.ShowDialog()
End With
ElseIf Response = DialogResult.Yes Then
Dim SelectPrinter As New PrintDialog
Dim PrinterSelected As DialogResult = DialogResult.Cancel
With SelectPrinter
.Document = DocumentToPrint
.AllowPrintToFile = False
.AllowSelection = False
.AllowCurrentPage = False
.AllowSomePages = False
.PrintToFile = False
End With
PrinterSelected = SelectPrinter.ShowDialog
If PrinterSelected = DialogResult.OK Then
Dim Copies As Integer = DocumentToPrint.PrinterSettings.Copies
Dim PrinterName As String = DocumentToPrint.PrinterSettings.PrinterName
Dim LastPageNumber As Integer = 1
Dim PrintBuffer As String = String.Empty
LastPageNumber = Report.FormatEngine.GetLastPageNumber(New ReportPageRequestContext)
Report.PrintOptions.PrinterName = PrinterName
Report.PrintOptions.PrinterDuplex = DocumentToPrint.PrinterSettings.Duplex
Report.PrintToPrinter(Copies, True, 1, LastPageNumber)
If Copies = 1 Then
PrintBuffer = "Printed " & Copies & " copy of "
Else
PrintBuffer = "Printed " & Copies & " copies of "
End If
If LastPageNumber = 1 Then
PrintBuffer += LastPageNumber.ToString & " page."
Else
PrintBuffer += LastPageNumber.ToString & " pages."
End If
MessageBox.Show("The report was sent to the following printer:" & vbCrLf & " • " & PrinterName & vbCrLf & PrintBuffer, "REPORT PRINTED", MessageBoxButtons.OK, MessageBoxIcon.Information)
End If
End If
PrintingReport = False
End Sub
End Module
The report itself is built to use an XML file as the data source, which is dynamically created by this application. All of that works normally, and oddly enough, if I send the report directly to the printer, it seems to print correctly without prompting me. It's only a problem when I try to display the report through the CrystalReportViewer object.
Some of the things I've tried without success:
I've tried with and without calling the Clear() methods on the
Parameter.CurrentValues and ReportValues objects.
I've tried moving all of the parameter setting logic to after I set the
ReportSource of the CrystalReportViewer control (rptViewer.ReportSource)
I've tried using alternate Crystal Reports objects (ParameterFields instead of ParameterFieldDefinitions and ParameterField instead of ParameterFieldDefinition).
I've tried removing all of the "complicated" code and just using the SetParameterValue method (i.e., Report.SetParameterValue("PrintAll", True)
I've even tried creating different types of parameter fields in the report itself (String, Boolean, Number) and passing appropriate values for those datatypes.
If I walk through the code, it doesn't appear to error out anywhere, and everything looks like it's working just great until I get to the .rptViewer.RefreshReport() line in the With ReportViewer block. I've verified that all of the parameters and values have only the value I am "selecting" via the application by checking them every step up to that point, and it all looks exactly as I expect it to look.
But the application (via Crystal Reports) continues to prompt me for the value I just passed in the code. If I select the value in that prompt, the report does generate correctly based on the value I select, but no matter what I "pass" in the programming, the prompt always defaults to True.
Does anyone have any suggestions that I may have overlooked for how to get this parameter to correctly pass to the CrystalReportViewer control? Please let me know if you need any additional information or have any questions about what I've set up so far here. Thank you in advance.
Okay, so based on the information I found over on http://www.it-sideways.com/2011/10/how-to-disable-parameter-prompt-for.html, it seems that the RefreshReport method for the CrystalReportViewer control basically wipes out any parameters and/or log on information:
1. ) Do not invoke CrystalReportViewer.RefreshReport Method
This method will refresh the data for the report currently displayed
in the CrystalReportViewer control. The report will prompt for
parameters or logon info is necessary.
So, this method is not needed unless the same
CrystalDecisions.CrystalReports.Engine.ReportDocument object is
reused.
By commenting out that line of the code, I was able to prevent the parameter prompt from being displayed. The only issue I have after making that change is that, even though the zoom level is being set to 1 (page width), and when I run the project the CrystalReportViewer control even shows that it's correctly set in the status bar ('Zoom Factor: Page Width' is displayed), the report itself is not actually zoomed in to the page width.
With the RefreshReport method uncommented, if I manually provided the value for my parameter, it would display the report properly zoomed. If I add the zoom button to the control (.rptViewer.ShowZoomButton = True), I can manually choose the Page Width option, which then correctly "re-zooms" the report to the desired level, but it won't immediately display that way if the RefreshReport method is not called.
Regardless, I can spend some time trying to fight that now, but I finally have it properly setting, passing and displaying the results of my parameter. I hope this helps someone else running into this issue.

When I try to divert standard output with System.Diagnostics.Process, the called program gives an error

To summarise the following: If I use Process to call WinZip, WinZip performs its task correctly but I lose the console output. If I divert the standard output, I get that output but WinZip fails to create a zip file and reports “FATAL ERROR: win32/windows system error (print.cpp#315): The handle is invalid.” Can anyone identify my error?
I have a security system written in Excel VBA. It identifies files to archive and uses batch files to call the command line interface of WinZip and maintains an index within an Excel worksheet. This system has served me well for many years but it had some deficiencies that I believed would be solved by recoding with VB.Net.
The VB.Net version was more of a port than a recode and I maintained the use of batch files. The new versions solved most of the deficiencies with the VBA version but one remained.
Within a batch file, a file name must be strictly ASCII to avoid problems. Most of my file names are ASCII but a few are not, for example: “009 Ålesund - Art Deco tower.jpg”. This is a photograph from a holiday in Norway where I have included the name of the town in the file name. Within batch files, hexadecimal C5 is sometimes treated as Unicode (display value Å) and sometimes as code page 437 (display value ┼). I suspect what is passed to a program is the UTF-8 code for hexadecimal C5 split into two bytes to give C3 85. Whatever is happening, no program called via a batch file can find file “009 Ålesund - Art Deco tower.jpg”.
I avoided this problem with the VBA solution by checking filenames for non-ASCII characters. If found, the processing would be of the form:
VBA macro renames file “009 Ålesund - Art Deco tower.jpg” as “009 $C5lesund - Art Deco tower.jpg”.
Batch file calls WinZip to zip file “009 $C5lesund - Art Deco tower.jpg”.
VBA macro renames file “009 $C5lesund - Art Deco tower.jpg” as “009 Ålesund - Art Deco tower.jpg”.
VBA macro records both the correct name and the name under which it had been archived in the index.
Although this was messy, it worked and was the best I could think of.
I have implemented the same approach with VB.Net but I have been looking for a better alternative.
I use Shell to execute the batch file so I first investigated using Shell to call WinZip directly rather than via a batch file. WinZip outputs progress and error information to the console which I redirect to text file “zip.txt” using ZipCommand > zip.txt. I thoroughly check for possible error conditions prior to creating the batch file so I rarely get error messages but I would prefer not to give up access to WinZip’s console output. I cannot discover any method of diverting console output using Shell.
I then tried Process which appears to offer the functionality I seek but I am encountering an error.
The code below was intended as proof-of-concept. Within it there are three statements that are currently commented out:
'1 .RedirectStandardOutput = True
'2 .UseShellExecute = False
'3 zipOutput = proc.StandardOutput.ReadToEnd()
If I run the code as is with the file to be zipped missing, the process finishes with exit code 12. WinZip’s error codes are not published and its documentation claims only exit code 0 meaning “no errors” is reliable.
If I run the code as is with the file to be zipped present, the process finishes with exit code 0. The zip file is formed correctly.
I get appropriate exception errors if statement 3 is enabled by removing “'3” or if both statements 1 and 3 are enabled. However, if I run the code with all three statements enabled I get exit code 254 and the zip file is not created. The diverted console output is:
WinZip(R) Command Line Support Add-On Version 4.0 32-bit (Build 10562)
Copyright (c) 1991-2013 WinZip International LLC - All Rights Reserved
using encryption AES-256
FATAL ERROR: win32/windows system error (print.cpp#315): The handle is invalid.
Program is terminating!
Please send the file wzCLine.rpt to the address below.
To help solve this problem, please include as detailed as possible
a description of what you were doing before the problem occurred,
so we can try to reproduce the problem here.
WinZip Computing
EMail: support#winzip.com
Web: http://www.winzip.com
The file “wzCLine.rpt” contains:
Please send the file wzCLine.rpt to the address below.
To help solve this problem, please include as detailed as possible
a description of what you were doing before the problem occurred,
so we can try to reproduce the problem here.
WinZip Computing
EMail: support#winzip.com
Web: http://www.winzip.com
FATAL ERROR: win32/windows system error (print.cpp#315): The handle is invalid.
Output_context_info: 528d4140
Return address = 0000000a
Windows NT 6.0 build 6002 Service Pack 2
Current date/time: 01/02/2016 14:56
WinZip(R) Command Line Support Add-On (10562cl)
Build 10562
Module name = c:\Program files\WinZip\wzzip.exe
Command line: <"c:\Program files\WinZip\wzzip.exe" -a -s"12345678" -ycAES256 -whs "C:\Secure\Temp\test.zip" "C:\Secure\Temp\009 Ålesund - Art Deco tower.jpg">
Memory in use = 61%
Total physical memory = 2097151 Kbytes
Physical memory available = 1199000 Kbytes
Total virtual memory = 2097024 Kbytes
Virtual memory available = 2035664 Kbytes
Country code: 44 Language: English Code-page: 1252
[eof]
I will probably send a report to WinZip.com but I assume the cause is an error in my use of Process and not an error within WinZip.
Any advice on how to avoid the error and/or improve my code will be gratefully received.
Public Class Form1
Private Sub btnExit_Click(sender As System.Object, e As System.EventArgs) _
Handles btnExit.Click
Me.Close()
End Sub
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) _
Handles MyBase.Load
' Process, ProcessStartInfo and ProcessWindowStyle within namespace System.Diagnostics
' Threading within namespace System
Dim proc As Process = Nothing
Dim procInfo As New ProcessStartInfo
Dim zipOutput As String = ""
Dim zipArguments As String = "-a -s""12345678"" -ycAES256 -whs ""C:\Secure\Temp\test.zip""" & _
" ""C:\Secure\Temp\009 Ålesund - Art Deco tower.jpg"""
With procInfo
.Arguments = zipArguments
.FileName = "c:\Program files\WinZip\wzzip.exe"
'1 .RedirectStandardOutput = True
'2 .UseShellExecute = False
.WindowStyle = ProcessWindowStyle.Minimized
End With
Dim endTime = DateAdd(DateInterval.Second, 30, Now())
Dim finished As Boolean = False
Try
proc = Process.Start(procInfo)
Catch ex As Exception
lblMsg.Text = "Unable to start process: " & ex.Message
Exit Sub
End Try
If proc.HasExited Then
' The started process may have activated an existing instance of itself and then exited.
Debug.Assert(False)
End If
' Pause until process has exited or timed out
Do While True
'3 zipOutput = proc.StandardOutput.ReadToEnd()
Debug.Print("Sleep")
Threading.Thread.Sleep(500)
If proc.HasExited Then
finished = True
Exit Do
End If
If Now() > endTime Then
Exit Do
End If
Loop
If finished Then
lblMsg.Text = "Process finished with exit code " & proc.ExitCode
Else
Try
proc.Kill()
lblMsg.Text = "Process did not terminate so I killed it"
Catch ex As Exception
lblMsg.Text = "Process did not terminate and my attempt to kill it failed."
End Try
End If
lblMsg.Text &= vbLf & zipOutput
Debug.Print(lblMsg.Text)
End Sub
End Class
Figured it out. Try this.
' Pause until process has exited or timed out
zipOutput = proc.StandardOutput.ReadToEnd()
proc.WaitForExit()
lblMsg.Text = "Process finished with exit code " & proc.ExitCode
lblMsg.Text &= vbLf & zipOutput
I'm not sure exactly why you're getting the invalid handle error, but it might be something to do with this excerpt from msdn here. I'm guessing that adding the Proc.WaitForExit makes your code wait until the previous zipOutput = proc.StandardOutput.ReadToEnd() has finished working.
The redirected StandardOutput stream can be read synchronously or
asynchronously. Methods such as Read, ReadLine, and ReadToEnd perform
synchronous read operations on the output stream of the process. These
synchronous read operations do not complete until the associated
Process writes to its StandardOutput stream, or closes the stream.
Full Code Here -
Public Class Form1
Private Sub btnExit_Click(sender As System.Object, e As System.EventArgs) _
Handles btnExit.Click
Me.Close()
End Sub
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) _
Handles MyBase.Load
' Process, ProcessStartInfo and ProcessWindowStyle within namespace System.Diagnostics
' Threading within namespace System
Dim proc As Process = Nothing
Dim procInfo As New ProcessStartInfo
Dim zipOutput As String = ""
Dim zipArguments As String = "-a -s""12345678"" -ycAES256 -whs ""C:\Secure\Temp\test.zip""" &
" ""C:\Secure\Temp\009 Ålesund - Art Deco tower.jpg"""
With procInfo
.Arguments = zipArguments
.FileName = "c:\Program files\WinZip\wzzip.exe"
.RedirectStandardOutput = True
.UseShellExecute = False
.WindowStyle = ProcessWindowStyle.Minimized
End With
Dim endTime = DateAdd(DateInterval.Second, 30, Now())
Dim finished As Boolean = False
Try
proc = Process.Start(procInfo)
Catch ex As Exception
lblMsg.Text = "Unable to start process: " & ex.Message
Exit Sub
End Try
If proc.HasExited Then
' The started process may have activated an existing instance of itself and then exited.
Debug.Assert(False)
End If
' Pause until process has exited or timed out
zipOutput = proc.StandardOutput.ReadToEnd()
proc.WaitForExit()
lblMsg.Text = "Process finished with exit code " & proc.ExitCode
lblMsg.Text &= vbLf & zipOutput
Debug.Print(lblMsg.Text)
End Sub
End Class

Loop to print text files is skipping some files(randomly, it seems)

I have a VB.NET program which lists some text files in a directory and loops through them. For each file, the program calls notepad.exe with the /p parameter and the filename to print the file, then copies the file to a history directory, sleeps for 5 seconds(to allow notepad to open and print), and finally deletes the original file.
What's happening is, instead of printing every single text file, it is only printing "random" files from the directory. Every single text file gets copied to the history directory and deleted from the original however, so I know that it is definitely listing all of the files and attempting to process each one. I've tried adding a call to Thread.Sleep for 5000 milliseconds, then changed it to 10000 milliseconds to be sure that the original file wasn't getting deleted before notepad grabbed it to print.
I'm more curious about what is actually happening than anything (a fix would be nice too!). I manually moved some of the files that did not print to the original directory, removing them from the history directory, and reran the program, where they DID print as they should, so I know it shouldn't be the files themselves, but something to do with the code.
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim f() As String = ListFiles("l:\", "997")
Dim i As Integer
Try
For i = 0 To f.Length - 1
If Not f(i) = "" Then
System.Diagnostics.Process.Start("Notepad.exe", " /p l:\" & f(i))
My.Computer.FileSystem.CopyFile("l:\" & f(i), "k:\" & f(i))
'Thread.Sleep(5000)
Thread.Sleep(10000)
My.Computer.FileSystem.DeleteFile("l:\" & f(i))
End If
Next
'Thread.Sleep(5000)
Thread.Sleep(10000)
Catch ex As Exception
End Try
End Sub
Public Function ListFiles(ByVal strFilePath As String, ByVal strFileFilter As String) As String()
'finds all files in the strFilePath variable and matches them to the strFileFilter variable
'adds to string array strFiles if filename matches filter
Dim i As Integer = 0
Dim strFileName As String
Dim strFiles(0) As String
Dim strExclude As String = ""
Dim pos As Integer = 0
Dim posinc As Integer = 0
strFileName = Dir(strFilePath)
Do While Len(strFileName) > 0
'check to see if filename matches filter
If InStr(strFileName, strFileFilter) Then
If InStr(strFileName, "997") Then
FileOpen(1, strFilePath & strFileName, OpenMode.Input)
Do Until EOF(1)
strExclude = InputString(1, LOF(1))
Loop
pos = InStr(UCase(strExclude), "MANIFEST")
posinc = posinc + pos
pos = InStr(UCase(strExclude), "INVOICE")
posinc = posinc + pos
FileClose(1)
Else : posinc = 1
End If
If posinc > 0 Then
'add file to array
ReDim Preserve strFiles(i)
strFiles(i) = strFileName
i += 1
Else
My.Computer.FileSystem.MoveFile("l:\" & strFileName, "k:\" & strFileName)
End If
'MsgBox(strFileName & " " & IO.File.GetLastWriteTime(strFileName).ToString)
pos = 0
posinc = 0
End If
'get the next file
strFileName = Dir()
Loop
Return strFiles
End Function
Brief overview of the code above. An automated program fills the "L:\" directory with text files, and this program needs to print out certain files with "997" in the filename (namely files with "997" in the filename and containing the text "INVOICE" or "MANIFEST"). The ListFiles function does exactly this, then back in the Form1_Load() sub it is supposed to print each file, copy it, and delete the original.
Something to note, this code is developed in Visual Studio 2013 on Windows 7. The machine that actually runs this program is still on Windows XP.
I can see a few issues. the first and most obvious is the error handling:
You have a Try.. Catch with no error handling. You may be running in to an error without knowing it!! Add some output here, so you know if that is the case.
The second issue is to do with the way you are handling Process classes.
Instead of just calling System.Diagnostics.Process.Start in a loop and sleeping you should use the inbuilt method of handling execution. You are also not disposing of anything, which makes me die a little inside.
Try something like
Using p As New System.Diagnostics.Process
p.Start("Notepad.exe", " /p l:\" & f(i))
p.WaitForExit()
End Using
With both of these changes in place you should not have any issues. If you do there should at least be errors for you to look at and provide here, if necessary.

Concurrency Issues with multiple linked aacdb files

I've started to run into some concurrency issues with my databases. I have roughly ten different aacdb files in a shared location on our office network. One of these databases is kind of the 'master' database. It is split into backend and front end. The backend of this databases holds common tables such as users/passwords, employees, departments, etc etc.
Yesterday, I made two databases purely for input. They each have a single form bound to a table in 'data entry' mode, with record locks set to 'edited record.' They also link to some of the same tables shared by other databases. This is where I started to run into (likely?) concurrency issues for the first time.
People have been reporting odd behavior (forms not opening, etc) in the 'master' database. This was tested a bit and only happens when users are also in the linked data-entry only databases.
There are still less than ten current users across all of the databases at a given time.
Would drop down selections hold a lock on a table, preventing certain forms from opening?
AFAIK, dropdowns are just queried when the form is loaded.
Any ideas?
I had fits with this issue, trying to have several users share the same front end from a network share. Things would just...not work. Then when I went back it was impossible to dupilcate the failures. I decided to have the application installed on the local machines, but this had version control issues, especially since I had several different front ends running at the same time for different projects. There were updaters out there but they either cost money or I couldnt see the code and didnt trust them. I came up with this as a solution and have been using it since Access 2003.
This is a seperate ACCESS database, you have to lock it down just like you would any front end.
This launcher works for the four access front ends that I am running right now. There are two table that you have to setup on the network.
TABLE NAME: RunTimeTracking
FIELD: RTTID : AutoNumber
FIELD: RTTComputerName : Text
FIELD: RTTLoginTime : Date/Time
TABLE NAME: VersionControlTable
FIELD: VCTID : AutoNumber
FIELD: VCTVersion : Number
FIELD: VCTSourceLoc : Text
FIELD: VCTDest : Text
FIELD: VCTDateVer : Date/Time
The RunTimeTracking table works to prevent the user from starting the actual application without using the launcher. When the launcher runs it inserts a entry into the table with the computer name. When the application runs it looks for that entry, if it doesnt see it. It warns and dumps.
In the version control table put the location of the most up to date app, the location on the local machine where you want the applicaiton to be stored.
If you have more than one program that you are controlling, then increment VCTVersion entry and reference it in your code in the launcher.
strSQL = "SELECT * FROM VersionControlTable WHERE VCTVersion = 200"
When the launcher runs it checks the CREATED datestamp on the local file to the one on the network, if they are different, it copies. If not, it runs.
Private Sub Form_Load()
DoCmd.ShowToolbar "Ribbon", acToolbarNo
DoCmd.ShowToolbar "Status Bar", acToolbarNo
DoCmd.Maximize
Form.TimerInterval = 2000
End Sub
Private Sub Form_Timer()
runDataCheck
End Sub
Private Sub runDataCheck()
' This is the launcher program. This program is designed to check for
' Version information and upload and download the new version automaticaly.
' Place entry into the Run Time Tracking Table. This will be used by the Main Application to verify that
' The application was launched by the Launcher and not run straight from the desktop
'First, retrieve the name of the computer from the Environment.
Dim strCompName As String
strCompName = Environ("computername")
' Now, delete all entries on the tracking table that have this computer name associated with it.
' Later we will try to add a trigger that archives the logins.
Dim strSQL As String
strSQL = "DELETE FROM RunTimeTracking WHERE RTTComputerName = '" & strCompName & "'"
adoSQLexec (strSQL)
' Now, add and entry into the table
strSQL = "INSERT INTO RunTimeTracking (RTTComputerName,RTTLoginTime) VALUES ('" & strCompName & "','" & Now() & "')"
adoSQLexec (strSQL)
' First, retrieve the parameters from the Version Control File and put them into variables that we can use.
Dim strSource As String
Dim strDest As String
Dim dateVer As Date
Dim rs As New ADODB.Recordset
'LBLSplashLabel.Caption = "Checking Version Information...."
strSQL = "SELECT * FROM VersionControlTable WHERE VCTVersion = 200"
With rs
rs.Open strSQL, CurrentProject.Connection
End With
strSource = rs.Fields("VCTSourceLoc").Value
strDest = rs.Fields("VCTDest").Value
dateVer = rs.Fields("VCTDateVer").Value
Set rs = Nothing
' Next. See if the folders on both the local drive and the source drive exists.
Dim binLocal As Boolean
Dim binNet As Boolean
Dim binDirectoryLocal As Boolean
'Debug.Print strSource
' First check to see if the network file exists.
binNet = FileExists(strSource)
If binNet = False Then
MsgBox ("The network source files are missing. Please contact Maintenance!")
Application.Quit (acQuitSaveNone)
End If
' Get the timestamp from the network version since it exists.
Dim fileNet As File
Dim fileLocal As File
Dim fileNetObject As New FileSystemObject
Set fileNet = fileNetObject.GetFile(strSource)
Debug.Print strSource
Debug.Print "Created Date : " & fileNet.DateCreated
Dim strDirName As String
Dim intFind As Integer
' Check to see if the Local file Exists.
binLocal = FileExists(strDest)
If binLocal = False Then
'There is no local file. Check to see if the directory exists
' Get the directory name
intFind = (InStrRev(strDest, "\", , vbTextCompare))
strDirName = (Left(strDest, intFind - 1))
Debug.Print "Directory Name: " & strDirName
binDirectoryLocal = FolderExists(strDirName)
If binDirectoryLocal = False Then
'There is no local directory. Create one
MkDir (strDirName)
' LBLSplashLabel.Caption = "Copying Files...."
'Copy the source file to the directory.
FileCopy strSource, strDest
'Since we have no copied the latest version over, no need to continue. Open the main app
OpenMaintApp (strDest)
Else
' No need to create the directory, simply copy the file.
'Copy the source file to the directory.
' LBLSplashLabel.Caption = "Copying Files...."
FileCopy strSource, strDest
'Since we have no copied the latest version over, no need to continue. Open the main app
OpenMaintApp (strDest)
End If
End If
'Now we know that the file is in the directory, now we need to check its version.
'Get the last modified date from the file.
Set fileLocal = fileNetObject.GetFile(strDest)
Debug.Print "Last Modified Date : " & fileLocal.DateCreated
'Do the version check
If fileLocal.DateCreated <> fileNet.DateCreated Then
' LBLSplashLabel.Caption = "Copying Files...."
'Copy the source file to the directory.
FileCopy strSource, strDest
'Since we have no copied the latest version over, no need to continue. Open the main app
OpenMaintApp (strDest)
Else
OpenMaintApp (strDest)
End If
OpenMaintApp (strDest)
End Sub
Private Sub OpenMaintApp(strAppName As String)
Dim accapp As Access.Application
Set accapp = New Access.Application
accapp.OpenCurrentDatabase (strAppName)
accapp.Visible = True
DoCmd.Quit acQuitSaveNone
End Sub