My.Application StartUp event - vb.net

I am rewriting an application for printing shipping labels that we have been using for many years, because over those many years, many programmers have added bits and pieces to it, and it has become harder and harder to maintain.
While considering design choices, I came across catching the application's StartUp event in ApplicationEvents.vb, which I thought would be a good choice for this application because the main form is only displayed if there are more than 10 labels to print at a time (displays a progress bar if so, but mostly it does one label at a time and as such the progress bar isn't desired).
Now, whether this is a good choice I'm not sure, but while playing around with it I have found a need to exit the application from this event if certain criteria aren't met (invalid command line args, network directory not found in expected location, and a few other criteria). I have been unsuccessful in figuring out how to exit from the application if something like the above were to occur. The only thing close I have found is setting the StartupEventArgs.Cancel to True, but the documentation states that this only stops the main form from loading and if set to true should point code on a different processing path.
Is there a "proper" way to exit the application in the StartUp event? I've tried using My.Application.Shutdown which doesn't exist, and RaiseEvent ShutDown() which gives the error "Derived classes cannot raise base class events."
Linked here is the main doc I have been using for research (and the related links on the linked page):
https://msdn.microsoft.com/en-us/library/t4zch4d2(VS.80).aspx

You can use End to immediately kill the process, note that:
This will prematurely kill the process
Any files being written to can become corrupted
Any other activity related to the program will be terminated
To protect against file corruption, you could use a subroutine that 'finishes up' and then terminates the program, for example:
Private Sub finishUp()
[..close files, forms, here..]
End
End Sub
I did this myself for a gameserver program I made a couple of months ago; the program needed to note in the SQL database the startup and shutdown times. I made it log the shut-down time in a subroutine similar to the one above.
Note that the same subroutine could (and probably should) be used when you need the program to close at any other time.

Related

send debug.print to different Immediate window

I have set up a multithreading routine in Access which opens up several Access files at the same time and executes a specific function in each file. The files act in parallel. I would like to send Debug.Print messages to the main file which initiates the multithreading.
I'm not sure if it can do this or if there is a better solution.
You have a number of processes, each working on their own part of the bigger picture.
The immediate pane of the "main" IDE belongs to that instance, in that process; you could Alt+Tab and then Alt+F11 to bring up a VBE instance in any/every instance to view that instance's Debug.Print output.
What you want is something like an ILogger implementation that writes the log entries in a dedicated database table: a DatabaseLogger, for example.
You replace Debug.Print with Logger.Log calls; that way you let the database server deal with the multiple incoming threads, and depending on the RDBMS you could even setup a job to cleanup, aggregate and/or archive the older log records. Or whatever, as long as it's not logic I need to care about in VBA code.
Writing to another VBE's immediate toolwindow involves low-level, cross-process Win32 wizardry that doesn't need to clutter up an otherwise nice & tidy VBA project. I wouldn't bother with that, there are simpler solutions.
You could create a PublicNotCreatable UserForm (not an Access Form) in the main application, and pass a reference to it, to all of the parallel apps. Add a custom method to the UserForm, like 'PrintLine(message as string), and each app can callform.PrintLine("app is running")`?
You could then have the UserForm display the output, or possibly, have the UserForm output the message to the Immediate window of the main application.

Microsoft crash upon reaching "End Sub" in VBA for MSAccess

I'm having a problem with a form that adds a source association to a company record. In the main form, the user specifies the source name to be associated, and then this form opens to allow the user to confirm the name, and pick a year, if that name has more than 1 year associated with it. When the user clicks on "Continue", the code checks if the association already exists, whether the source has multiple years, and if so, if the user has selected which year to put with the associated name. When there are not multiple records (ie the name has only one year,) the continue button works just fine, but when the user has to pick a value of the year, the continue buttons works fine and does its job, entering the new association into the database, until it gets to the Exit sub line under the Exit_cmdAssign_Click line, and then the whole Access stops. I get a message box saying "Microsoft has stopped working", it backs up the database and eventually reopens it. At one point, as it stopped it gave me an option to debug in Visual Studio, but there, there were problems finding necessary files for the debug. Before I tried to debug, it said, "An unhandled win32 exception occurred in DWWIN.EXE [8940]."
Does anyone know how an exit sub can cause such a crash when one extra variable is included in an equation, which actually performs as it should, but then seems to cause problems exiting the sub?
Crashes of Access are not regular errors that you can fix by changing your VBA code. No matter what your code is, under no circumstances should this happen.
That said, from time to time Access does crash. Most of the time the cause lies in an inconsistent compilation state. That can be easily remedied by decompiling the database. Follow the instructions over here: https://stackoverflow.com/a/3266849/6216216
And make sure you have a backup copy of the database before you decompile.

prevent button press across instances

I have created a database/app where a report is created when a particular button is clicked. just now, two people managed to hit the button at exactly the same time, which caused all sorts of not-good.
Is there a way to make a button invisible across instances once it's clicked by one person? Or some way to lock the database so nothing can be done until the person who clicked first is done?
I have a solution (basically, a global check variable that stops the report creation) but now I want to know if either of the other two options can be done.
It would really help to know more about your architecture here. What database? What language have you written your application in? Concurrent reading is usually an important and basic feature of most multi-user databases.
Seconding Daniel Cook's general notion, maybe explicating a bit: don't have the button run the report directly. Have it run a little subroutine that first checks a special purpose table where you represent report "runs" with a new record that has a start date-time and an end date-time. If there is a record sitting in the table with no (null) end-date, then the report must still be running, therefore, do NOT begin report, turn off button instead. Else, insert into that same table and then start running the report. Add to this a periodic, not-too-frequent callback on that button to perform the same check, and you've got something that comes close, but isn't "realtime", but should work in most architectures (not knowing anything about session management capabilities).
Here's what I did:
If DLookup("PayLock", "table", "pkID=1") Then 'it's locked - exit
MsgBox "Someone else has already started the pay process.", vbOKOnly
Exit Sub
Else
blah blah blah......
The "PayLock" field in the table holds the check variable. After "Else" comes the actual code to run when the button is clicked.
Just FYI, since they were asked:
it is split database
there are multiple users
yes, the report just reads data and exports it into an excel spreadsheet.
It looks like this is the only solution, which works, but seems inelegant. I keep discovering that the way I get around my lack of knowledge is the actual way to do it...

How can I protect a process I start from within my vb.net program?

I've created a small application that basically reads and writes to a single Excel.exe process. It's basically a timer that records the time I use on projects and then store it in an Excel sheet. This works great, however, I've noticed that if I open Excel manually, work on some sheets and whatnot, save and exit etcetc, the process my software use gets broken or something. The same thing that happens if I manually close the excel.exe process and my software doesn't "know".
So I was wondering if it's possible to protect the excel.exe process somehow? To make sure it can't be closed or tampered with in the meantime?
Let me suggest an alternative approach that does not require you to have an Excel process running all the time (after all, this also consumes a lot of system resources):
Let your application record your information. Every now and then -- for example, after a work entry has been finished or a specific time has elapsed -- open the Excel sheet, write the data, and close it again (also closing the Excel process that you are automating). This save operation should not take more than a few seconds and it will (mostly) prevent the problem you are experiencing.
In fact, since Office automation is always a bit painful, an even better way would be to output your data without requiring an Excel process. To do this, you could use
one of the third-party Excel libraries available for .net,
a CSV or HTML file, which can be opened by Excel, or
open the Excel file as a database with ADO.NET.
You cannot protect a process, but you can check the process.HasExited property to find out whether the process has terminated and take action based on that.
Add an exception handler. Either call non-throw methods if possible.

How to allow users to quit out of long-running VBA tasks?

I have a routine that examines thousands of records looking for discrepancies. This can take upwards of 5 minutes to complete and although I provide a progress bar and elapsed time count, I'm not sure I want to encourage folk pressing ctrl-break to quit the report should it be taking longer than expected.
A button in the progress bar won't work as the form is non-modal, so is there any neat way of allowing users to quit in this situation?
You need DoEvents and a variable whose scope is greater than the scope of what you're running. That is, if it's just a procedure, you need a module level variable. If it's more than one module, you need a global variable. See here
Stopwatch at DDoE
Normally, the VB engine will tie up the processor until it's done. With DoEvents, however, VB allows the processor to work on whatever is next in the queue, then return to VB.
I don't think there is a way to do it like you would want it to work. VBA is a scripting language so when you start your procedure, it's gonna run until it's done. If you had another button somewhere that even WOULD let you click it while the original procedure was running, I'm not sure how you would reference that procedure and stop it.
You could do something like ask the user if they want to contine, but that would make it run even longer.
Also you could have your procedure check for a condition outside of Excel and keep running as long as it's true. Something easy might be check if a certain text file is in a folder. If you wanted the procedure to stop, open the folder and move the file. On your loop's next iteration, it wouldn't see the file and stop running. Cludgy, inefficient, and not elegant, but it would work. You could also have it check a cell, checkbox, radiobutton, basically any control in another Excel sheet running in another instance of Excel. Again cludgy.
CTRL+Break works. Accept it and move on. One neat trick about that though, is that if you password protect your code and they hit CTRL+Break, the debug option is unavailable and they will only get Continue or End.
If this is code that is run frequently, have you considered scripting something that runs it during times when a human is not using the computer? I used to run telnet screen scraping macros that would take hours to go through our widgets, but I always had them run either on a separate computer or when I wasn't there (nights/weekends).