jwcolby
jwcolby at colbyconsulting.com
Thu Dec 15 20:50:39 CST 2011
Thanks for those suggestions. My methods are crude and I have always wanted to get a little more sophisticated. I will have to spend a little time thinking about the suggestions you discuss. John W. Colby Colby Consulting Reality is what refuses to go away when you do not believe in it On 12/15/2011 5:40 PM, Dan Waters wrote: > Yes - Library referencing can be a pain. > > At my customers each client has a shortcut which points to an AutoUpdater > file. The AutoUpdater file will check to see if the Main and Library files > on the client are older than the files on the server. If so, it will copy > those files up to the client in the same folder. > > But here's the problem - the main file on the client will still reference > the library file on the server. The way I get around that is to leave the > library file on the server with an XX in the name - that way the main file > on the client can't find it so it re-references to the library file on the > client (they're in the same folder). > > When the autoupdater file does its thing, it compares modified dates between > Library.mdb on the client and LibraryXX.mdb on the server. If the server > has the newer file, then autoupdater will copy and rename the LibraryXX.mdb > file on the server to Library.mdb on the client. > > Another problem is that the Library.mdb file is renamed on the server (your > dev/test system), so you can't open it until you manually retype the name, > and then you have to remember to retype it back when you're done. To solve > that I made a ChangeXX.mdb file. It has an AutoExec macro which runs the > following code: > > '------------------------ > Public Function StartupChangeXX() > On Error GoTo EH > > Dim stg As String > Dim rst As DAO.Recordset > Dim fso As FileSystemObject > Dim blnAddXX As Boolean > Dim stgXXFile As String > Dim stgExtension As String > Dim stgPrompt As String > Dim blnNeedManualPrompt As Boolean > Dim blnFoundAllClear As Boolean > Dim blnFoundAllXX As Boolean > Dim blnFirstLoopComplete As Boolean > Dim stgSystemMode As String > > stgSystemMode = Command() > ' stgSystemMode = "Review" '-- TEST > > Set fso = CreateObject("Scripting.FileSystemObject") > > '-- Do the files exist? > stg = "SELECT FileFullPath FROM tblParameters" _ > & " WHERE SystemMode = '"& stgSystemMode& "'" > Set rst = DBEngine(0)(0).OpenRecordset(stg, dbOpenSnapshot) > Do While rst.EOF = False > > stgExtension = fso.GetExtensionName(rst("FileFullPath")) > stgXXFile = Left(rst("FileFullPath"), Len(rst("FileFullPath")) - 4) > & " XX ."& stgExtension > > If fso.FileExists(rst("FileFullPath")) = False And > fso.FileExists(stgXXFile) = False Then > MsgBox "The file "& rst("FileFullPath")& " does not exist!", > vbExclamation + vbOKOnly, "Missing File" > rst.Close > Set rst = Nothing > Application.Quit > End If > > rst.MoveNext > > Loop > rst.Close > Set rst = Nothing > > > '-- Are all files clear or all XX? > stg = "SELECT FileFullPath FROM tblParameters" _ > & " WHERE SystemMode = '"& stgSystemMode& "'" > Set rst = DBEngine(0)(0).OpenRecordset(stg, dbOpenSnapshot) > Do While rst.EOF = False > > If blnFirstLoopComplete = False Then > If fso.FileExists(rst("FileFullPath")) = True Then > blnFoundAllClear = True > blnAddXX = True > Else > blnFoundAllClear = False > blnAddXX = False > End If > blnFirstLoopComplete = True > Else > If fso.FileExists(rst("FileFullPath")) = True Then > If blnFoundAllClear = False Then > blnNeedManualPrompt = True > Exit Do > End If > Else > If blnFoundAllClear = True Then > blnNeedManualPrompt = True > Exit Do > End If > End If > End If > > rst.MoveNext > > Loop > rst.Close > Set rst = Nothing > > > '-- Select to add XX or Remove XX > If blnNeedManualPrompt = True Then > stgPrompt = "Push Yes to add XX." _ > & vbNewLine& vbNewLine _ > & "Push No to remove XX." > If MsgBox(stgPrompt, vbQuestion + vbYesNo + vbDefaultButton2, > "Change XX") = vbYes Then > blnAddXX = True > Else > blnAddXX = False > End If > End If > > > '-- Add or remove XX > stg = "SELECT FileFullPath FROM tblParameters" _ > & " WHERE SystemMode = '"& stgSystemMode& "'" > Set rst = DBEngine(0)(0).OpenRecordset(stg, dbOpenSnapshot) > Do While rst.EOF = False > > stgExtension = fso.GetExtensionName(rst("FileFullPath")) > stgXXFile = Left(rst("FileFullPath"), Len(rst("FileFullPath")) - 4) > & " XX ."& stgExtension > > If blnAddXX = True Then > > If fso.FileExists(rst("FileFullPath")) = True Then > fso.CopyFile rst("FileFullPath"), stgXXFile, True > fso.DeleteFile rst("FileFullPath") > End If > > Else > > If fso.FileExists(stgXXFile) = True Then > fso.CopyFile stgXXFile, rst("FileFullPath"), True > fso.DeleteFile stgXXFile > End If > > End If > > rst.MoveNext > > Loop > rst.Close > Set rst = Nothing > > > If blnAddXX = True Then > CreateObject("WScript.Shell").PopUp "Added XX.", 1, "XX Change" > Else > CreateObject("WScript.Shell").PopUp "Removed XX.", 1, "XX Change" > End If > > Application.Quit > > Exit Function > > EH: > MsgBox "Error!" _ > & vbNewLine& vbNewLine _ > & "Code: "& Err.Number& vbNewLine _ > & "Desc: "& Err.Description& vbNewLine _ > & "Line: "& Erl > > End Function > '----------------------- > > There is also a tblParameters which contains information about which actual > system I'm working on - I have three at each customer. Prod, Test, and > Review. The shortcut on the desktop which opens ChangeXX.mdb has a command > argument (like '/cmd Test') so the code will know which system it's supposed > to be working on. > > Also, there is a popup message that stays open for 1 second to tell me > whether it added the XX or removed the XX, then ChangeXX.mdb quits. > > Just click the ChangeXX shortcut on the server's desktop until it says > 'Added XX' or 'Removed XX', and you're done! > > Hope this helps! > Dan > > -----Original Message----- > From: accessd-bounces at databaseadvisors.com > [mailto:accessd-bounces at databaseadvisors.com] On Behalf Of jwcolby > Sent: Thursday, December 15, 2011 3:37 PM > To: Access Developers discussion and problem solving > Subject: [AccessD] libraries, References and some subtleties to be > considered > > I use libraries - MDAs - to hold common code, variables and constants. > Libraries are essentially places to put common code so that many different > applications can do things the same way. If a bug is found it can be fixed > in the library, in just one place. > > It is possible for a lib to reference another lib. For example my C2DbFW3G > understands and uses my Presentation Level Security System and so it > references C2DbPLSS. However C2DbPLSS is a standalone library, i.e. it can > be used without my FW3G. Should I have just merged the two into one big > lib? > That is a conversation for another day. > > While on this subject, two more things. There can be no circular references > between libs, i.e. FW3G cannot reference PLSS *and* PLSS also reference > FW3G. Any lib can reference another lib but the reference can never "circle > back around". Additionally the order of reference comes into play if there > are two functions, classes, variables etc with the same name. We all > understand the scope thing (local function, module, global) but the same > issue exists in libraries in that if a name is not found in the local > container the compiler starts looking at other referenced objects, starting > from the top reference in the references dialog and working down. > > This can cause oddities if we have a function (for example) with the same > name found in the application and the library. Code in the application will > use the function inside of the application container, whereas code in the > library will use the function in the library container. > If you use libraries and you write a function and move it to the library, do > not forget to delete the function from the application or you will have > problems. > > I have two main libraries, C2DbFW3G which is the 3rd generation of my > framework, and C2DbPLSS which is my Presentation Level Security System. > > Having an application reference a library causes some issues shall we say > which do not exist if you do not use them, and I just thought I would walk > through my findings and how I handle things in order to start a conversation > on the subject. > > Some tidbits in no particular order. > > When the developer references a library they do so via a browse button and > so the reference ends up specific to a location available from the > developer's machine. This implies that the location may or may not be > available to another user opening the application. > > When the application opens, it tries to find the file at the location > specified in the existing reference. If found it uses that copy of the > library, no questions asked. > > If the library cannot be found at that referenced location then the > application silently begins to search a set of paths to find the library. > > http://support.microsoft.com/kb/824255 > > If the library is found the search immediately ceases and the reference is > "fixed up" to point to that location. When the application closes it saves > that new reference location. So the application has been silently > "re-referenced" to the new location. When I say silently, I mean that there > is no immediate in-your-face indication that any of this happened. > > This silent re-reference can cause odd problems. Let's take some real life > scenarios that I encounter at my client. > > I have a directory on my C: drive at the client called C:\Dev\DisNew\ This > path, in particular the Dev\ part, is unique to my machine (standing for > development). I build a framework and an application in this location. I > reference the lib from the application, browsing to that location and voila, > the reference points to a library in a location that does not physically > exist anywhere else in the company. > > I copy the two files up to X:\DisNew\Test which is the production > (X:\DisNew) test directory. the user has a batch file which builds a > directory on their local C: drive, copies the library and application to > that local directory and opens the application. The application tries to > find the lib at my dev directory and fails, so it tries to find it in the > local directory and succeeds. Life is good. > > Now... I go into the X:\Disnew\Tester directory and open the application > file. Guess what happens? > The application opens and tests the reference and... finds it because it > can see my dev path. The file works. Life is good, nothing changes. > > A user goes into the X:\DisNew\Tester directory and opens the application > file and ... the application cannot find my dev directory so it starts "the > search". It finds the library in the X:\DisNew\Test directory and > re-references and the application works. Now when the user closes the > file... the file is referenced to the lib on the network. Life is no longer > good! > > Now we decide that the application file tests good and copy it to production > where it is copied, along with the lib down to the user's hard disk. The > user opens the copy on their hard disk and... > the application is referenced to the lib on the network (test directory) and > so it opens the lib on the network. Now I am trying to copy a new version > of the lib to tester and the file is locked. Or something. Life is not > good. > > Let's discuss decompile for a minute. Decompile flushes the pcode buffers > in the Access container, which, simply put, means that all of the "compiled" > code is flushed out. Yes I understand that Access is an interpreter but it > actually compiles the English (VBA) language stuff we write into P-Code and > interprets the P-Code. The compile of the Decompile / Compile matched pair > simply recompiles every single line of VBA code into P-Code and stuffs it > back into the buffers. > > When you perform a decompile / compile, you *REALLY* need to decompile / > compile the library first, then the application using the application. I > don't understand all of the stuff but apparently there is a table of > pointers built by the compile, things like the entry point to functions and > the locations of constant and variables. Apparently when you compile the > application, it goes out and searches the library for these tables in order > to correctly call functions and variables in the library. > > But why do we do a decompile / compile in the first place? Because it is > possible and in fact not uncommon, for the P-Code to get corrupted over > time. If the lib is corrupted and you recompile the app, then the app calls > into corrupted lib stuff. So, decompile / compile the lib *before* you > decompile the application that references the lib. And if you decompile / > compile the lib, then you must must *must* recompile the app because the lib > entry points and variables might change. > > Guess what? If you happen to get confused and decompile / compile anything > on a network share... it may (or may not) cause weird things like the app > refusing to close. So never never *never* decompile / compile anything that > is not local to your hard disk. > > Unfortunately the simple fact that FW3G references the PLSS does not expose > the PLSS on through to the application. So C2DbFW3G references C2DbPLSS and > the application references C2DbPLSS *and* C2DbFW3G because it directly uses > code in both. Oh my goodness. Now I have to decompile / compile the PLSS > first, then the FW3G (because it references PLSS), and then the application > (which directly references both libs). All of this must be done on my local > machine so as to avoid the "can't close" issue discussed above, and then > copied to the final destination for public consumption. > > Furthermore I need to make sure that I reference the PLSS in the FW3G to the > DEV path on my local machine, and likewise reference PLSS and FW3G inside of > the application to the dev path of my local machine. Why? Because that > path is not public to the company and will trigger the re-reference when the > user downloads all this stuff to their local machine. > > But wait, there's more. I have three different applications that use the > PLSS and the framework. > So if I decompile / compile the PLSS / FW3G, all of the applications that > use these libs need to be recompiled. Again, if I make changes to the libs, > any app that I do not decompile will not reacquire the pointer tables in the > libs and may start to fail. > > > And around and around we go. > > I use batch files to copy these pieces to the user's system so that the user > ends up with local copies and doesn't end up permanently re-referencing > things back to the production location. This works reasonably well as long > as everyone plays by the rules. If anyone (other than myself) actually > opens any of these files up in tester or production, then the references > silently change and things go south in a hurry. It took me awhile to figure > out that this was happening (a long time ago) and it took me awhile to > remember that this occurs when I started having strange things happening > recently. That is the reason for starting this thread, to remind the list > how this stuff works and to get input from other list members on their > experiences with this stuff. > > I am a believer in libraries to hold common code. They exist for the simple > reason that changes to that code, bug fixes etc can be done in one place and > propagated to every place the change is needed. It is important to > understand what goes on behind the scenes however or you can have some > strange things happening that will be very difficult to figure out. > > -- > John W. Colby > Colby Consulting > > Reality is what refuses to go away > when you do not believe in it > > -- > AccessD mailing list > AccessD at databaseadvisors.com > http://databaseadvisors.com/mailman/listinfo/accessd > Website: http://www.databaseadvisors.com >