DWUTKA at marlow.com
DWUTKA at marlow.com
Tue May 17 19:02:29 CDT 2005
I would not agree that a global should be used there, it is not declared for scope, but for convenience. Drew -----Original Message----- From: Dan Waters [mailto:dwaters at usinternet.com] Sent: Tuesday, May 17, 2005 6:05 PM To: 'Access Developers discussion and problem solving' Subject: RE: [AccessD] Global Variable Drew, I'm making them global purely for my convenience. I use these variables in perhaps hundreds of procedures and functions. Dimensioning them once as global variables in a standard module instead of in every form, report, or standard module is much more convenient. It would be more technically correct to Dimension these variables in each procedure or function where they are used. The value in these variables will be lost if there is an unhandled error, but it doesn't matter because the value I'm interested in only lasts until that value is passed to the GlobalErrors procedure. Also, I don't use these variables in any other context throughout my applications. The reason I use a variable at all is that both Err.Number and Err.Description are 'one-trick ponies'. As soon as you use them in an expression, the value of each goes to null. So, because I want to use the value of Err.Number in a Select Case and also pass it to GlobalErrors, I first have to read the value to a Dimensioned variable which will maintain the value of the error number. Dan Waters -----Original Message----- From: accessd-bounces at databaseadvisors.com [mailto:accessd-bounces at databaseadvisors.com] On Behalf Of DWUTKA at marlow.com Sent: Tuesday, May 17, 2005 10:27 AM To: accessd at databaseadvisors.com Subject: RE: [AccessD] Global Variable I just skimmed to the bottom, where you are setting both variables to the Err objects Number and description. If those are global, I would wonder why...because you are passing them through a function. The function is receiving the values they represent, so why make them global? Drew -----Original Message----- From: Dan Waters [mailto:dwaters at usinternet.com] Sent: Monday, May 16, 2005 7:34 PM To: 'Access Developers discussion and problem solving' Subject: RE: [AccessD] Global Variable Drew, Yes, I also put error handling into almost all procedures. Below is an directly copied example of a public procedure that I keep in a standard module. Errors are collected in a table, and when the first person at a customer site opens the app, the contents of the table are placed in the message of an email and sent to me in the background. This way I can play 'mother ship' and all my children tell me how they're doing every day! A TEST FOR YOU: Look at GlngErrNumber and GstgErrDescription. Why am I doing this and why is it OK? Dan Waters Public Sub CreateCustomSpreadsheet(stgProcessName As String, stgSQL As String) If ErrorTrapping = True Then On Error GoTo EH '-- This will create a spreadsheet based on user criteria from the different custom reporting screens Dim fso As FileSystemObject Dim stgFolderName As String Dim stgSpreadsheetName As String Dim frm As Form If stgProcessName = "AllBackups" Then Set frm = Forms("frmPeopleCustomReports") Else Set frm = Forms("frm" & stgProcessName & "CustomReports") End If Set fso = CreateObject("Scripting.FileSystemObject") stgFolderName = "C:\" & SystemAcronym & "\" If fso.FolderExists(stgFolderName) = False Then fso.CreateFolder (stgFolderName) End If stgFolderName = "C:\" & SystemAcronym & "\Spreadsheets\" If fso.FolderExists(stgFolderName) = False Then fso.CreateFolder (stgFolderName) End If Set fso = Nothing stgSpreadsheetName = SystemAcronym & " " & stgProcessName & " Custom " & CurrentDateTimeString & ".xls" Select Case stgProcessName Case "DBM" DBEngine(0)(0).QueryDefs("qryDBMCustomSpreadsheet").SQL = stgSQL Case "SDEV" DBEngine(0)(0).QueryDefs("qrySDEVCustomSpreadsheet").SQL = stgSQL Case "CAPA" DBEngine(0)(0).QueryDefs("qryCAPACustomSpreadsheet").SQL = stgSQL Case "People" DBEngine(0)(0).QueryDefs("qryPeopleCustomSpreadsheet").SQL = stgSQL Case "InspectionBatchCreate" DBEngine(0)(0).QueryDefs("qryInspectionBatchCreateCustomSpreadsheet").SQL = "SELECT * FROM tblInspectionBatchCreate IN '" & BEFullPath & "' WHERE " & stgSQL Case "InspectionBatchData" DBEngine(0)(0).QueryDefs("qryInspectionBatchDataCustomSpreadsheet").SQL = stgSQL Case "AllBackups" '-- The qryAllBackupsCustomSpreadsheet simply picks up the entire tblFEAllBackups table Case Else DBEngine(0)(0).QueryDefs("qry" & stgProcessName & "CustomSpreadsheet").SQL = "SELECT * FROM tbl" & stgProcessName & "Main IN '" & BEFullPath & "' WHERE " & stgSQL End Select DoCmd.TransferSpreadsheet acExport, , "qry" & stgProcessName & "CustomSpreadsheet", stgFolderName & stgSpreadsheetName If FormattedMsgBox(GstgQuestion, "The spreadsheet titled " _ & vbCrLf & vbCrLf _ & stgSpreadsheetName _ & vbCrLf & vbCrLf _ & " has been created in the C:\" & SystemAcronym & "\Spreadsheets folder on your PC." _ & vbCrLf & vbCrLf & vbCrLf _ & "Do you want to open that spreadsheet now?@ @", vbYesNo + vbDefaultButton1, "Spreadsheet Created") = vbYes Then If stgProcessName = "AllBackups" Then frm!butAllBackupsSpreadsheet.HyperlinkAddress = stgFolderName & stgSpreadsheetName frm!butAllBackupsSpreadsheet.Hyperlink.Follow Else frm!butSpreadsheetHyperlink.HyperlinkAddress = stgFolderName & stgSpreadsheetName frm!butSpreadsheetHyperlink.Hyperlink.Follow End If End If Exit Sub EH: Application.Echo True DoCmd.Hourglass False GlngErrNumber = Err.Number GstgErrDescription = Err.Description Select Case GlngErrNumber Case 58 '-- Folder already exists Resume Next Case 287 '-- User chose to cancel after 'virus' warning message Resume Next Case Else Call GlobalErrors("", GlngErrNumber, GstgErrDescription, "Create Custom Spreadsheet", "CreateCustomSpreadsheet") End Select End Sub -----Original Message----- From: accessd-bounces at databaseadvisors.com [mailto:accessd-bounces at databaseadvisors.com] On Behalf Of DWUTKA at marlow.com Sent: Monday, May 16, 2005 5:09 PM To: accessd at databaseadvisors.com Subject: RE: [AccessD] Global Variable Okay, now I can't tell if you are being sarcastic or not. If you think module AND global level variables are bad practice....well, that only leaves procedural, and you might as well stop programming.... You CAN be 100% sure that you don't have unhandled errors. Put Error handling procedures into every procedure! Now that is a GOOD practice. Admittedly, I don't do that myself, not on personal stuff...... (now THAT, I'm willing to concede is a bad practice! <grin>). If you can be sure you're not using global variables (or modular level), then you could also be sure that you have no unhandled errors! Maybe you don't understand this, so I'll explain this for you, please don't think I'm trying to be obnoxious, but if you don't understand error handling, then it would explain your statements below. One, If you start every procedure (Function or Sub (and Property statements in classes)) with On Error statements, you cannot have an unhandled error. On Error Resume Next will simply let every error just pass right through the system. Obviously...not smart, but sometimes it is actually necessary. On Error Goto Well, with that statement, you can force the code to go to an 'Error Handler', where you can do all sorts of things. You can accomodate an error (sometimes, you program expecting an error to occur, so getting into the errorhandler is by design in those cases). Let's say that you put in ErrorHandling when a user puts text data into a numeric field. Now, let's say that you might expect the user to type 'one', instead of '1'. You may decide to create a text to number function, so that the user never knows something is wrong, you fix the issue, and continue your code. To do that, your errorhandler has to determine if the error applies (wrong data type in that case). Even if you don't know what type of error it is, you can always display the error number and description, and move on. (Use a Select Case statement in the errorhandler...and include the Case Else statement). Either way, if you have Errorhandling in your procedures, you'll never have the code stopped by the user (unless you let them get into your code!). The only time this does NOT apply, is when you have an exception fault. Yes, you can cause that from within VBA...don't ask me how, cause I'm drawing a blank, but I've done it before! <grin> In cases like that, the Error handling isn't triggered....instead, ACCESS gets shut down. If that happens, it doesn't really matter that the global variables will be lost, because EVERYTHING is lost! Does that make sense? Drew -----Original Message----- From: Dan Waters [mailto:dwaters at usinternet.com] Sent: Monday, May 16, 2005 4:45 PM To: 'Access Developers discussion and problem solving' Subject: RE: [AccessD] Global Variable Drew, Globals are bad practice because: If I use global variables and I have an unhandled error I can be 100% sure that my global variable value will be lost. I cannot be 100% sure that I won't have unhandled errors. I can be 100% sure that I'm not using global variables. If I could be 100% certain that I won't have unhandled errors, then global variables would be a good practice. Dan Waters PS - Based on what you pointed out about module level variables, I think using them is bad practice too! See - I learned something new because you told me 'why'! -----Original Message----- From: accessd-bounces at databaseadvisors.com [mailto:accessd-bounces at databaseadvisors.com] On Behalf Of DWUTKA at marlow.com Sent: Monday, May 16, 2005 3:53 PM To: accessd at databaseadvisors.com Subject: RE: [AccessD] Global Variable Right, but it's a jaded, and actually illogical proclamation that Globals are bad practice. Let's look at this Dan. What's the argument that they are bad practice? Because stopping the code makes them lose their values. Okay, that is true, but does it apply to anything else? It sure does, but the 'general consensus' folks are only worried about Globals. Why? Who knows. Let's test our theory. Make a form, and put three command buttons on it. Now paste the following code behind the form (and set the events for the form's load, and the command buttons 'On Click' events. Option Compare Database Option Explicit Private intSomeNumber As Long Private Sub Command0_Click() intSomeNumber = 5 End Sub Private Sub Command1_Click() MsgBox intSomeNumber End Sub Private Sub Command2_Click() Dim x As Long x = 1 / 0 End Sub Private Sub Form_Load() intSomeNumber = 1 End Sub Open the form, and hit the first button. Now hit the second button. We get 5. Close the form, open the form, and hit the second button. We get 1. All as expected right? Now hit the third button...uh oh, an error, it's not handled. Now hit 'end' (which stops the code from executing). To the user, the error is 'over'. Nothing to worry about, right? Wrong, we just lost ALL variables. Hit the second button now, we get a big ZERO!!!! Now, if we say that global variables are bad practice, because they lose their value when the code is stopped from an unhandled error, shouldn't we also say that module level variables are bad practice? If we dimensioned intSomeNumber as 'Public MyNumericValue', which takes a module level variable, and makes it a PROPERTY of the form, we get the same results. The stop in the execution of the code cause the property to lose it's value! So should we also apply the 'bad practice' principle to class properties? Of course not, because we'd be left with procedural level variables, which, by the way, ALSO lose their values when the code is stopped, but that is completely unnoticed, since the procedure being run just stopped! Now, let's change Command2_Click() to this: Private Sub Command2_Click() On Error GoTo ErrorHandler Dim x As Long x = 1 / 0 Exit Sub ErrorHandler: MsgBox Err.Number & " - " & Err.Description Err.Clear End Sub Now, same process, and look at that...the values are still there. (so would the values in a global variable, and a class property). Now, tell me Dan, which is bad practice? Global Variables, or missing/improper error handling? Eh? Now for why I get into this type of discussion, and why I may appear to 'rail' against the norm. Let's take Jim Bob the new developer. He's just starting out in coding, but is a very fast learner. He's tearing through using classes, and creating custom events, and is progressing at an amazing pace. He's done this, of course, by being an avid reader on AccessD. Now, he's seen posts from people like Charlotte, and JC, people who's coding and experience is nothing but impressive. He sees a post by them that says 'Don't get carried away with them, they are considered bad practice in general.'. Now, whether or not Jim Bob the Developer has specifically had one of them help him with his code, he has more then likely made use of the knowledge he has gleaned from their posts. Now he is in a situation where using Global variables is the best solution. He may have to use a lot of them. But it all works just fine, and does exactly what it's supposed to do. But wait a minute, people he respected, and simply took their word as law in the coding world said that it was 'bad practice'. There must be something he doesn't know about global variables. It's 'iffy'. That's the concept that comes across. So he doesn't use Global Variables. Instead, he writes 5 times more code (ya, look at the typing difference between Global XYZ, and a function to handle preloading etc....) or more, and now his code is NOT as efficient as it should be, and he's wasting time. What's worse, is that when Jim Bob the Developer starts posting to AccessD as Jim Bob the experienced and world renowned developer, he is going to tell Jim Bob Junior, the 'new' developer that Global Variables are 'bad practice'...and the cycle continues. This particular 'bad practice' issue is fed by the misconception that there is other alternatives that are just as good as a Global Variable. Not really. Pulling a value from a table takes a lot more time then just pulling it from memory. As I've just shown you, even module level variables will lose their values with unhandled errors, so you can't even keep a 'global form' with values, unless you put them as values in a textbox. So...values in a textbox....that's a viable option, right? Wrong. It's now where near as efficient as an actual Global variable. First of all, you have all of the overhead of a form being opened. Then, when getting the value, you aren't going to a register in memory, you are going to a form, then a control, then a property on that control. Granted, even on 100 mhz machines, the speed and memory issues are inconsequential, but on a larger scale, it becomes much more apparent. And, there are uses for global variables that are MUCH more difficult to try and replicate with another method. For instance, I developed an inventory system, where all sorts of things were done to the inventory. If a user changes anything in the inventory, I used a custom class, set as a global variable, which was used for ALL transactions. Whenever something was changed, it raised events to alert to the various changes. Anything that used the inventory class would immediately refresh it's data when the event was raised. Without having that class as a Global Variable, to do the same type of 'live update' on everything would have been a very daunting task. Each new process would have to 'trigger' every existing process, and all of the existing processes would have to be setup to trigger the new ones. With a Global 'class', only the new stuff needed coding, as you would want, and all of the old stuff just 'knew' what was happening. Sure, I know the people that preach 'bad practices' probably won't stop, or change their preaching, but hopefully Jim Bob the new developer will see MY posts, and not pick up the 'bad practice' of thinking something is iffy when it's not. Drew -----Original Message----- From: Dan Waters [mailto:dwaters at usinternet.com] Sent: Monday, May 16, 2005 2:31 PM To: 'Access Developers discussion and problem solving' Subject: RE: [AccessD] Global Variable Drew, For me, it helps a lot to understand 'why' I should follow a particular practice. If I understand why, I can expand of what is being stated by applying the 'why' to my circumstance, and therefore learn even more! Dan Waters -----Original Message----- From: accessd-bounces at databaseadvisors.com [mailto:accessd-bounces at databaseadvisors.com] On Behalf Of DWUTKA at marlow.com Sent: Monday, May 16, 2005 1:50 PM To: accessd at databaseadvisors.com Subject: RE: [AccessD] Global Variable You know, I think this whole debate could be avoided, again, if it was just stated that using global variables when a 'lesser' scope should be used, is bad practice. I know I get into these discussions, because I am self taught, and it takes a LOT more energy to overcome incumbent thought obstacles then it does to learn new things. Drew -----Original Message----- From: John W. Colby [mailto:jwcolby at colbyconsulting.com] Sent: Monday, May 16, 2005 12:31 PM To: 'Access Developers discussion and problem solving' Subject: RE: [AccessD] Global Variable Gustav, There are many things generally considered poor programming practice in the programming industry. Excessive use of Gotos and Global variables are a couple of examples. In general they ARE CONSIDERED (by myself and many many many other people) to be bad programming practices. Globals, like Gotos are handed down from the days of old when there was no such thing as functions, data types declarations, scope and the many other tools that make modern programming worlds above the code written 40 years ago. All of these things exist for a reason. Globals are considered poor programming practice because they expose variables to update and even overwriting by code that has no business updating that variable. It is generally accepted practice that variables should have the minimum exposure absolutely necessary to perform their function. A variable used ONLY in a specific function should NOT EVER be declared as a global variable. A variable used ONLY in a module should NOT EVER be declared a global. Microsoft and other compiler writers provide tools like variable scope for a good reason, they help minimize, and track down bugs. If a variable is private to a module then if that variable is being updated incorrectly, you at least know it is because of some code in that module. If it can be updated anywhere in the entire program then you must search the entire program when something goes wrong. Function level variables (declared inside a function) are local to the function and cannot be declared otherwise. Thus the "excessive use" of globals would include making a variable, intended for use in a module, global "just in case". I personally make it a practice to always explicitly declare module level variables private Private MyVar as SomeVarType unless there is an over-riding reason to expose the variable on a global basis. Even then I make it a practice to build a function that reads a private variable in those cases where code in other modules must be able to read the variable. Only in cases where code in multiple modules must both read and write a global variable will I expose the variable itself as a global. And even in those cases it is sometime useful to cause the write to go through a function, particularly if the variable is a computed value. Global variables is one of those issues that strikes a sensitive note with many programmers, but it is indeed widely accepted to be poor programming practice to just use them willy-nilly and for no good reason. Every tool exists for a reason and variable scope is a very powerful tool to assist the programmer in writing better code. John W. Colby www.ColbyConsulting.com Contribute your unused CPU cycles to a good cause: http://folding.stanford.edu/ -----Original Message----- From: accessd-bounces at databaseadvisors.com [mailto:accessd-bounces at databaseadvisors.com] On Behalf Of Gustav Brock Sent: Monday, May 16, 2005 12:34 PM To: accessd at databaseadvisors.com Subject: RE: [AccessD] Global Variable Hi John You may like or prefer or need, or not, a global variable - that's up to everyone to decide for him/herself - but they are not "bad practice", not even in general. You may write clumsy code using globals, but not using them gives no guarantee for nice code. /gustav -- AccessD mailing list AccessD at databaseadvisors.com http://databaseadvisors.com/mailman/listinfo/accessd Website: http://www.databaseadvisors.com -- AccessD mailing list AccessD at databaseadvisors.com http://databaseadvisors.com/mailman/listinfo/accessd Website: http://www.databaseadvisors.com -- AccessD mailing list AccessD at databaseadvisors.com http://databaseadvisors.com/mailman/listinfo/accessd Website: http://www.databaseadvisors.com -- AccessD mailing list AccessD at databaseadvisors.com http://databaseadvisors.com/mailman/listinfo/accessd Website: http://www.databaseadvisors.com -- AccessD mailing list AccessD at databaseadvisors.com http://databaseadvisors.com/mailman/listinfo/accessd Website: http://www.databaseadvisors.com -- AccessD mailing list AccessD at databaseadvisors.com http://databaseadvisors.com/mailman/listinfo/accessd Website: http://www.databaseadvisors.com -- AccessD mailing list AccessD at databaseadvisors.com http://databaseadvisors.com/mailman/listinfo/accessd Website: http://www.databaseadvisors.com -- AccessD mailing list AccessD at databaseadvisors.com http://databaseadvisors.com/mailman/listinfo/accessd Website: http://www.databaseadvisors.com -- AccessD mailing list AccessD at databaseadvisors.com http://databaseadvisors.com/mailman/listinfo/accessd Website: http://www.databaseadvisors.com