MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy ––...

137
M M S S H H L L i i b b r r a a r r y y T T o o o o l l s s & & U U t t i i l l i i t t i i e e s s D D o o c c u u m m e e n n t t a a t t i i o o n n Version v2017-04 F F o o r r M M S S A A c c c c e e s s s s 2 2 0 0 0 0 7 7 a a n n d d H H i i g g h h e e r r VBA Code & Documentation by Matthew S Harris

Transcript of MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy ––...

Page 1: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess

DDooccuummeennttaattiioonn VVeerrssiioonn vv22001177--0044

FFoorr MMSS AAcccceessss 22000077 aanndd HHiigghheerr VVBBAA CCooddee && DDooccuummeennttaattiioonn bbyy MMaatttthheeww SS HHaarrrriiss

Page 2: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

Copyright (c) 2014-2017 by Matthew S. Harris

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.

The Didjiman.com graphics, "Didjiman.com", and the name "Didjiman" are part of the original author's identity and may not be reproduced except as part of the original Copyright notices required by the GNU Free Documentation License, the GNU Lesser General Public License and GNU General Public License.

A copy of the documentation license is included in the section entitled "GNU Free Documentation License" (page 123).

This code and documentation is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

The Visual Basic for Applications (VBA) code in this document is licensed to you under the terms of the GNU Lesser General Public License (LGPL), which incorporates and extends the GNU General Public License (GPL). Copies of the GNU LGPL and GNU GPL are included in this document (pages 128 and 130, respectively).

IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED IN THE LICENSE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

MS Office, MS Access, MS Word, MS Outlook, MS Excel, Visual Basic for Applications (VBA) are copyrighted names belonging to Microsoft Corporation.

Page 3: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library – Tools & Utilities For MS Access 2007 and Higher

Table of Contents Originating Developer .................................................................................................................................................................................... 6

Introduction .................................................................................................................................................................................................... 7

Overview ......................................................................................................................................................................................................... 8

libConfirm ..................................................................................................................................................................................................... 10 Confirm_Lookup_GlobalChange .......................................................................................................................................................................... 11 DeletionConfirmed ................................................................................................................................................................................................. 12 DeletionDOUBLEConfirmed .................................................................................................................................................................................. 13

libDataValidation .......................................................................................................................................................................................... 14 FieldHasAwkwardChars ........................................................................................................................................................................................ 14 FindFirst_NullRequiredField ................................................................................................................................................................................. 15 IsItemInComboBoxControl.................................................................................................................................................................................... 16 IsItemInListBoxControl .......................................................................................................................................................................................... 16 Validate_ComboBoxItem ....................................................................................................................................................................................... 17 Validate_YearEntryBox .......................................................................................................................................................................................... 17 Validate_YearEntryComboBox ............................................................................................................................................................................. 18

libDates ......................................................................................................................................................................................................... 19 IsFutureDate ........................................................................................................................................................................................................... 19 msg_NoFutureDates .............................................................................................................................................................................................. 19 IsPastDate ............................................................................................................................................................................................................... 20 msg_NoPastDates .................................................................................................................................................................................................. 20 ListFill_YrMonths ................................................................................................................................................................................................... 20 ListFill_YrQuarters ................................................................................................................................................................................................. 21 NextQuarter ............................................................................................................................................................................................................ 21

libExternalDB ................................................................................................................................................................................................ 22 Create_UserDefined_DB ........................................................................................................................................................................................ 23 Form_Get_List ........................................................................................................................................................................................................ 24 Form_Open ............................................................................................................................................................................................................. 25 Report_Get_List ..................................................................................................................................................................................................... 26 Report_Open .......................................................................................................................................................................................................... 27

libListBoxes .................................................................................................................................................................................................. 28 ListBox_Selection_Clear ....................................................................................................................................................................................... 28 ListBox_Selection_SelectAll ................................................................................................................................................................................. 28 ListBox_Selection_Invert ...................................................................................................................................................................................... 29 ListBox_MoveItemDown ........................................................................................................................................................................................ 29 ListBox_MoveItemUp ............................................................................................................................................................................................. 30 msg_OnlyOneSelectedItemCanBeMoved ............................................................................................................................................................ 30

libOpenArgs.................................................................................................................................................................................................. 31 OpenArgs_Build ..................................................................................................................................................................................................... 32 OpenArgs_GetArgument ....................................................................................................................................................................................... 33

libFiles........................................................................................................................................................................................................... 34 FileExists................................................................................................................................................................................................................. 35

Page 4: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 4 of 137

FolderExists ............................................................................................................................................................................................................ 35 FileName_FromFullPath ........................................................................................................................................................................................ 35 PathName_FromFullPath ...................................................................................................................................................................................... 36 FileExtension_FromFullPath................................................................................................................................................................................. 36 GetFileSize .............................................................................................................................................................................................................. 37 Get_FileDateCreated .............................................................................................................................................................................................. 37 Get_FileDateLastModified ..................................................................................................................................................................................... 37 Get_FileDateLastAccessed ................................................................................................................................................................................... 38 Make_Unique_FileName ........................................................................................................................................................................................ 38 DriveExists .............................................................................................................................................................................................................. 39 Get_DriveList .......................................................................................................................................................................................................... 39 DriveFreeSpace ...................................................................................................................................................................................................... 40 DriveAvailableSpace .............................................................................................................................................................................................. 40 TotalDriveSpace ..................................................................................................................................................................................................... 41 Functions to Scale Size in Bytes to KB, MB, or GB ........................................................................................................................................... 42

libNumeric .................................................................................................................................................................................................... 43 IsInteger .................................................................................................................................................................................................................. 43

libString ........................................................................................................................................................................................................ 44 AssembleName_FirstNameFirst ........................................................................................................................................................................... 45 AssembleName_LastNameFirst ........................................................................................................................................................................... 46 ConcatStringList .................................................................................................................................................................................................... 48

libSystem ...................................................................................................................................................................................................... 50 GetComputerName ................................................................................................................................................................................................ 50 GetComputerName_API ........................................................................................................................................................................................ 50 GetLogonName ...................................................................................................................................................................................................... 51 GetLogonName_API .............................................................................................................................................................................................. 51 CreateLNKShortcut ................................................................................................................................................................................................ 52 CreateURLShortcut ................................................................................................................................................................................................ 53

libTables ....................................................................................................................................................................................................... 54 TableExists ............................................................................................................................................................................................................. 54 TableExists_Linked ................................................................................................................................................................................................ 54 TableExists_Local .................................................................................................................................................................................................. 55 Make_TempTableName ......................................................................................................................................................................................... 55

libUIControl................................................................................................................................................................................................... 56 NavPane_Minimize ................................................................................................................................................................................................. 56 NavPane_Maximize ................................................................................................................................................................................................ 56 NavPane_Hide ........................................................................................................................................................................................................ 57 NavPane_UnHide ................................................................................................................................................................................................... 57

libUnitConversion ........................................................................................................................................................................................ 58 Linear (Distance) Measurement ............................................................................................................................................................................ 59 Area Measurement ................................................................................................................................................................................................. 60 Weight Measurement ............................................................................................................................................................................................. 60 Volume Measurement ............................................................................................................................................................................................ 61 Liquid Measurement .............................................................................................................................................................................................. 62

Page 5: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 5 of 137

Speed Measurement .............................................................................................................................................................................................. 62 Temperature Measurement ................................................................................................................................................................................... 63 Print & Screen Measurement ................................................................................................................................................................................ 63 Color Notation ........................................................................................................................................................................................................ 63

libWindow ..................................................................................................................................................................................................... 64 Reset_Window_Width ............................................................................................................................................................................................ 64 Reset_WindowSize ................................................................................................................................................................................................ 64

Sorter (sorting tools) .................................................................................................................................................................................... 65 Sort_ArrayList ........................................................................................................................................................................................................ 65 Sort_Array2D .......................................................................................................................................................................................................... 66 SortListBox ............................................................................................................................................................................................................. 66 RowSortListBox ..................................................................................................................................................................................................... 67

Code Listings ............................................................................................................................................................................................... 68 The LIBCONFIRM Module .......................................................................................................................................................................................... 68 The LIBDATAVALIDATION Module ............................................................................................................................................................................. 70 The LIBDATES Module ............................................................................................................................................................................................. 75 The LIBEXTERNALDB Module .................................................................................................................................................................................. 78 The LIBLISTBOXES Module ...................................................................................................................................................................................... 82 The LIBOPENARGS Module ...................................................................................................................................................................................... 85 The LIBFILES Module ............................................................................................................................................................................................... 87 The LIBNUMERIC Module .......................................................................................................................................................................................... 96 The LIBSTRING Module ............................................................................................................................................................................................ 97 The LIBSYSTEM Module ........................................................................................................................................................................................... 99 The LIBTABLES Module .......................................................................................................................................................................................... 102 The LIBUICONTROL Module ................................................................................................................................................................................... 104 The LIBUNITCONVERSION Module .......................................................................................................................................................................... 106 The LIBWINDOW Module ........................................................................................................................................................................................ 115 The SORTER Module .............................................................................................................................................................................................. 117

GNU Free Documentation License (FDL) ................................................................................................................................................. 123

Software Licenses ...................................................................................................................................................................................... 128 GNU Lesser General Public License (LGPL) ..................................................................................................................................................... 128 GNU General Public License (GPL) .................................................................................................................................................................... 130

Page 6: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 6 of 137

Originating Developer

MSH Library – Tools And Utilities was programmed and documented by Matthew S. Harris. Matthew is a

database designer, VBA developer (all MS Office applications), and Visual Studio developer. Matthew has 25+ years experience designing and developing business and scientific research applications. Matthew is also the author or contributing author of over 20 how-to books on topics ranging from hard-disk compression to advanced programming in MS Office.

Matthew may be contacted by e-mail at [email protected] or through his website www.didjiman.com/business.

Page 7: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 7 of 137

Introduction MSH Library – Tools And Utilities contains a variety of code for several different purposes. The code in this library is intended to provide basic short-hand tools for a large number of programming tasks. This code was developed in response to practical needs over many years of programming database applications in MS Access VBA and other languages and environments.

The procedures and functions in these code libraries are intended to provide simple procedure calls with only one or two arguments to perform common tasks – such as determining whether a disk file or folder exists, converting measurements, manipulating strings, getting information about the computer and user, validating data, and others.

The complete source code for MSH Library – Tools And Utilities is distributed in an MS Access file named MSH Library – Tools And Utilities

v2017-04.accdb., which may be downloaded from http://www.didjiman.com/business/cv-swareVBA.htm.

This document describes the current released version (v2017-04) of the MSH Library – Tools And Utilities library.

The following sections of this document cover these topics:

Overview – an explanation of how the MSH Library – Tools And Utilities library is organized.

Library Modules – A description of the contents of each library module, listing all of the available procedures and functions, their syntax, and examples of their use.

Documentation & Source Code Licenses – This document, the source code in this document, and the source code contained in the MSH Library

– Tools And Utilities v2017-04.accdb database are released to you under one or more of the GNU licenses. Each pertinent GNU

license is included in full at the end of this document. You agree to be bound by these license agreements at such time as you utilize the VBA source code, and/or distribute this or new versions of the code and/or this document.

The next section provides an overview of the Tools And Utilities library.

Library Dependencies Some of the components in this library are interdependent – that is, some of the library modules use code from other library modules:

libDataValidation uses functions from libNumeric

libExternalDB uses functions from libFiles

If you download the MSH Library – Tools And Utilities v2017-04.accdb. from http://www.didjiman.com/business/cv-

swareVBA.htm, all dependencies and references are already inserted, and the code will compile.

NOTE This document assumes you are familiar with Visual Basic for Applications (VBA) – its data types, syntax, and built-in objects and classes.

All code in this library was written and tested in MS Access.

Page 8: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 8 of 137

Overview The Tools And Utilities library consists of a fairly large number of procedures and functions grouped by categories into standard modules. The library contains fifteen modules, one each for procedures and functions related to a particular category of purpose.

Some of the library procedures merely display messages or prompts – such procedures can save a lot of programming time by enabling you to use a single statement (a call to one of these library procedures) instead of writing the half-dozen or so statements required to do the job. Other functions validate data, format strings, build lists, and take care of other actions that a programmer is likely to perform frequently in an application.

To use the library procedures and functions, import the relevant module into your project, or cut-and-paste the code you want to use into your own modules.

The modules of the Tools And Utilities library and their contents are summarized below:

Confirmations: libConfirm – this module contains functions to obtain confirmation for deletions and global data changes.

Data Validation: libDataValidation – Quickly validate a variety of values with functions designed to validate user-entries with user-friendly error

messages.

Dates: libDates – Quickly populate combo-box and list box controls with the months of a year, the quarters of the year, compute the next quarter of

the year, and perform other date-related tasks.

External Databases: libExternalDB – These procedures enable you to easily obtain the names of forms and reports in another database, and

open forms or reports in another database. Create a new database linked to tables in the current database.

List Boxes: libListBoxes – add functionality to List Box controls with procedures to select all list items, clear all selected items, invert the current

selection, and move items up or down in the list order.

Arguments for Forms and Reports: libOpenArgs – make it easier to use the OpenArgs option for Form and Report objects by assembling an

arguments string and parsing single argument values out of that string.

Files: libFiles – Test the existence of disk files and folders, create a unique filename, extract file name, file type, and file path from a fully qualified

path. Get information about disk drives – total space and available space, enumerate current drives.

Numbers: libNumeric – Although VBA has many useful numeric functions, it lacks at least one: a function to determine whether a value is an

integer. This module fulfills that need.

Strings: libString – Make lists, format names.

System Information: libSystem – Get information from the operating system, create LNK shortcut files.

MS Access Tables: libTables – Test whether tables exist, create a unique temporary table name.

MS Access User-Interface: libUIControl – Procedures to control the appearance of the MS Access Navigation Pane.

Conversions: libUnitConversion – Convert from one unit of measurement to another – procedures to convert measurements in area, distance,

square units, cubic units, liquid measure, and weights. Functions to convert values within the same measurement system (for example, pounds to ounces) and between measurement system (for example, centimeters to inches or Fahrenheit to Celsius/Centigrade). Conversions between measurement systems are paired with their reciprocal conversion (for example, inches to centimeters and centimeters to inches).

Window Management: libWindow – Procedures to restore the original dimensions of a form window.

Sorting: Sorter – Procedures to sort the contents of list box and combo box controls; can also be used to sort single or multi-dimensional arrays.

Although the code in this library was written and developed in MS Access VBA, most of it should be usable in other MS Office applications. The code in the Tools And Utilities library should be usable in any version of MS Office 2007 or higher, and has been tested in Office 2016.

Page 9: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 9 of 137

Now that you have a sense of the Tools And Utilities library's structure and features, the next several sections of this document discuss each of the library modules and their contents in detail. In most cases, the discussion shows the syntax for using a procedure or function, includes code illustrating its use, and notes describing special issues related to using the procedure or function. A complete listing of the code in each library is in the section titled Code Listings on page 68.

Page 10: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 10 of 137

libConfirm The module named libConfirm contains functions to get user confirmation for making a global data change, and to confirm deletions.

The libConfirm module contains these procedures/functions:

Confirm_Lookup_GlobalChange – This function returns True or False depending on whether a user confirms a global data change.

DeletionConfirmed – This function returns True or False depending on whether a user confirms a deletion. Although typically used for record

deletions, may be used for any deletion action.

DeletionDOUBLEConfirmed – This function is identical to DeletionConfirmed, except that it requires the user to confirm the action twice.

NOTE: These functions are primarily designed to be called from command button events or other user interactions with controls on a form, with the expectation that record deletions are carried out by code and the user will see no other warnings about the deletion. You can also call these procedures from a Form's

OnDelete event-handler. In this case, the user will also see the MS Access built in confirmation regarding record deletion. In this case, the

DeletionConfirmed function amounts to double-confirmation, and DeletionDOUBLEConfirmed amounts to a triple confirmation. You will seriously

annoy your application's users if you ask them to confirm actions too many times, so if you use these functions in a Form's OnDelete event-handler, using

DeletionDOUBLEConfirmed is not recommended.

The next sections describe each function in more detail, including examples of their use and any special considerations about their use. You can see the

complete listing for libConfirm on page 68.

Page 11: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 11 of 137

Confirm_Lookup_GlobalChange Use the Confirm_Lookup_GlobalChange function to get a user's confirmation for a global data change. The function returns True if the user confirms

the change, False otherwise.

The declaration of Confirm_Lookup_GlobalChange is:

Public Function Confirm_Lookup_GlobalChange(varOldValue As Variant, varNewValue As Variant) As Boolean

varOldValue should contain the value being replaced. Notice that varOldValue is a Variant, so any data type may be passed in this argument.

varNewValue should contain the new value. Notice that varNewValue is also a Variant, so any data type may be passed in this argument.

This code fragment shows an example of Confirm_Lookup_GlobalChange:

strOld = "Old Value"

strNew = "New Value"

If libConfirm. Confirm_Lookup_GlobalChange(strOld, strNew) Then

'code that carries out the global replacement.

End If

In the preceding code fragment, strOld represents the value to be replaced, and strNew represents the new value. When this code executes,

Confirm_Lookup_GlobalChange uses MsgBox to display this prompt message:

Notice that the No button is the default button. Defaulting to No prevents an inattentive user from simply banging the Enter key and making a deletion they

did not truly intend.

NOTES:

The Confirm_Lookup_GlobalChange function uses CurrentProject.Name to supply the title for the MsgBox function.

See the code listing for libConfirm starting on page 68 to see the full source code for Confirm_Lookup_GlobalChange.

Page 12: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 12 of 137

DeletionConfirmed Use the DeletionConfirmed function to get a user's confirmation for a deletion action. The function returns True if the user confirms the change,

False otherwise. Although typically used for record deletions, this function may be used to confirm any type of deletion – files, list entries, and so on.

The declaration of DeletionConfirmed is:

Public Function DeletionConfirmed(strName As String) As Boolean

strName is a literal or variable string value describing what is being deleted.

This code fragment shows an example of DeletionConfirmed:

Dim strDescr As String

strDescr = "This Thing Here"

If libConfirm.DeletionConfirmed(strDescr) Then

'code to carry out the deletion

End If

In the preceding code fragment, strDescr contains the name/description of the item to be deleted: "This Thing Here". When this code executes,

DeletionConfirmed uses MsgBox to display this prompt message:

Notice that the No button is the default button. Defaulting to No prevents an inattentive user from simply banging the Enter key and making a deletion they

did not truly intend.

NOTES:

The DeletionConfirmed function uses CurrentProject.Name to supply the title for the MsgBox function.

See the code listing for libConfirm starting on page 68 to see the full source code for DeletionConfirmed.

Page 13: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 13 of 137

DeletionDOUBLEConfirmed Use the DeletionDOUBLEConfirmed function to get a user's doubly-confirmed consent to carry out a deletion action. Essentially the same as

DeletionConfirmed, DeletionDOUBLEConfirmed prompts the user to confirm the deletion twice. The function returns True if the user confirms the

change, False otherwise. Although typically used for record deletions, this function may be used to confirm any type of deletion – files, list entries, and so on.

The declaration of DeletionDOUBLEConfirmed is:

Public Function DeletionDOUBLEConfirmed(strName As String) As Boolean

strName is a literal or variable string value describing what is being deleted.

This code fragment shows an example of DeletionDOUBLEConfirmed:

Dim strDescr As String

strDescr = "This Thing"

If libConfirm.DeletionDOUBLEConfirmed(strDescr) Then

'code to carry out the deletion

End If

In the preceding code fragment, strDescr contains the name/description of the item to be deleted: "This Thing". When this code executes,

DeletionDOUBLEConfirmed uses MsgBox to display these two prompt messages (the second is only shown if the user responds Yes to the first one):

Notice that the No button is the default button. Defaulting to No prevents an inattentive user from simply banging the Enter key and making a deletion they

did not truly intend.

NOTES:

The DeletionDOUBLEConfirmed function uses CurrentProject.Name to supply the title for the MsgBox function.

See the code listing for libConfirm starting on page 68 to see the full source code for DeletionDOUBLEConfirmed.

Page 14: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 14 of 137

libDataValidation The module named libDataValidation contains code to help validate user data entries in Form controls.

The libDataValidation module contains these procedures/functions:

FieldHasAwkwardChars – Tests to find out if a string has characters that are problematic in queries or value lists.

FindFirst_NullRequiredField – Locates the first bound field in a Form whose value is required at table level, and is currently empty.

IsItemInComboBoxControl – Verifies that a string is indeed in a Combo Box control's value list.

IsItemInListBoxControl – Verifies that a string is indeed in a List Box control's value list.

Validate_ComboBoxItem – Tests to find out if the value in a Combo Box control is in the control's value list.

Validate_YearEntryBox – Tests the contents of a text field to ensure that its value is (or can be expanded to) a 4-digit year value.

Validate_YearEntryComboBox – Tests the contents of a Combo Box control to ensure that its value is (or can be expanded to) a 4-digit year

value.

The following sections describe each procedure and function of the libDataValidation module in more detail. You can see the complete listing for

libDataValidation on page 70.

FieldHasAwkwardChars Use the FieldHasAwkwardChars function to determine whether a string contains any of the characters which produce problems when using the string as

part of the WHERE clause of an SQL statement (or MS Access query), as part of a Filter string, or when used as an item in a value list.

FieldHasAwkwardChars returns True if the string does contain any of the problem characters, and False if it does not.

The characters which tend to cause problems are: the comma (,), semi-colon (;), pound sign (#), and single quote mark ('). These characters are

problematic because they are typically used as delimiters – for example, a pound sign (#) is used to delimit date literals (#11/30/2017#), so a string

containing a # often causes an error because it is interpreted as an invalidly marked date literal.

The declaration of FieldHasAwkwardChars is:

Public Function FieldHasAwkwardChars(sTest As String) As Boolean

sTest is any string to be tested.

FieldHasAwkwardChars is intended to be called to validate entries typed by a user into Text Box and Combo Box controls.

As string such as "Suite #410" will cause FieldHasAwkwardChars to return True. A string such as "Suite 410" will cause

FieldHasAwkwardChars to return False.

See the full code listing for libDataValidation starting on page 70 to see the full source code for FieldHasAwkwardChars.

Page 15: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 15 of 137

FindFirst_NullRequiredField Use the FindFirst_NullRequiredField function to locate the first Text Box, List Box, or Combo Box control on the form that is blank or null, and

whose bound field is marked Required at the table level. You can call this function in update event-handlers, as part of the code activated by a command

button, or in a Form's OnError event-handler.

FindFirst_NullRequiredField returns a string that contains the control's Tag value, and the name of the control. If the Tag property of the control is

empty, the string contains the value of the ControlSource property. If no empty or null required field is found, the returned string is empty.

The declaration of FindFirst_NullRequiredField is:

Public Function FindFirst_NullRequiredField(ByRef frm As Form) As String

The frm argument is a reference to a currently open form. Usually, FindFirst_NullRequiredField is called from within a Form, so the frm

argument would be the self-referential Me. For example:

strString = FindFirst_NullRequiredField(Me)

If not empty, the string returned by FindFirst_NullRequiredField has this format:

"<Tag Property Contents> :: <Control Name>"

"<Control Source> :: <Control Name>"

The returned string is easily divided around the "::" characters, and provides two parts of a user-friendly error message. Whatever procedure calls

FindFirst_NullRequiredField is responsible for parsing the returned string and displaying an appropriate message. Typically, you would use the

returned control name to set focus to the control whenever possible.

NOTES:

FindFirst_NullRequiredField only evaluates Text Box, List Box, and Combo Box controls.

FindFirst_NullRequiredField can only detect required fields if the bound field of the control is set as Required at the table level.

Unbound controls are not evaluated.

See the code listing for libDataValidation starting on page 70 to see the full source code for FindFirst_NullRequiredField.

Page 16: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 16 of 137

IsItemInComboBoxControl Use the IsItemInComboBoxControl function to verify that an item is in a Combo Box control's list. IsItemInComboBoxControl returns True if

the item being tested is in the list, False otherwise.

The declaration of IsItemInComboBoxControl is:

Public Function IsItemInComboBoxControl(ByRef lst As ComboBox, lstCol As Integer, sItem As String) As Boolean

The lst argument is a reference to a ComboBox control on an open Form. lstCol specifies the column of the list to be tested, and sItem is a literal or

variable string value to be tested against the contents of the ComboBox control's list.

IsItemInComboBoxControl is designed to be called from within a form. It is intended to be used with bound or unbound ComboBox lists.

IsItemInComboBoxControl was originally developed to enable the addition of list items with user confirmation, specifically when the list represents a

lookup list from a bound table. If value selection in a Combo Box is set to be limited to the list, users can never enter new values for the lookup list. If value selection in the Combo Box is not limited to list, the programmer loses control over what values may be selected.

The IsItemInComboBoxControl function helps a programmer write code that enables users to dynamically add new values to a lookup table. By calling

IsItemInComboBoxControl in the BeforeUpdate event of a Combo Box, the programmer can verify whether or not the new value exists in the

Combo Box control's list. If the value is not in the list, the programmer can prompt the user to add the new value to the control's value list, or to a bound lookup

table. If the user refuses to add the new entry, the programmer can revert the ComboBox to its previous value.

NOTES:

Remember that ComboBox data lists and columns use 0-based numbering.

See the code listing for libDataValidation starting on page 70 to see the full source code for IsItemInComboBoxControl.

IsItemInListBoxControl Use the IsItemInListBoxControl function to verify that an item is in a List Box control's list. IsItemInListBoxControl returns True if the item

being tested is in the list, False otherwise.

The declaration of IsItemInListBoxControl is:

Public Function IsItemInListBoxControl(ByRef lst As ListBox, lstCol As Integer, sItem As String) As Boolean

The lst argument is a reference to a ListBox control on an open Form. lstCol specifies the column of the list to be tested, and sItem is a literal or

variable string value to be tested against the contents of the ListBox control's list.

IsItemInListBoxControl is designed to be called from within a form. It is intended to be used with bound or unbound ListBox lists.

Like IsItemInComboBoxControl, this function is intended to help a programmer confirm the addition of new values to a List Box control's bound lookup

table or value list. Refer to the preceding description of IsItemInComboBoxControl for more information.

NOTES:

Remember that ListBox data lists and columns use 0-based numbering.

See the code listing for libDataValidation starting on page 70 to see the full source code for IsItemInListBoxControl.

Page 17: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 17 of 137

Validate_ComboBoxItem The Validate_ComboBoxItem function provides a dynamic way of adding entries to the RowSource table of a ComboBox control. The function

validates data in a ComboBox control on a form, and (with user confirmation) adds a new item to the ComboBox's RowSource table. The ComboBox

control is presumed to have a table as its RowSource, and not be limited to list.

The declaration of Validate_ComboBoxItem is:

Public Function Validate_ComboBoxItem(ByRef LstCtl As ComboBox, lstCol As Integer, _

sItem As String, sUpdateField As String) As Boolean

The LstCtl argument is a reference to a ComboBox control on an open Form. lstCol specifies the column of the list to be tested, and sItem is a literal

or variable string value to be tested against the contents of the ComboBox control's list. sUpdateField is the name of the field in the control's bound table

that provides the ComboBox list's RowSource.

Validate_ComboBoxItem searches the data list associated with LstCtl for sItem. If sItem is not found in the list, the user is asked to confirm

adding the item to the list. Returns False if the user refuses the addition. If the user accepts the addition, the ControlSource property is used to add the

entry to the appropriate lookup table.

NOTES:

Remember that ComboBox data lists and columns use 0-based numbering.

sUpdateField is the name of the field in the RowSource table to update (there's no other way to know the name of this field).

See the code listing for libDataValidation starting on page 70 to see the full source code for Validate_ComboBoxItem.

Validate_YearEntryBox Use the Validate_YearEntryBox function to verify that a user's entry into a Text Box is, or can be converted to, a 4-digit year value.

The declaration of Validate_YearEntryBox is:

Public Function Validate_YearEntryBox(ctl As TextBox) As Boolean

The ctl argument is a reference to a Text Box control.

Validate_YearEntryBox is intended to be called from an open form, typically from the BeforeUpdate event of the Text Box control.

When testing to see if the content of the Text Box is, or can be converted to, a 4-digit year value, entries of less than 4 digits are padded out to match the

current century and decade, then validated. For example, if the current year is 2017, and the user enters "02", the Text Box's value is converted to "2002"

and then tested. If the user enters "2", then the value is converted to "2012" and then tested.

Validate_YearEntryBox returns False if the value being tested is blank, is not an integer, or exceeds 4 characters in length.

Validate_YearEntryBox uses MsgBox to display appropriate error messages for blank entries, non-integer entries, or entries that are too long.

See the code listing for libDataValidation starting on page 70 to see the full source code for Validate_YearEntryBox.

Page 18: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 18 of 137

Validate_YearEntryComboBox Use the Validate_YearEntryComboBox function to verify that a user's entry into a Combo Box is, or can be converted to, a 4-digit year value.

The declaration of Validate_YearEntryComboBox is:

Public Function Validate_YearEntryComboBox(ctl As ComboBox) As Boolean

The ctl argument is a reference to a Combo Box control.

Validate_YearEntryComboBox is intended to be called from an open form, typically from the BeforeUpdate event of the Combo Box control.

When testing to see if the content of the Combo Box is, or can be converted to, a 4-digit year value, entries of less than 4 digits are padded out to match the

current century and decade, then validated. For example, if the current year is 2017, and the user enters "02", the Combo Box's value is converted to

"2002" and then tested. If the user enters "2", then the value is converted to "2012" and then tested.

Validate_YearEntryComboBox returns False if the value being tested is blank, is not an integer, or exceeds 4 characters in length.

Validate_YearEntryComboBox uses MsgBox to display appropriate error messages for blank entries, non-integer entries, or entries that are too long.

See the code listing for libDataValidation starting on page 70 to see the full source code for Validate_YearEntryComboBox.

Page 19: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 19 of 137

libDates The module named libDates contains code for carrying out tasks associated with Date/Time values.

The libDates module contains these procedures/functions:

IsFutureDate – tests whether or not a date value is in the future.

msg_NoFutureDates – displays a message informing the user that dates in the future are not allowed.

IsPastDate – tests whether or not a date value is in the past.

msg_NoPastDates – displays a message informing the user that dates in the past are not allowed.

ListFill_YrMonths – populates a list or combo box control with the months of the year.

ListFill_YrQuarters – populates a list or combo box with a list of quarters in the year.

NextQuarter – returns the next quarter of the year based on a specified date.

The following sections describe each procedure and function of the libDates module in more detail. You can see the complete listing for libDates on

page 70.

IsFutureDate The IsFutureDate function tests a date value to see if it is later than today. IsFutureDate returns True if the date is later than today, False

otherwise.

The declaration of IsFutureDate is:

Public Function IsFutureDate(dtDate As Date) As Boolean

In many applications, particularly applications that log events or tasks, you will want to validate dates entered by the user for accuracy. The IsFutureDate

function makes it easier to validate dates, especially if you want to ensure that a user does not mistakenly enter a future date for an event or task that can only

have occurred on today's date or earlier. The IsFutureDate function provides a readable substitute for writing a date-comparison statement repetitively.

See the code listing for libDates starting on page 70 to see the full source code for IsFutureDate.

msg_NoFutureDates

The msg_NoFutureDates procedure merely displays a message stating that days later than today are not allowed. This procedure is intended to be used

in conjunction with the IsFutureDate function to help validate dates entered by a user.

See the code listing for libDates starting on page 70 to see the full source code for msg_NoFutureDates.

Page 20: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 20 of 137

IsPastDate The IsPastDate function tests a date value to see if it is earlier than today. IsPastDate returns True if the date is earlier than today, False otherwise.

The declaration of IsPastDate is:

Public Function IsPastDate(dtDate As Date) As Boolean

In many applications, particularly applications that schedule events or tasks, you will want to validate dates entered by the user for accuracy. The

IsPastDate function makes it easier to validate dates, especially if you want to ensure that a user does not mistakenly enter a past date for an event or task

that can only occur on today's date or later. The IsPastDate function provides a readable substitute for writing a date-comparison statement repetitively.

See the code listing for libDates starting on page 70 to see the full source code for IsPastDate.

msg_NoPastDates

The msg_NoPastDates procedure merely displays a message stating that days earlier than today are not allowed. This procedure is intended to be used in

conjunction with the IsPastDate function.

See the code listing for libDates starting on page 70 to see the full source code for msg_NoPastDates.

ListFill_YrMonths The ListFill_YrMonths procedure fills a List Box or Combo Box control with a two-column list of months and their ordinal values. This procedure is

intended to be called whenever a list to select a month of the year needs to be populated.

The declaration for ListFill_YrMonths is:

Public Sub ListFill_YrMonths(ByRef objList As Object)

The objList argument is a reference to a List Box or Combo Box control in an open Form.

When ListFill_YrMonths populates the list, it sets the default value of the list to the current month, based on today's date.

NOTES:

The list control, whether a List Box or a Combo Box, must be configured to have two columns.

The first column of the List Box or Combo Box should be the bound column – this will cause the list's selection value to be the ordinal number of the month (1 through 12).

To hide the ordinal value of the month, and show only the month name, set the width of the first column to 0.

If the objList argument is not a List Box or Combo Box, a runtime error is raised.

If the control's RowSourceType is not "Value List", a runtime error is raised.

See the code listing for libDates starting on page 70 to see the full source code for ListFill_YrMonths.

Page 21: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 21 of 137

ListFill_YrQuarters The ListFill_YrQuarters procedure fills a List Box or Combo Box control with an ordered list of year quarters: 1

st, 2

nd, 3

rd, 4

th and their ordinal values (1,

2, 3, 4).

The declaration for ListFill_YrQuarters is:

Public Sub ListFill_YrQuarters(ByRef objList As Object)

The objList argument is a reference to either a List Box or Combo Box control on an open Form.

When ListFill_YrQuarters populates the list, it sets the default value of the list to the current quarter of the year, based on today's date.

NOTES:

The list control, whether a List Box or a Combo Box, must be configured to have two columns.

The first column of the List Box or Combo Box should be the bound column – this will cause the list's selection value to be the ordinal number of the quarter (1 through 4).

To hide the ordinal value of the quarter, and show only the quarter's name, set the width of the first column to 0.

If the objList argument is not a List Box or Combo Box, a runtime error is raised.

If the control's RowSourceType is not "Value List", a runtime error is raised.

See the code listing for libDates starting on page 70 to see the full source code for ListFill_YrQuarters.

NextQuarter

The NextQuarter function returns a string indicating the quarter of the year following the quarter of a specific date value.

The declaration for NextQuarter is:

Public Function NextQuarter(dtDate As Date) As String

NextQuarter returns the next annual quarter after the date supplied in the dtDate argument. The returned string is in the format "4Q17" for "4th

Quarter, 2017". For example, if the dtDate argument is 11/15/2017, then NextQuarter will return "1Q18" – the 1st Quarter of 2018 is the next

quarter of the year after 11/15/2017.

See the code listing for libDates starting on page 70 to see the full source code for NextQuarter.

Page 22: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 22 of 137

libExternalDB The libExternalDB module contains code for carrying out some basic operations with an MS Access database external to the database in which these

procedures and functions are executing.

The libExternalDB module contains these procedures/functions:

Create_UserDefined_DB – Creates a new MS Access database, and links all of the tables from the current database to the new database.

Form_Get_List – Gets a list of all forms in another database.

Form_Open – Opens a specific form in another database.

Report_Get_List – Gets a list of all reports in another database.

Report_Open – Opens a specific report in another database.

The following sections describe each procedure and function of the libExternalDB module in more detail. You can see the complete listing for

libExternalDB on page 78.

Page 23: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 23 of 137

Create_UserDefined_DB The Create_UserDefined_DB procedure creates a new MS Access database file, and then links all tables from the current database to the new

database. Create_UserDefined_DB links all data tables to the new database, including any tables linked to the current database, and will link any hidden

tables from the current database to the new database. MS Access system tables (whose names are prefixed with "Msys") are not linked. Temporary tables

(whose name is prefixed with "~") are not linked.

The primary purpose of Create_UserDefined_DB is to create a database with linked tables from the current database so that users may create their own

queries, reports, or forms to view the data in your application without having to alter your application's components, and without having to worry about keeping data synchronized between the primary database and a copy of the database.

The declaration of Create_UserDefined_DB is:

Public Sub Create_UserDefined_DB(sDBName As String)

sDBName is the name of the new database file. sDBName should not include a path. The new database file will be stored in the same folder as the current

project. For example, if the current project is running from a folder named C:\Users\User\Documents\Financials, then the new database will be

created in that same folder.

sDBName may omit the file extension, since the database creation process in MS Access automatically supplies the .accdb file extension. Any file extension

you supply will be used instead of the .accdb extension, although the new database file is always created in MS Access .accdb format. If you supply an

extension other than .accdb, you may not be able to open the new database file.

Create_UserDefined_DB uses MsgBox to display messages to the user – notification of successfully creating the new database file and its linked tables,

or an error message if something went wrong.

NOTES:

Do not include a full path in the sDBName argument, Create_UserDefined_DB defines the path.

The sDBName argument should contain only a file name and extension.

If you include a file extension other than .accdb, you may not be able to open the new database file.

It is strongly recommended that you use the .accdb extension, or no extension at all (because MS Access will automatically add the .accdb

extension).

If the database you are copying contains linked tables, and any one of those linked tables has an invalid link, a message is displayed asking whether to skip the table with the invalid link, or cancel the entire operation.

If the operation is canceled by user choice or because a runtime error has occurred, the database copy is deleted before the procedure ends.

See the code listing for libExternalDB starting on page 78 to see the full source code for Create_UserDefined_DB.

Page 24: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 24 of 137

Form_Get_List The Form_Get_List function returns an array of strings containing the names of all Forms in another .accdb file. If no Forms are found, an empty string

is returned.

The declaration of Form_Get_List is:

Public Function Form_Get_List(sDBName As String) As Variant

sDBName is a literal or variable string value containing the name and fully qualified path of the .accdb file from which to extract the list of Form names.

Notice that Form_Get_List returns a Variant data type so that it may return a Variant data type containing an array of strings.

This code fragment shows an example of Form_Get_List:

Dim frmList As Variant

Dim k As Long

Dim s As String

s = "C:\Users\User\Documents\test\Project Time Log.accdb"

frmList = libExternalDB.Form_Get_List(s)

If TypeName(frmList) <> "String()" Then

'not an array returned

Debug.Print "NO FORMS FOUND"

Else

Debug.Print "FORMS FOUND: "

For k = LBound(frmList) To UBound(frmList)

Debug.Print frmList(k)

Next

End If

In the preceding code fragment, the frmList variable (a Variant) is assigned the return result of Form_Get_List. If any Forms were found in the

database, frmList contains an array of strings, otherwise it contains an empty string. The code uses the VBA TypeName function to determine what has

been returned in frmList. If Forms were found, a For…Next loop is used to print all the form names in the VBA Debugger's Immediate window.

NOTES:

Form_Get_List uses MsgBox to display an error message if the database specified by the sDBName argument is not found.

See the code listing for libExternalDB starting on page 78 to see the full source code for Form_Get_List.

Page 25: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 25 of 137

Form_Open

The Form_Open procedure opens a specified Form in an external database. Use Form_Open to open a database other than the current database, and to

open the selected Form.

The declaration of Form_Open is:

Public Sub Form_Open(sDBName As String, sFormName As String)

sDBName is a literal or variable string value specifying the database to open. sDBName must include the database name, file extension, and a fully qualified

path. sFormName is the name of the form to be opened.

This code fragment shows an example of Form_Open:

Dim strDBName As String

Dim strFormName As String

strDBName = "C:\Users\User\Documents\test\Project Time Log.accdb"

strFormName = "frm_ProjectDataEntry"

libExternalDB.Form_Open strDBName, strFormName

In the preceding code fragment, strDBName is initialized with a database file name and a fully qualified path for the database containing the Form to open.

strFormName is initialized with the name of the Form to be opened. When this code executes, the Form_Open procedure creates a new instance of MS

Access to open the database specified by the strDBName variable, and then executes the DoCmd.OpenForm method to open the Form specified by the

strFormName variable. The new MS Access instance is made visible, and its window is maximized and left open for the user to interact with the newly

opened Form.

See the code listing for libExternalDB starting on page 78 to see the full source code for Form_Open.

Page 26: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 26 of 137

Report_Get_List The Report_Get_List function returns an array of strings containing the names of all Reports in another .accdb file. If no Reports are found, an empty

string is returned.

The declaration of Report_Get_List is:

Public Function Report_Get_List(sDBName As String) As Variant

sDBName is a literal or variable string value containing the name and fully qualified path of the .accdb file from which to extract the list of Report names.

Notice that Report_Get_List returns a Variant data type so that it may return a Variant data type containing an array of strings.

This code fragment shows an example of Report_Get_List:

Dim rptList As Variant

Dim k As Long

Dim s As String

s = "C:\Users\User\Documents\test\Project Time Log.accdb"

rptList = libExternalDB.Report_Get_List(s)

If TypeName(rptList) <> "String()" Then

'not an array returned

Debug.Print "NO REPORTS FOUND"

Else

Debug.Print "REPORTS FOUND: "

For k = LBound(rptList) To UBound(rptList)

Debug.Print rptList(k)

Next

End If

In the preceding code fragment, the rptList variable (a Variant) is assigned the return result of Report_Get_List. If any Reports were found in the

database, rptList contains an array of strings, otherwise it contains an empty string. The code uses the VBA TypeName function to determine what has

been returned in rptList. If Reports were found, a For…Next loop is used to print all the report names in the VBA Debugger's Immediate window.

NOTES:

Report_Get_List uses MsgBox to display an error message if the database specified by the sDBName argument is not found.

See the code listing for libExternalDB starting on page 78 to see the full source code for Report_Get_List.

Page 27: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 27 of 137

Report_Open

The Report_Open procedure opens a specified Report in an external database. Use Report_Open to open a database other than the current database,

and to open the selected Report.

The declaration of Report_Open is:

Public Sub Report_Open(sDBName As String, sReportName As String)

sDBName is a literal or variable string value specifying the database to open. sDBName must include the database name, file extension, and a fully qualified

path. sReportName is the name of the Report to be opened.

This code fragment shows an example of Report_Open:

Dim strDBName As String

Dim strReportName As String

strDBName = "C:\Users\User\Documents\test\Project Time Log.accdb"

strReportName = "rpt_ProjectTime"

libExternalDB.Report_Open strDBName, strReportName

In the preceding code fragment, strDBName is initialized with a database file name and a fully qualified path for the database containing the Report to open.

strReportName is initialized with the name of the Report to be opened. When this code executes, the Report_Open procedure creates a new instance of

MS Access to open the database specified by the strDBName variable, and then executes the DoCmd.OpenReport method to open the Report specified

by the strReportName variable. The new MS Access instance is made visible, and its window is maximized and left open for the user to interact with the

newly opened Report.

See the code listing for libExternalDB starting on page 78 to see the full source code for Report_Open.

Page 28: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 28 of 137

libListBoxes The libListBoxes module contains code to enhance the functionality of List Box controls based on a Value List and, in particular, multi-select List Box

controls. The procedures in libListBoxes enable you to select all list items, clear selections, invert selections, and to move a selection up or down.

The libListBoxes module contains these procedures/functions:

ListBox_Selection_Clear – Clear selected items in a List Box.

ListBox_Selection_SelectAll – Select all of the items in a multi-select List Box.

ListBox_Selection_Invert – Invert the current list selection.

ListBox_MoveItemDown – Move a selected item down one place in the list.

ListBox_MoveItemUp – Move a selected item up one place in the list.

The MSH Library – Tools And Utilities v2017-04.accdb. (downloadable from http://www.didjiman.com/business/cv-swareVBA.htm)

contains a form named frm_test_libListBox which exercises the functions in this module.

The following sections describe each procedure and function of the libListBoxes module in more detail. You can see the complete listing for

libListBoxes on page 82.

ListBox_Selection_Clear

The ListBox_Selection_Clear procedure clears the selection from a List Box. Whether or not the List Box is multi-select, any selected item is marked

as unselected. You will usually call this procedure by putting it in the event handler of a button labeled "Clear List", "Clear All" or something similar.

The declaration of ListBox_Selection_Clear is:

Public Sub ListBox_Selection_Clear(ByRef xListBox As ListBox)

The xListBox argument is a reference to any ListBox control. However many items are selected (one or more than one if the list is multi-select) all items

are de-selected.

See the code listing for libListBoxes starting on page 82 to see the full source code for ListBox_Selection_Clear.

ListBox_Selection_SelectAll

The ListBox_Selection_SelectAll procedure selects all of the items in a multi-select List Box. You will usually call this procedure by putting it in the

event handler of a button labeled "Select All."

The declaration of ListBox_Selection_SelectAll is:

Public Sub ListBox_Selection_SelectAll(ByRef xListBox As ListBox)

The xListBox argument is a reference to any ListBox control. Selects all of the items in a multi-select List Box.

NOTES:

This procedure is not useful unless the List Box is multi-select.

See the code listing for libListBoxes starting on page 82 to see the full source code for ListBox_Selection_SelectAll.

Page 29: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 29 of 137

ListBox_Selection_Invert

The ListBox_Selection_Invert procedure inverts the selection of items in a multi-select List Box. You will usually call this procedure by putting it in the

event handler of a button labeled "Invert Selection."

The declaration of ListBox_Selection_Invert is:

Public Sub ListBox_Selection_Invert(ByRef xListBox As ListBox)

The xListBox argument is a reference to any ListBox control. When this procedure executes, all unselected items in the list are marked as selected, and

all selected items are marked as unselected.

NOTES:

This procedure is not useful unless the List Box is multi-select.

See the code listing for libListBoxes starting on page 82 to see the full source code for ListBox_Selection_Invert.

ListBox_MoveItemDown

The ListBox_MoveItemDown procedure moves one selected item down in the list. You'll usually call this procedure by putting it in the event handler of a

button labeled "Move Down."

The declaration of ListBox_MoveItemDown is:

Public Sub ListBox_MoveItemDown(ByRef xListBox As ListBox)

The xListBox argument is a reference to any ListBox control. Provided the selected item is not already at the end of the list, this procedure moves the

selected item down one row in the list.

NOTES:

xListBox must be a value list, or a runtime error is raised.

ListBox_MoveItemDown works whether or not a list is multi-select.

If the List Box is multi-select, and more than one item is selected when ListBox_MoveItemDown is called, the user is shown an error message and

no action is carried out.

Remember that, if a List Box is multi-select, you must use the ItemsSelected collection to determine which items in the list are selected.

See the code listing for libListBoxes starting on page 82 to see the full source code for ListBox_MoveItemDown.

Page 30: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 30 of 137

ListBox_MoveItemUp

The ListBox_MoveItemUp procedure moves one selected item up in the list. You'll usually call this procedure by putting it in the event handler of a button

labeled "Move Up."

The declaration of ListBox_MoveItemUp is:

Public Sub ListBox_MoveItemUp(ByRef xListBox As ListBox)

The xListBox argument is a reference to any ListBox control. Provided the selected item is not already at the beginning of the list, this procedure moves

the selected item up one row in the list.

NOTES:

xListBox must be a value list, or a runtime error is raised.

ListBox_MoveItemUp works whether or not a list is multi-select.

If the List Box is multi-select, and more than one item is selected when ListBox_MoveItemUp is called, the user is shown an error message and

no action is carried out.

Remember that, if a List Box is multi-select, you must use the ItemsSelected collection to determine which items in the list are selected.

See the code listing for libListBoxes starting on page 82 to see the full source code for ListBox_MoveItemUp.

msg_OnlyOneSelectedItemCanBeMoved

The msg_OnlyOneSelectedItemCanBeMoved procedure merely displays a message to the user, saying that only one selected item at a time can be

moved and suggesting the user select a single item and try again. This procedure is declared Private to the module because it is intended only to be called by

other procedures in this module. – ListBox_MoveItemUp and ListBox_MoveItemDown.

The declaration of msg_OnlyOneSelectedItemCanBeMoved is:

Private Sub msg_OnlyOneSelectedItemCanBeMoved()

As you can see, this procedure has no arguments.

See the code listing for libListBoxes starting on page 82 to see the full source code for msg_OnlyOneSelectedItemCanBeMoved.

Page 31: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 31 of 137

libOpenArgs The libOpenArgs module contains a pair of functions intended to help when using the OpenArgs argument of the DoCmd.OpenForm and

DoCmd.OpenReport methods of MS Access.

The libOpenArgs module contains these procedures/functions:

OpenArgs_Build – assembles a single string to be passed as the OpenArgs argument value.

OpenArgs_GetArgument – extracts the value of a single, specific argument from a string passed into a Form or Report by the OpenArgs

argument.

The following sections describe each function of the libOpenArgs module in more detail. You can see the complete listing for libOpenArgs on page 85.

Page 32: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 32 of 137

OpenArgs_Build

The OpenArgs_Build function returns a single string assembled from a list of argument names and values provided in a ParamArray argument to the

function.

The declaration of OpenArgs_Build is:

Public Function OpenArgs_Build(ParamArray sArgList()) As String

sArgList is a ParamArray argument. You specify the arguments to be included in the returned string in pairs: the argument name, and the argument

value. You may specify as many argument names and values as you wish, using literal or variable values.

Each pair of arguments is assembled in the format <argname>:=<argvalue>[vbCr].

The following code fragment shows an example of calling the OpenArgs_Build function:

Dim s As String

Dim sArgName1 As String

Dim sArgValue1 As String

sArgName1 = "TestArg"

sArgValue1 = "Test Argument"

s = libOpenArgs.OpenArgs_Build("Arg1", 1, " Arg2", "Value2", "MyArg", 3.2, sArgName1, sArgValue1)

The final line above shows a call to OpenArgs_Build that uses both literal and variable values in the list of arguments to assemble.

The string returned by OpenArgs_Build in the preceding code fragment is:

Arg1:=1

Arg2:=Value2

MyArg:=3.2

TestArg:=Test Argument

Because each argument name/argument value pair ends with the vbCR character, each argument appears on a separate line when printed with

Debug.Print.

NOTES:

You must list the arguments in pairs, with the argument name first, followed by its value.

If you omit the sArgList argument, OpenArgs_Build raises a runtime error.

If the sArgList argument does not contain an even number of items in the list (that is, does not contain matched argument name/argument value

pairs), OpenArgs_Build raises a runtime error.

See the code listing for libOpenArgs starting on page 85 to see the full source code for OpenArgs_Build.

Page 33: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 33 of 137

OpenArgs_GetArgument

The OpenArgs_GetArgument function returns the value of a single specified argument from a string formatted following the rules used in the

OpenArgs_Build function (although the argument string does not have to be created by OpenArgs_Build). The value for the specified argument name

is parsed from the string and returned as a string.

The declaration of OpenArgs_GetArgument is:

Public Function OpenArgs_GetArgument(sOpenArgs As String, ByVal sArgName As String) As String

sOpenArgs is a string containing argument name/argument value pairs formatted as <argname>:=<argvalue>[vbCr]. sArgName is a literal or

variable value containing the name of the argument value you want to retrieve.

The following code fragment shows an example of calling the OpenArgs_GetArgument function:

Debug.Print libOpenArgs.OpenArgs_GetArgument(s, "Arg1")

Debug.Print libOpenArgs.OpenArgs_GetArgument(s, "Arg2")

In the two lines above, if s contains the argument string built by the code shown in the preceding section on the OpenArgs_Build function, then the first line

above displays 1 in the Immediate window, and the second line above displays Value2 in the Immediate window.

NOTES:

The OpenArgs_GetArgument function is intended to be called from inside the Form or Report which receives an OpenArgs string (typically in the

OnLoad event).

OpenArgs_GetArgument returns only the specified argument's value.

All values returned by OpenArgs_GetArgument are strings; the calling code must convert strings to numeric values as needed.

If the specified argument name does not exist in the sOpenArgs string, then OpenArgs_GetArgument returns an empty string. It is up to the

calling code to test whether the return string is empty.

See the code listing for libOpenArgs starting on page 85 to see the full source code for OpenArgs_GetArgument.

Page 34: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 34 of 137

libFiles The libFiles module contains functions to determine whether files or folders exist, extract the various parts of a fully qualified path, to create unique file

names, and to get informaton about the disk drives attached to the computer.

The libFiles module contains these procedures/functions:

FileExists – Tests whether or not a specified file exists.

FolderExists – Tests whether or not a specified folder exists.

FileName_FromFullPath – Extracts the file name from a fully qualified path.

PathName_FromFullPath – Extracts the path name from a fully qualified path.

FileExtension_FromFullPath – Extracts the file extension (such as .txt, .accdb, and so on) from a fully qualified path or a file name.

GetFileSize – Returns the size of a specified file in bytes.

Get_FileDateCreated – returns the date/time that a file was created.

Get_FileDateLastModified – returns the date/time that a file was last modified.

Get_FileDateLastAccessed – returns the date/time that a file was last accessed (as shown in the file's Properties window).

Make_Unique_FileName – Creates a unique file name by adding numeric suffixes to a file name, such as File 01.txt, File 02.txt, and

so on.

DriveExists – Tests whether or not a specified disk drive exists.

Get_DriveList – Returns a string array containing the drive letters for all drives currently connected to the computer.

DriveFreeSpace – Returns, in bytes, the amount of free space on a specified disk drive.

DriveAvailableSpace – Returns, in bytes, the amount of space available to the current user on a specified disk drive. This value may differ from

the free space on a drive if the computer has been configured to implement drive-space quotas for users.

DriveTotalSpace – Returns, in bytes, the total amount of space on a specified disk drive.

Bytes2KB, Bytes2MB, Bytes2GB, and Bytes2String – these functions are used to scale a value in bytes to a value in Kilobytes (KB),

Megabytes (MB), or Gigabytes (GB).

The following sections describe each procedure and function of the libFiles module in more detail. You can see the complete listing for libFiles on

page 87.

Page 35: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 35 of 137

FileExists Use the FileExists function to determine whether a particular file exists. FileExists returns True if the specified file is found, False otherwise.

The declaration for FileExists is:

Public Function FileExists(sFileName As String) As Boolean

sFileName is a literal or variable string value specifying the file to be searched for. sFileName must include the file name, its extension, and a fully

qualified path.

NOTES:

FileExists finds only "Normal" files – it will not find system or hidden files.

See the code listing for libFiles starting on page 87 to see the full source code for FileExists.

FolderExists

Use the FolderExists function to determine whether a particular folder exists. FolderExists returns True if the specified folder is found, False

otherwise.

The declaration for FolderExists is:

Public Function FolderExists(sFolderName As String) As Boolean

sFolderName is a literal or variable string value containing a fully qualified path, without a file name.

NOTES:

FolderExists finds only normal folders (directories) – it will not find system or hidden folders.

See the code listing for libFiles starting on page 87 to see the full source code for FolderExists.

FileName_FromFullPath The FileName_FromFullPath function extracts and returns the file name portion of a fully qualified path.

The declaration for FileName_FromFullPath is:

Public Function FileName_FromFullPath(sFullPath As String) As String

sFullPath is a literal or variable string value containing a fully qualified path and file name.

As an example, given the fully qualified path:

C:\Users\User\Documents\test\somefilename.docx

the FileName_FromFullPath function returns the string:

somefilename.docx

See the code listing for libFiles starting on page 87 to see the full source code for FileName_FromFullPath.

Page 36: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 36 of 137

PathName_FromFullPath

The PathName_FromFullPath function extracts and returns the path portion of a fully qualified path.

The declaration for PathName_FromFullPath is:

Public Function PathName_FromFullPath(sFullPath As String) As String

As an example, given the fully qualified path:

C:\Users\User\Documents\test\somefilename.docx

The PathName_FromFullPath function returns the string:

C:\Users\User\Documents\test\

See the code listing for libFiles starting on page 87 to see the full source code for PathName_FromFullPath.

FileExtension_FromFullPath

The FileExtension_FromFullPath function extracts and returns the file extension portion of a fully qualified path or file name.

The declaration of FileExtension_FromFullPath is:

Public Function FileExtension_FromFullPath(sFullPath As String) As String

As an example, given the fully qualified path:

C:\Users\User\Documents\test\somefilename.docx

The FileExtension_FromFullPath function returns the string:

.docx

As another example, given this string:

somefilename.docx

The FileExtension_FromFullPath function returns the same file extension: .docx

NOTES:

FileExtension_FromFullPath returns any and all characters after the final "." in a file path/file name.

If the supplied path does not contain a "." character, FileExtension_FromFullPath returns an empty string.

See the code listing for libFiles starting on page 87 to see the full source code for FileExtension_FromFullPath.

Page 37: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 37 of 137

GetFileSize

The GetFileSize function returns the size of a file in bytes.

The declaration for GetFileSize is:

Public Function GetFileSize(sFileName As String) As Double

The sFileName argument is a variable or literal string containing a fully qualified path and file name with extension.

If the specified file does not exist, or a runtime error occurs when executing the function, then GetFileSize returns -1 as a result.

See the code listing for libFiles starting on page 87 to see the full source code for GetFileSize.

Get_FileDateCreated

The Get_FileDateCreated function returns the date and time that a specified file was created.

The declaration for Get_FileDateCreated is:

Public Function Get_FileDateCreated(sFileName As String) As Date

The sFileName argument is a variable or literal string containing a fully qualified path and file name with extension.

If the specified file does not exist, or a runtime error occurs when executing the function, then Get_FileDateCreated returns -1 as a result.

See the code listing for libFiles starting on page 87 to see the full source code for Get_FileDateCreated.

Get_FileDateLastModified

The Get_FileDateLastModified function returns the date and time that a specified file was last modified.

The declaration for Get_FileDateLastModified is:

Public Function Get_FileDateLastModified(sFileName As String) As Date

The sFileName argument is a variable or literal string containing a fully qualified path and file name with extension.

If the specified file does not exist, or a runtime error occurs when executing the function, then Get_FileDateLastModified returns -1 as a result.

See the code listing for libFiles starting on page 87 to see the full source code for Get_FileDateLastModified.

Page 38: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 38 of 137

Get_FileDateLastAccessed

The Get_FileDateLastAccessed function returns the date and time that a specified file was last accessed.

The declaration for Get_FileDateLastAccessed is:

Public Function Get_FileDateLastAccessed(sFileName As String) As Date

The sFileName argument is a variable or literal string containing a fully qualified path and file name with extension.

If the specified file does not exist, or a runtime error occurs when executing the function, then Get_FileDateLastModified returns -1 as a result.

See the code listing for libFiles starting on page 87 to see the full source code for Get_FileDateLastAccessed.

Make_Unique_FileName The Make_Unique_FileName function creates a unique file name based on a fully qualified path that you supply. Make_Unique_FileName adds a

numeric suffix to a file name (01, 02, and so on) until the file name no longer duplicates an existing name in the same folder.

Make_Unique_FileName is very useful if your application writes files of any kind, such as mail-merge data files, temp files, and so on. Use

Make_Unique_FileName to create a unique file name and avoid over-writing any previously written files.

The declaration for FileExtension_FromFullPath is:

Public Function Make_Unique_FileName(ByVal sFileName As String) As String

sFileName is a literal or variable string value containing a file name, file extension, and a fully qualified path. Notice that sFileName is passed by value

(ByVal) because Make_Unique_FileName modifies the argument while working.

The following code fragment shows an example of Make_Unique_FileName:

Dim strName As String

strName = "C:\Users\User\Documents\test\Some File.txt"

Debug.Print Make_Unique_FileName(strName)

In this code fragment, a string variable (strName) is initialized with a fully qualified path for a file named Some File.txt. If a file of the same name

already exists in the specified path, Make_Unique_FileName returns Some File 01.txt. If Some File 01.txt exists, then

Make_Unique_FileName returns Some File 02.txt, and so on.

NOTES:

The numeric suffix starts at 01.

See the code listing for libFiles starting on page 87 to see the full source code for Make_Unique_FileName.

Page 39: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 39 of 137

DriveExists

The DriveExists function returns True if a specified drive exists on the computer, and False if it does not.

The declaration for DriveExists is:

Public Function DriveExists(ByVal DrvLetter As String) As Boolean

The DrvLetter argument is the letter of the drive to be tested for existence. If the DrvLetter argument is more than one character in length, only the first

character is used as the drive letter. For example, "c" and "cox" will both test for the existence of drive C:.

See the code listing for libFiles starting on page 87 to see the full source code for DriveExists.

Get_DriveList

The Get_DriveList function returns an array of strings; each element in the array contains a drive letter suffixed with a colon (C:, D:, E:, and so on).

The array contains one element for each drive currently attached to the computer.

The declaration for Get_DriveList is:

Public Function Get_DriveList() As String()

There is presumably at least one drive attached to the computer, so there is no need to worry about Get_DriveList returning an empty array. The array

returned by Get_DriveList is 0 based. If there are 4 drives attached to the computer, the array will have subscripts numbered 0 through 3.

Use a looping structure similar to that shown in the following code snippet to iterate the array returned by Get_DriveList:

DrvList = libFiles.Get_DriveList

For k = LBound(DrvList) To UBound(DrvList)

Debug.Print DrvList(k) 'code that uses the drive letters (for example to populate a list)

Next

Page 40: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 40 of 137

DriveFreeSpace The DriveFreeSpace function returns the amount of free space on a disk drive, in bytes. If the specified drive does not exist, DriveFreeSpace returns -

1. If the specified drive is not ready (such as an empty DVD drive), then DriveFreeSpace returns -2.

The declaration for DriveFreeSpace is:

Public Function DriveFreeSpace(ByVal DrvLetter As String) As Double

The DrvLetter argument is the letter of the drive for which you want to get the free space. If the DrvLetter argument is more than one character in

length, only the first character is used as the drive letter. For example, "c" and "cox" will both return the amount of free space on drive C:.

NOTES:

When using DriveFreeSpace on read-only CD/DVD disks, the free space value returned is always 0, and will not match the free space shown in

the drive's properties window or the This PC window in File Explorer.

If the computer has been configured to use disk space quotas for users, the amount of free space returned by DriveFreeSpace may not be the

amount of space actually available to the current user. Use the DriveAvailableSpace function to determine how much space is available to the

current user.

See the code listing for libFiles starting on page 87 to see the full source code for DriveFreeSpace.

DriveAvailableSpace The DriveAvailableSpace function returns the amount of space available to the current user on a specified disk drive, in bytes. If the specified drive does

not exist, DriveAvailableSpace returns -1. If the specified drive is not ready (such as an empty DVD drive), then DriveAvailableSpace returns -

2.

Use DriveAvailableSpace on computers that have been configured with disk space quotas for users to find out how much space is available to the

current user. If you don't know whether a computer may have been configured with disk space quotas, use DriveAvailableSpace instead of

DriveFreeSpace.

The declaration for DriveAvailableSpace is:

Public Function DriveAvailableSpace(ByVal DrvLetter As String) As Double

The DrvLetter argument is the letter of the drive for which you want to get the available space. If the DrvLetter argument is more than one character in

length, only the first character is used as the drive letter. For example, "d" and "dog" will both return the amount of available space on drive D:.

NOTES:

When using DriveAvailableSpace on read-only CD/DVD disks, the available space value returned is always 0, and will not match the available

space shown in the drive's properties window or the This PC window in File Explorer.

If the computer has been configured to use disk space quotas for users, use DriveAvailableSpace instead of DriveFreeSpace.

See the code listing for libFiles starting on page 87 to see the full source code for DriveAvailableSpace.

Page 41: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 41 of 137

TotalDriveSpace The TotalDriveSpace function returns the total amount of space on a specified disk drive, in bytes. If the specified drive does not exist,

TotalDriveSpace returns -1. If the specified drive is not ready, then TotalDriveSpace returns -2.

The declaration for TotalDriveSpace is:

Public Function DriveTotalSpace(ByVal DrvLetter As String) As Double

The DrvLetter argument is the letter of the drive for which you want to get the total space. If the DrvLetter argument is more than one character in

length, only the first character is used as the drive letter. For example, "g" and "golf" will both return the total amount of space on drive G:.

NOTES:

When using TotalDriveSpace on read-only CD/DVD disks, the total space value returned is equal to the total amount of space used on the

CD/DVD, and will not match the total space shown in the drive's properties window or the This PC window in File Explorer.

See the code listing for libFiles starting on page 87 to see the full source code for TotalDriveSpace.

Page 42: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 42 of 137

Functions to Scale Size in Bytes to KB, MB, or GB

All of the drive space functions, along with the GetFileSize function, return the size value in bytes. Although this is the most accurate measurement, values

in bytes tend to be very large numbers that are hard for a human to take in. This is why Windows and most applications adjust the measurement in bytes to a value in Kilobytes (KB), Megabytes (MB), or Gigabytes (GB).

The libFiles module contains 4 functions intended to help scale values in bytes to more easily read numbers:

Bytes2KB – scales a value in bytes to a value in KB.

Bytes2MB – scales a value in bytes to a value in MB.

Bytes2GB – scales a value in bytes to a value in GB.

Bytes2String – automatically chooses whether to format a byte value as KB, MB, or GB, depending on the size of the value in bytes. Returns a

string with the scaled value rounded to a specified number of decimal places, and suffixed with the new unit value: KB, MB, or GB.

The declarations for these functions are:

Public Function Bytes2KB(dblBytes As Double) As Double

Public Function Bytes2MB(dblBytes As Double) As Double

Public Function Bytes2GB(dblBytes As Double) As Double

Public Function Bytes2String(dblBytes As Double, Optional intDecPlaces As Integer = 0) As String

In each of these functions, the dblBytes argument is presumed to be a value in bytes. The functions Bytes2KB, Bytes2MB, and Bytes2GB return a

Double value containing the dblBytes argument's value scaled to the specified unit. The Bytes2String function returns a String with the scaled,

formatted value and the scale unit appended to the number.

In the Bytes2String function, the optional intDecPlaces argument specifies the number of decimal places to include in the result; if omitted, no decimal

places are included. The value is rounded for the number of specified decimal places.

NOTES:

The Bytes2String function automatically chooses how to scale the bytes value using these criteria:

o If dblBytes is less than 1 MB, it is scaled to KB

o If dblBytes is greater than or equal to 1 MB and less than 1GB, it is scaled to MB

o If dblBytes is greater than or equal to 1 GB, it is scaled to GB

In the Bytes2String function, if the intDecPlaces argument is omitted or less than zero, then no decimal places are shown in the result.

See the code listing for libFiles starting on page 87 to see the full source code for Bytes2KB, Bytes2MB, Bytes2GB, and Bytes2String.

Page 43: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 43 of 137

libNumeric VBA contains many numeric functions. VBA has a built-in function to determine whether a value is numeric (IsNumeric), but it does not have a function to

determine whether a value is an integer (as opposed to some other numeric type).

The libNumeric module contains a single function: IsInteger. The IsInteger function determines not only whether a value is numeric, but whether

or not it is specifically an integer number.

The next section describes the IsInteger function in more detail. You can see the complete code listing for libNumeric module on page 96.

IsInteger The IsInteger function tests whether or not a value is an integer value. IsInteger returns True if the value being tested is a Long or Integer value,

False otherwise.

IsInteger is typically used to test whether a string (such as one entered in a text box by a user) contains an integer value.

The declaration for IsInteger is:

Function IsInteger(s As Variant) As Boolean

The s argument is of the Variant type, so that IsInteger can test any data type.

See the code listing for libNumeric starting on page 96 to see the full source code for IsInteger.

Page 44: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 44 of 137

libString The libString module contains string functions to format names and build lists.

The libString module contains these functions:

AssembleName_FirstNameFirst – Given the components of a person's name (first name, last name, etc.) returns a string with the name

formatted as Firs Name/Last Name. Optionally includes middle initial, title of respect, and more.

AssembleName_LastNameFirst – Given the components of a person's name (first name, last name, etc.) returns a string with the name

formatted as Last Name/First Name.

ConcatStringList – Concatenates strings as a list with a selected delimiter. The selected delimiter may be a single character, or a multi-

character string.

The following sections describe each function of the libString module in more detail. You can see the complete listing for libString on page 97.

Page 45: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 45 of 137

AssembleName_FirstNameFirst

The AssembleName_FirstNameFirst function returns a single string for a person's name, assembled from component parts. The returned string has

the person's first name first, followed by the person's last name, e.g. "John Smith". The assembled name string may optionally include a middle initial or full middle name, the person's organization or job title, a title of respect (Mr., Mrs.) and a suffix (such as Jr., PhD., Esq., etc.).

Use AssembleName_FirstNameFirst in queries, reports, forms, or anywhere you want the improved cosmetic appearance of an assembled name

instead of the various fields that make up the person's name.

The declaration for AssembleName_FirstNameFirst is:

Public Function AssembleName_FirstNameFirst(sFirstName As String, sLastName As String, _

Optional sMI As String = "", _

Optional sOrg As String = "", _

Optional sRespect As String = "", _

Optional sSuffix As String = "") As String

sFirstName is a string value for a person's first name. sLastName is a string value for a person's last name. There are several Optional arguments:

sMI is a person's middle initial or full middle name, sOrg is a person's organization/business/job title, etc., sRespect is the title of respect (Mr., Mrs., Dr.,

etc.), and sSuffix is whatever comes after the name, such as Jr., Esq., PhD, MD, etc.

The following code fragment shows an example of the AssembleName_FirstNameFirst function:

Dim strFName As String

Dim strLName As String

Dim strMI As String

Dim strPosition As String

Dim strRespect As String

Dim strSuffix As String

strFName = "James"

strLName = "Kirk"

strMI = "Tiberius"

strPosition = "Starfleet"

strRespect = "Capt."

strSuffix = "Sr."

Debug.Print libString.AssembleName_FirstNameFirst(strFName, strLName, strMI, strPosition, strRespect, strSuffix)

Debug.Print libString.AssembleName_FirstNameFirst(strFName, strLName, strMI, , strRespect)

Debug.Print libString.AssembleName_FirstNameFirst(strFName, strLName, , , strRespect)

The preceding code fragment initializes several string variables for use in a call to the AssembleName_FirstNameFirst function. Three different calls to

the AssembleName_FirstNameFirst function are made; the first call uses all of the optional arguments, the other two calls use only some of the optional

arguments. The function results are printed in the VBA Debugger's Immediate window:

Capt. James Tiberius Kirk Sr.; Starfleet

Capt. James Tiberius Kirk

Capt. James Kirk

Page 46: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 46 of 137

In practice, the arguments for AssembleName_FirstNameFirst are most likely to come from the fields of a database table.

See the code listing for libString starting on page 97 to see the full source code for AssembleName_FirstNameFirst.

AssembleName_LastNameFirst

The AssembleName_LastNameFirst function returns a single string for a person's name, assembled from component parts. The returned string has the

person's last name first, followed by the person's first name, e.g. "Smith, John". The assembled name string may optionally include a middle initial or full middle name, and the person's organization or job title.

Use AssembleName_LastNameFirst in queries, reports, forms, or anywhere you want the improved cosmetic appearance of an assembled name

instead of the various fields that make up the person's name. Also, using AssembleName_LastNameFirst allows an alphabetic sorting by last name

from the function result.

The declaration for AssembleName_LastNameFirst is:

Public Function AssembleName_LastNameFirst(sFirstName As String, sLastName As String, _

Optional sMI As String = "", _

Optional sOrg As String = "") As String

sFirstName is a literal or variable string value for a person's first name, sLastName is a literal or variable string value for a person's last name. There are

two optional arguments: sMI is a person's middle initial or full middle name, and sOrg is a person's organization/business/job title, etc.

The following code fragment shows an example of the AssembleName_LastNameFirst function:

Dim strFName As String

Dim strLName As String

Dim strMI As String

Dim strPosition As String

strFName = "James"

strLName = "Kirk"

strMI = "Tiberius"

strPosition = "Starfleet"

Debug.Print libString.AssembleName_LastNameFirst(strFName, strLName, strMI, strPosition)

Debug.Print libString.AssembleName_LastNameFirst(strFName, strLName, strMI)

Debug.Print libString.AssembleName_LastNameFirst(strFName, strLName, , strPosition)

The preceding code fragment initializes several string variables for use in a call to the AssembleName_LastNameFirst function. Three different calls to

the AssembleName_LastNameFirst function are made; the first call uses all of the optional arguments, the other two calls use only some of the optional

arguments. The function results are printed in the VBA Debugger's Immediate window:

Kirk, James Tiberius; Starfleet

Kirk, James Tiberius

Kirk, James; Starfleet

In practice, the arguments for AssembleName_LastNameFirst are most likely to come from the fields of a database table.

Page 47: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 47 of 137

See the code listing for libString starting on page 97 to see the full source code for AssembleName_LastNameFirst.

Page 48: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 48 of 137

ConcatStringList

The ConcatStringList function concatenates one string to another, using a specified delimiter, and optionally leaving no space between the two strings.

The ConcatStringList function has a large number of uses – you can build strings showing lists of values separated by any character(s) you choose. By

using delimiters such as "AND" and "OR" you can use ConcatStringList to build lists of criteria for a WHERE clause in an SQL statement, filter strings

for forms and reports, assemble lines for comma delimited text files, and many other purposes.

The declaration for ConcatStringList is:

Public Function ConcatStringList(s1 As String, s2 As String, sDelim As String, _

Optional NoSpace As Boolean = False) As String

The ConcatStringList function returns s2 concatenated to the end of s1, delimited by sDelim. Usually, a space is inserted after the delimiter, and

before s2. Use the optional NoSpace argument to concatenate the strings without a space.

The following procedure exercises the ConcatStringList function:

Sub test_ConcatStringList()

Dim strPart1 As String

Dim strPart2 As String

strPart1 = "apples"

strPart2 = "oranges"

strPart1 = libString.ConcatStringList(strPart1, strPart2, ";")

strPart2 = "pears"

strPart1 = libString.ConcatStringList(strPart1, strPart2, ";")

Debug.Print strPart1

strPart1 = "lions"

strPart2 = "tigers"

strPart1 = libString.ConcatStringList(strPart1, strPart2, ";", True)

strPart2 = "bears"

strPart1 = libString.ConcatStringList(strPart1, strPart2, ";", True)

Debug.Print strPart1

strPart1 = "(Orders < 10)"

strPart2 = "(Orders > 1)"

strPart1 = libString.ConcatStringList(strPart1, strPart2, " AND")

strPart1 = "(" & strPart1 & ")"

strPart2 = "(Level = 2)"

strPart1 = libString.ConcatStringList(strPart1, strPart2, " OR")

Debug.Print strPart1

End Sub

Page 49: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 49 of 137

The test procedure above contains 6 calls to the ConcatStringList function, marked in bold text. The first two calls to ConcatStringList create the

following string:

apples; oranges; pears

The next two calls to ConcatStringList create this string (the NoSpace optional argument is True):

lions;tigers;bears

The final two calls to ConcatStringList create a string suitable for use as a Filter string in a Form or Report, or (with the addition of table names) the

criteria portion of a WHERE clause in an SQL statement:

((Orders < 10) AND (Orders > 1)) OR (Level = 2)

In these final calls to ConcatStringList, the sDelim arguments were the logical operators "AND" and "OR".

NOTES:

The sDelim argument may be any character, symbol, word or phrase that you choose, including an empty string so that no delimiter is included.

Use the ConcatStringList function instead of the VBA Join function when:

o you don't have all of the list elements available at once

o you want to use varying delimiters (such as using "AND" and "OR" as delimiters in the same string)

See the code listing for libString starting on page 97 to see the full source code for ConcatStringList.

Page 50: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 50 of 137

libSystem The libSystem module contains procedures and functions that work with the Windows operating system.

The libSystem module contains these procedures and functions:

GetComputerName – Returns the name of the computer.

GetComputerName_API – Returns the name of the computer by using a Windows API call.

GetLogonName – Returns the name of the user logged in to Windows.

GetLogonName_API – Returns the name of the user logged in to Windows by using a Windows API call.

CreateLNKShortcut – Creates a shortcut file for a specified file in a specified location.

CreateURLShortcut – Creates a shortcut file for a specified URL in a specified location.

The following sections describe each procedure and function of the libSystem module in more detail. You can see the complete listing for libSystem on

page 99.

GetComputerName The GetComputerName function returns the name of the computer on which your application is running by using the VBA Environ function.

The declaration for GetComputerName is:

Public Function GetComputerName() As String

See the code listing for libSystem starting on page 99 to see the full source code for GetComputerName.

GetComputerName_API The GetComputerName_API function returns the name of the computer on which your application is running by making a call to the Windows

kernel32.dll.

The declaration for GetComputerName_API is:

Public Function GetComputerName_API() As String

NOTES:

The GetComputerName_API function will not work without the following Declare statement in the Declarations section of the module where

GetComputerName_API is defined:

Private Declare PtrSafe Function APICall_GetComputerName Lib "kernel32" _

Alias "GetComputerNameA" (ByVal lpBuffer As String, nSize As Long) As Long

The code supplied in MSH Library – Tools And Utilities v2017-04.accdb uses conditional compilation to compile the Declare

statement above appropriately for 64-bit or 32-bit versions of VBA.

See the code listing for libSystem starting on page 99 to see the full source code for GetComputerName_API.

Page 51: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 51 of 137

GetLogonName The GetLogonName function returns the current Windows user name. GetLogonName uses the VBA Environ function.

The declaration for GetLogonName is:

Public Function GetLogonName() As String

See the code listing for libSystem starting on page 99 to see the full source code for GetLogonName.

GetLogonName_API The GetLogonName_API function returns the current Windows user name by making a call to the Windows advapi32.dll.

The declaration for GetLogonName_API is:

Public Function GetLogonName_API() As String

NOTES:

The GetLogonName_API function will not work without the following Declare statement in the Declarations section of the module where

GetLogonName_API is defined:

Private Declare PtrSafe Function APICall_GetUserName Lib "advapi32.dll" _

Alias "GetUserNameA" (ByVal lpBuffer As String, nSize As Long) As Long

The code supplied in MSH Library – Tools And Utilities v2017-04.accdb uses conditional compilation to compile the Declare

statement above appropriately for 64-bit or 32-bit versions of VBA.

See the code listing for libSystem starting on page 99 to see the full source code for GetLogonName_API.

Page 52: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 52 of 137

CreateLNKShortcut The CreateLNKShortcut procedure creates a Windows shortcut file (a .lnk file) for a specified item. CreateLNKShortcut uses WScript.Shell

to carry out its task.

The declaration for CreateLNKShortcut is:

Public Sub CreateLNKShortcut(ByVal ShortCutFileName As String, _

PointsTo As String, _

WorkDir As String, _

HotKey As String, _

Description As String)

ShortCutFileName is the full path and filename for the new shortcut. ShortCutFileName is typically in the format:

"C:\Users\[USERNAME]\Desktop\[LINKNAME].lnk" where [USERNAME] is the user's logon name and [LINKNAME] is the desired name of

the shortcut. You can place a shortcut in any folder, this example shows placing the shortcut in the current user's desktop.

PointsTo is the target of the shortcut – that is, the item the shortcut is intended to open. PointsTo must include a fully qualified path. For example,

"C:\Users\Matthew\Documents\Financials\First Quarter Sales.xlsx" sets the shortcut to open an Excel workbook named "First

Quarter Sales.xlsx" in the folder "C:\Users\Matthew\Documents\Financials\".

WorkDir is the working directory/folder of the item opened by the shortcut, typically the same path used in PointsTo. WorkDir must contain a fully

qualified path, such as "C:\Users\Matthew\Documents\Financials\".

HotKey is a string containing a representation of the hotkey you want to use for the shortcut, such as "Ctrl+Alt+T". Generally you should not set a hotkey, as it

is very difficult to know whether you are using a hotkey already in use for another shortcut.

Description appears in the Comment box of the shortcut's properties window.

NOTES:

If you omit the .lnk file extension in the ShortCutFileName argument, it is automatically added.

You cannot use this procedure to create a shortcut to a URL. Use the CreateURLShortcut procedure instead.

You can get the current logged on user name with the GetLogonName or GetLogon_API functions in this library.

If you do not want to set a hotkey for the shortcut link, pass an empty string in the HotKey argument.

All arguments are required.

If you do not specify valid, fully qualified paths in the various arguments of CreateLNKShortcut, a runtime error will occur.

The CreateLNKShortcut procedure can only create shortcuts for the currently logged in user. Modifying the public desktop setting is not possible

without Administrator permissions, which VBA does not run with (even if the logged in user has Administrator permission). Similarly, it cannot create shortcuts for another user, as the VBA code will not have permission to change the contents of another user's desktop folder.

See the code listing for libSystem starting on page 99 to see the full source code for CreateLNKShortcut.

Page 53: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 53 of 137

CreateURLShortcut The CreateURLShortcut procedure creates a Windows shortcut file (a .url file) for a specified item. CreateURLShortcut uses WScript.Shell

to carry out its task.

The declaration for CreateURLShortcut is:

Public Sub CreateURLShortcut(ByVal ShortCutFileName As String, _

PointsTo As String)

ShortCutFileName is the full path and filename for the new shortcut. ShortCutFileName is typically in the format:

"C:\Users\[USERNAME]\Desktop\[LINKNAME].url" where [USERNAME] is the user's logon name and [LINKNAME] is the desired name of

the shortcut.

PointsTo is the target of the shortcut – that is, the URL the shortcut is intended to open. PointsTo must include a well-formed URL. For example,

"http://www.didjiman.com/business" sets the shortcut to open the web page in your default browser.

NOTES:

If you omit the .url file extension in the ShortCutFileName argument, it is automatically added.

You can get the current logged on user name with the GetLogonName or GetLogon_API functions.

All arguments are required.

If you do not specify a valid, fully qualified path in the ShortCutFileName argument, a runtime error will occur.

The CreateURLShortcut procedure can only create shortcuts for the currently logged in user. Modifying the public desktop setting is not possible

without Administrator permissions, which VBA does not run with (even if the logged in user has Administrator permission). Similarly, it cannot create shortcuts for another user, as the VBA code will not have permission to change the contents of another user's desktop folder.

See the code listing for libSystem starting on page 99 to see the full source code for CreateLNKShortcut.

Page 54: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 54 of 137

libTables The libTables module contains functions for working with tables in the current MS Access database.

The libTables module contains these functions:

TableExists – Tests whether a table of a particular name exists. Detects local and linked tables.

TableExists_Linked – Tests whether a table of a particular name exists. Detects only linked tables.

TableExists_Local – Tests whether a table of a particular name exists. Detects only local tables.

Make_TempTableName – Creates a name for a temporary table.

The following sections describe each procedure and function of the libTables module in more detail. You can see the complete listing for libTables on

page 102.

TableExists

The TableExists function tests whether a particular table exists in the current database. TableExists returns True if the table exists, False

otherwise. TableExists detects the existence of both local and linked tables, and will detect hidden tables.

The declaration for TableExists is:

Public Function TableExists(ByVal sTableName As String) As Boolean

sTableName is a literal or variable string value containing the name of a table to search for.

NOTES:

TableExists performs a case-insensitive search when matching table names; for example, TableExists treats ThisTable, THISTABLE,

and THIStable as being the same name.

See the code listing for libTables starting on page 102 to see the full source code for TableExists.

TableExists_Linked

The TableExists_Linked function tests whether a particular linked table exists in the current database. TableExists_Linked returns True if the

linked table exists, False otherwise. TableExists_Linked detects only the existence of linked tables; it will detect hidden linked tables.

The declaration for TableExists_Linked is:

Public Function TableExists_Linked(ByVal sTableName As String) As Boolean

sTableName is a literal or variable string value containing the name of a linked table to search for.

NOTES:

TableExists_Linked performs a case-insensitive search when matching table names; for example, TableExists_Linked treats

ThisTable, THISTABLE, and THIStable as being the same name.

See the code listing for libTables starting on page 102 to see the full source code for TableExists_Linked.

Page 55: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 55 of 137

TableExists_Local

The TableExists_Local function tests whether a particular local table exists in the current database. TableExists_Local returns True if the local

table exists, False otherwise. TableExists_Local detects only the existence of local tables; it will detect hidden local tables.

The declaration for TableExists_Local is:

Public Function TableExists_Local(ByVal sTableName As String) As Boolean

sTableName is a literal or variable string value containing the name of a local table to search for.

NOTES:

TableExists_Local performs a case-insensitive search when matching table names; for example, TableExists_Local treats ThisTable,

THISTABLE, and THIStable as being the same name.

See the code listing for libTables starting on page 102 to see the full source code for TableExists_Local.

Make_TempTableName

The Make_TempTableName function is intended to create unique names for temporary tables. Make_TempTableName returns a name based on the

current date and time, and ensures that it is unique.

The declaration for Make_TempTableName is:

Public Function Make_TempTableName() As String

The returned table name is formatted as TEMPyyyymmddhhmmssN where yyyy is the year, mm is the month, dd is the day, hh the hour, mm the minutes, ss

is the seconds, and N is a number (0,1,2,3 and so on). For example, a call to Make_TempTableName on 9/10/17 at 11:02:45am would return this table

name:

TEMP201709101102450

See the code listing for libTables starting on page 102 to see the full source code for Make_TempTableName.

Page 56: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 56 of 137

libUIControl The libUIControl module contains procedures for working with the MS Access user-interface, specifically the Navigation Pane.

The libUIControl module contains these functions:

NavPane_Minimize – Minimizes the Navigation Pane.

NavPane_Maximize – Maximizes the Navigation Pane.

NavPane_Hide – Completely hides the Navigation Pane.

NavPane_UnHide – Restores the Navigation Pane to visibility.

The following sections describe each procedure and function of the libUIControl module in more detail. You can see the complete listing for

libUIControl on page 104.

NavPane_Minimize

The NavPane_Minimize procedure minimizes the MS Access Navigation Pane, but leaves it visible.

The declaration for NavPane_Minimize is:

Public Sub NavPane_Minimize(Optional bCustom As Boolean = False)

To minimize the Navigation Pane, the NavPane_Minimize procedure must make the Navigation Pane the selected object in the MS Access user interface.

The act of selecting the Navigation Pane affects its view setting. NavPane_Minimize selects the Object Type category view for the Navigation Pane by

default. Use the optional bCustom argument to cause NavPane_Minimize to select the Custom category view in the Navigation Pane.

See the code listing for libUIControl starting on page 104 to see the full source code for NavPane_Minimize.

NavPane_Maximize

The NavPane_Maximize procedure maximizes the MS Access Navigation Pane – that is, the Navigation Pane "shutter" is opened.

The declaration for NavPane_Maximize is:

Public Sub NavPane_Maximize(Optional bCustom As Boolean = False)

To maximize the Navigation Pane, the NavPane_Maximize procedure must make the Navigation Pane the selected object in the MS Access user interface.

The act of selecting the Navigation Pane affects its view setting. NavPane_Maximize selects the Object Type category view for the Navigation Pane by

default. Use the optional bCustom argument to cause NavPane_Maximize to select the Custom category view in the Navigation Pane.

See the code listing for libUIControl starting on page 104 to see the full source code for NavPane_Maximize.

Page 57: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 57 of 137

NavPane_Hide

The NavPane_Hide procedure hides the Navigation Pane – that is, makes it completely invisible.

The declaration for NavPane_Hide is:

Public Sub NavPane_Hide(Optional ByRef FormCalling As Form = Nothing)

The optional FormCalling argument has a very special purpose, and should only be used if NavPane_Hide is being called from a form.

Because of a quirk when trying to hide the Navigation Pane from code called from a popup/modal Form, the FormCalling argument should always be used,

using Me as the form reference:

NavPane_Hide Me

When the FormCalling argument is supplied, NavPane_Hide applies the work-around technique to successfully hide the Navigation Pane from a

popup/modal Form. Note that you will get a runtime error if you supply the FormCalling argument when NavPane_Hide has not been called from inside

a Form.

See the code listing for libUIControl starting on page 104 to see the full source code for NavPane_Hide.

NavPane_UnHide

The NavPane_UnHide procedure makes the Navigation Pane visible.

The declaration for NavPane_UnHide is:

Public Sub NavPane_UnHide()

NOTES:

When the Navigation Pane is unhidden, it is also maximized. If you want the Navigation Pane to be minimized, you should make a call to the

NavPane_Minimize procedure after the call to NavPane_UnHide.

See the code listing for libUIControl starting on page 104 to see the full source code for NavPane_UnHide.

Page 58: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 58 of 137

libUnitConversion The libUnitConversion module contains 75 functions to convert values from one unit of measurement to another for linear distance, area, weight,

volume, temperature, speed, and more.

With only a few exceptions, the functions in libUnitConversion take a Double numeric value as an argument, and return the appropriate conversion

result as a Double number. A typical function declaration in libUnitConversion is:

Public Function Convert_Inch2Centimeter(dblInches As Double) As Double

All of the function declarations in libUnitConversion are named similarly to the example above – the source unit comes first, the numeral "2" (shorthand

for "to") and then the unit of measurement being converted to. The example declaration above is Inch2Centimeter, or "inches to centimeters".

The next few sections represent the categories of conversion functions, and list the function names. You can see the full source code for the

libUnitConversion module starting on page 106.

NOTES:

Measurement units for Gallons, Pints, and Miles are for United States Gallons, Pints, and Miles. Imperial units are not included in these conversion functions (Imperial gallons, miles, etc. are used in Great Britain and the British Commonwealth nations.)

Page 59: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 59 of 137

Linear (Distance) Measurement

Conversion Function Name Category

Inches to Centimeters Convert_Inch2Centimeter Metric – US

Centimeters to Inches Convert_Centimeter2Inch US – Metric

Feet to Meters Convert_Feet2Meter Metric – US

Meters to Feet Convert_Meter2Feet US – Metric

Kilometers to Miles Convert_Kilometer2Mile US – Metric

Miles to Kilometers Convert_Mile2Kilometer US – Metric

US Land Miles to Feet Convert_USLandMiles2Feet English – English

Meters to Centimeters Convert_Meter2Centimeter Metric – Metric

Centimeter to Meter Convert_Centimeter2Meter Metric – Metric

Feet to Yards Convert_Feet2Yards US – US

Yards to Feet Convert_Yards2Feet US – US

Feet to Inches Convert_Feet2Inches US – US

Feet to Inches String Convert_Feet2FtInchesStr US – US

Inches to Feet Convert_Inches2Feet US – US

Inches to Feet String Convert_Inches2FtInchesStr US – US

NOTES:

The Convert_Feet2FtInchesStr function returns a String data type. This function is intended for displaying a value in Feet as feet and

inches, and is intended primarily as cosmetic formatting. For example, 1.5 feet will result in the string 1' 6".

The Convert_Inches2FtInchesStr function also returns a String data type. This function is intended to display a value in Inches as feet and

inches, and is intended primarily as cosmetic formatting. For example, a value of 18 inches will result in the string 1' 6".

You can see the full source code for the libUnitConversion module starting on page 106.

Page 60: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 60 of 137

Area Measurement

Conversion Function Name Category

Sq. Centimeters to Sq. Inches Convert_SqCentimeters2SqInches Metric – US

Sq. Inches to Sq. Centimeters Convert_SqInches2SqCentimeters US – Metric

Sq. Meters to Sq. Feet Convert_SqMeters2SqFeet Metric – US

Sq. Feet to Sq. Meters Convert_SqFeet2SqMeters US – Metric

Acres to Sq. Feet Convert_Acres2SqFeet US – US

Sq. Feet to Acres Convert_SqFeet2Acres US – US

Sq. Feet to Sq. Inches Convert_SqFeet2SqInches US – US

Sq. Inches to Sq. Feet Convert_SqInches2SqFeet US – US

Sq. Feet to Sq. Yards Convert_SqFeet2SqYards US – US

Sq. Yards to Sq. Feet Convert_SqYards2SqFeet US – US

Sq. Centimeter to Sq. Meters Convert_SqCentimeter2SqMeter Metric – Metric

Sq. Meters to Sq. Centimeter Convert_SqMeters2SqCentimeters Metric – Metric

You can see the full source code for the libUnitConversion module starting on page 106.

Weight Measurement

Conversion Function Name Category

Grams to Ounces Convert_Gram2Ounce Metric – US

Ounces to Grams Convert_Ounce2Gram US – Metric

Kilos to Pounds Convert_Kilo2Pound Metric – US

Pounds to Kilos Convert_Pound2Kilo US – Metric

Ounces to Pounds Convert_OZ2LB US – US

Pounds to Ounces Convert_LB2OZ US – US

Grams to Kilos Convert_Gram2Kilo Metric – Metric

Kilos to Grams Convert_Kilos2Grams Metric – Metric

You can see the full source code for the libUnitConversion module starting on page 106.

Page 61: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 61 of 137

Volume Measurement

Conversion Function Name Category

Cubic Centimeters to Cubic Inches Convert_CubicCentimeters2CubicInches Metric – US

Cubic Inches to Cubic Centimeters Convert_CubicInches2CubicCentimeters US – Metric

Cubic Meters to Cubic Feet Convert_CubicMeters2CubicFeet Metric – US

Cubic Feet to Cubic Meters Convert_CubicFeet2CubicMeters US – Metric

Cubic Inches to Cubic Feet Convert_CubicInches2CubicFeet US – US

Cubic Feet to Cubic Inches Convert_CubicFeet2CubicInches US – US

Cubic Centimeters to Cubic Meters Convert_CubicCentimeters2CubicMeters Metric – Metric

Cubic Meters to Cubic Centimeters Convert_CubicMeters2CubicCentimeters Metric – Metric

Cubic Centimeters to Liters Convert_CubicCentimeters2Liters Metric – Metric

Liters to Cubic Centimeters Convert_Liters2CubicCentimeters Metric – Metric

You can see the full source code for the libUnitConversion module starting on page 106.

Page 62: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 62 of 137

Liquid Measurement

Conversion Function Name Category

Liters to US Gallons Convert_Liters2Gallons Metric – US

US Gallons to Liters Convert_USGallons2Liters US – Metric

Fluid Ounces to Pints Convert_FluidOZ2Pints US – US

Pints to Fluid Ounces Convert_USPints2FluidOZ US – US

US Pints to US Quarts Convert_USPints2Quarts US – US

US Quarts to US Pints Convert_USQuarts2Pints US – US

US Quarts to US Gallons Convert_Quarts2USGallons US – US

US Gallons to US Quarts Convert_USGallons2Quarts US – US

Milli-liters to US Fluid Ounce Convert_ML2FluidOZ Metric – US

US Fluid Ounce to Milli-liters Convert_FluidOZ2ML US – Metric

Milli-liter to Liter Convert_ML2Liter Metric – Metric

Liter to Milli-Liter Convert_Liter2ML Metric – Metric

Liters to Pints Convert_Liter2Pint Metric – US

Pints to Liters Convert_Pint2Liter US – METRIC

You can see the full source code for the libUnitConversion module starting on page 106.

Speed Measurement

Conversion Function Name Category

Kilometers/Hour (KPH) to Miles/Hour (MPH) Convert_KPH2MPH Metric – US

Miles/Hour (MPH) to Kilometers/Hour (KPH) Convert_MPH2KPH US – Metric

Miles/Hour (MPH) to Feet Per Second (FPS) Convert_MPH2FPS US – US

Feet Per Second (FPS) to Miles/Hour (MPH) Convert_FPS2MPH US – US

Kilometers/Hour (KPH) to Meters Per Second (MPS) Convert_KPH2MPS Metric – Metric

Meters Per Second (MPS) to Kilometers/Hour (KPH) Convert_MPS2KPH Metric – Metric

You can see the full source code for the libUnitConversion module starting on page 106.

Page 63: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 63 of 137

Temperature Measurement

Conversion Function Name Category

Centigrade/Celsius to Fahrenheit Convert_Centigrade2Fahrenheit Metric – US

Fahrenheit to Centigrade/Celsius Convert_Fahrenheit2Centigrade US – Metric

Fahrenheit to Kelvin Convert_Fahrenheit2Kelvin US – Scientific

Kelvin to Fahrenheit Convert_Kelvin2Fahrenheit Scientific – US

Centigrade to Kelvin Convert_Centigrade2Kelvin Metric – Scientific

Kelvin to Centigrade Convert_Kelvin2Centigrade Scientific – Metric

You can see the full source code for the libUnitConversion module starting on page 106.

Print & Screen Measurement

Conversion Function Name Category

Inches to Twips Convert_Inches2Twips

Twips to Inches Convert_Twips2Inches

As you probably know, print and print spacing is measured in points, abbreviated pt. Many design systems use points as a measurement unit.

A twip is a twentieth of a point. Some design systems use a mixture of inches and twips. For example, the position of a window on-screen may be measured in twips, whereas the window's dimensions are measured in inches. If you want to calculate, for example, where the right-hand edge of the window is on the screen, you will need to convert between twips and inches.

You can see the full source code for the libUnitConversion module starting on page 106.

Color Notation

Conversion Function Name Category

Hexadecimal Color Notation to Red-Green-Blue Convert_HexRGB2RedGreenBlue

Red-Green-Blue Notation to Hexadecimal Convert_RedGreenBlue2HexRGB

Different color-picker dialogs use different ways of entering color notations. Some require entry of custom color values in Hexadecimal notation (for example #0F0F0F) and others require entry of separate values for Red, Green, and Blue (frequently referred to as RGB). These two functions convert from the RGB format to the Hexadecimal format, and vice-versa.

You can see the full source code for the libUnitConversion module starting on page 106.

Page 64: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 64 of 137

libWindow The libWindow module is only useful in applications that use forms in overlapping windows, or have popup/modal forms. The procedures in libWindow

enable you to reset the width only, or the width and height of an open Form window.

The libWindow module contains these procedures:

Reset_Window_Width – Resets a Form window to its width at design time. Use this procedure if you want to allow a user to resize a popup dialog

vertically, but not horizontally.

Reset_WindowSize – Resets a Form window's width and height to their values at design-time.

The following sections describe each procedure of the libWindow module in more detail. You can see the complete listing for libWindow on page 115.

Reset_Window_Width Use the Reset_Window_Width procedure in the OnResize event-handler of a Form to only allow the Form to be resized vertically.

The declaration of Reset_Window_Width is:

Public Sub Reset_Window_Width(frm As Form)

The frm argument is typically the self-reference Me.

See the code listing for libWindow starting on page 115 to see the full source code for Reset_Window_Width.

Reset_WindowSize Use the Reset_WindowSize procedure to restore an open form to its size at design time.

The declaration of Reset_WindowSize is:

Public Sub Reset_WindowSize(frm As Form)

The frm argument is typically the self-reference Me.

See the code listing for libWindow starting on page 115 to see the full source code for Reset_WindowSize.

Page 65: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 65 of 137

Sorter (sorting tools) The Sorter module contains code to carry out a basic bubble sort for arrays and the value lists of ListBox and ComboBox controls. The bubble-sort

algorithm is not the most efficient sorting algorithm, but it is easy to implement. For lists that are relatively short, the bubble-sort algorithm is an excellent choice.

The MS Access ListBox and ComboBox controls do not have an ability to sort themselves. The majority of the time, this does not matter as these controls

are populated from ordered recordsets. Inevitably, though, there will be times, however rare, when you end up with an unsorted list either in a ListBox or

ComboBox control, or in an array.

The Sorter module contains these Public procedures:

Sort_ArrayList – Sorts a single-dimensional array.

Sort_Array2D – Sorts a two-dimensional array on an optionally specified column of the second dimension.

SortListBox – Sorts a single-column list in either a ListBox or ComboBox control.

RowSortListBox – Sorts a multi-column list in either a ListBox or a ComboBox control.

The Sorter module also contains a couple of Private procedures. The Private procedures are intended only to be called by the Public procedures

in the Sorter module, and are not discussed further in this document.

The Private procedures of the Sorter module are:

RowSwap – Swaps a row from a two-dimensional array or multi-column ListBox or ComboBox control.

Swap – Swaps elements in a single-dimensional array or single-column ListBox or ComboBox control.

The following sections describe the Public procedures of the Sorter module in more detail. You can see the complete listing for Sorter on page 117.

Sort_ArrayList The Sort_ArrayList procedure sorts a single-dimensional array.

The declaration for Sort_ArrayList is:

Public Sub Sort_ArrayList(ByRef aList As Variant, Optional SortAsc As Boolean = True)

The aList argument is expected to be an array of some kind. aList is a Variant data type so that any type of array may be passed. aList is passed

by reference (ByRef) and contains the sorted array when the procedure is finished executing.

The optional SortAsc argument defaults to True, and controls whether the array is sorted in ascending order (the default) or in descending order.

NOTES:

If the type name of the aList argument does not end with () (indicating that the aList argument does not contain an array), then

Sort_ArrayList raises a runtime error.

See the code listing for Sorter starting on page 117 to see the full source code for Sort_ArrayList.

Page 66: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 66 of 137

Sort_Array2D

The Sort_Array2D procedure sorts a two-dimensional array. A two-dimensional array can be regarded as a list of rows (the first dimension of the array)

and columns (in the second dimension of the array). For example, an array declared as:

Dim Array2D(5, 4) As String

could be regarded as 6 rows (numbered 0-5) with 5 columns (numbered 0-4).

Sort_Array2D is intended to sort exactly this type of representation in an array.

The declaration for Sort_Array2D is:

Public Sub Sort_Array2D(ByRef aList As Variant, _

Optional SortAsc As Boolean = True, _

Optional SortCol As Long = 0, _

Optional NumCompare As Boolean = False)

The aList argument is expected to be an array of some kind. aList is a Variant data type so that any type of array may be passed. aList is passed

by reference (ByRef) and contains the sorted array when the procedure is finished executing.

The optional SortAsc argument defaults to True, and controls whether the array is sorted in ascending order (the default) or in descending order.

SortCol is an optional argument specifying which column (that is, a subscript value for the second dimension of the array) to sort the array on.

The optional NumCompare argument defaults to False, and controls whether values in the array are converted to numeric values before being compared.

NOTES:

Sort_Array2D will only work correctly if the array being sorted is organized with the first dimension as rows and the second dimension as columns.

If the type name of the aList argument does not end with () (indicating that the aList argument does not contain an array), then Sort_Array2D

raises a runtime error.

See the code listing for Sorter starting on page 117 to see the full source code for Sort_Array2D.

SortListBox The SortListBox procedure sorts a single-column list in either a ListBox or ComboBox control having a value list.

The declaration for SortListBox is:

Public Sub SortListBox(ByRef LstBox As Control, Optional SortAsc As Boolean = True)

The LstBox argument is expected to be a reference to either a ListBox or ComboBox control.

The optional SortAsc argument defaults to True, and controls whether the array is sorted in ascending order (the default) or in descending order.

NOTES:

If the type name of the LstBox argument is not ListBox or ComboBox, then SortListBox raises a runtime error.

If the ListBox or ComboBox control to be sorted does not have a Row Source Type of "Value List", then SortListBox raises a runtime error.

See the code listing for Sorter starting on page 117 to see the full source code for SortListBox.

Page 67: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 67 of 137

RowSortListBox The RowSortListBox procedure sorts a multi-column list in either a ListBox or ComboBox control.

The declaration for RowSortListBox is:

Public Sub RowSortListBox(ByRef LstBox As Control, _

Optional SortAsc As Boolean = True, _

Optional SortCol As Long = 0, _

Optional NumCompare As Boolean = False)

The LstBox argument is expected to be a reference to either a ListBox or ComboBox control.

The optional SortAsc argument defaults to True, and controls whether the array is sorted in ascending order (the default) or in descending order.

SortCol is an optional argument specifying which column to sort the control's list on.

The optional NumCompare argument defaults to False, and controls whether values in the array are converted to numeric values before being compared.

NOTES:

If the type name of the LstBox argument is not ListBox or ComboBox, then RowSortListBox raises a runtime error.

The ListBox or ComboBox control to be sorted must have "Value List" as the Row Source Type property; if it does not, the procedure raises a

runtime error.

See the code listing for Sorter starting on page 117 to see the full source code for RowSortListBox.

Page 68: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 68 of 137

Code Listings

This section contains the complete module code listing for each of the modules in the Tools & Utilities library.

The complete source code for MSH Library – Tools And Utilities is distributed in an MS Access file named MSH Library – Tools And Utilities

v2017-04.accdb, which may be downloaded from http://www.didjiman.com/business/cv-swareVBA.htm.

The LIBCONFIRM Module This section contains the entirety of the libConfirm module. If you copy and paste this code into your own applications, you must keep the copyright and

license notices intact, per the GNU Lesser General Public License (pg. 128) and the GNU General Public License (pg. 130). This is not public domain software; it is licensed to you at no cost.

'COPYRIGHT NOTICE

'THIS VBA MODULE IS COPYRIGHT 2014-2017 BY MATTHEW S. HARRIS. All Rights Reserved.

'You can contact Matthew S. Harris at [email protected]

'Code Version: v2017-04

'COPYING PERMISSIONS

'The libConfirm module code is free software: you can redistribute it and/or modify

'it under the terms of the GNU Lesser General Public License as published by

'the Free Software Foundation, either version 3 of the License, or any later version.

'This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;

'without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

'See the GNU Lesser General Public License for more details.

'You should have received a copy of the GNU General Public License

'along with this module. If not, see <http://www.gnu.org/licenses/>.

Option Compare Database

Option Explicit

Public Function DeletionDOUBLEConfirmed(strName As String) As Boolean

'Asks user to confirm a deletion twice. Returns True only if user responds "Yes" twice, False otherwise.

'strName should contain a name or description of the item being deleted.

Dim chc As Integer

DeletionDOUBLEConfirmed = False 'presume user will answer no at least once

chc = MsgBox(prompt:="Are you certain you want to delete: '" & strName & "'?" & vbNewLine & vbNewLine & _

"Deletions are permanent and cannot be undone.", _

buttons:=vbExclamation + vbYesNo + vbDefaultButton2, Title:=CurrentProject.Name & " - Confirm Deletion")

If chc = vbYes Then 'ask again

chc = MsgBox(prompt:="Are you absolutely certain you want to delete: '" & strName & "'?" & vbNewLine & vbNewLine & _

"Deletions are PERMANENT and cannot be undone.", _

buttons:=vbExclamation + vbYesNo + vbDefaultButton2, Title:=CurrentProject.Name & " - Confirm Deletion")

If chc = vbYes Then

'user still wants to delete after being asked twice

DeletionDOUBLEConfirmed = True

End If

End If

End Function

Page 69: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 69 of 137

Public Function DeletionConfirmed(strName As String) As Boolean

'Asks user to confirm a deletion. Returns True if user responds "Yes", False otherwise.

'strName should contain a name or description of the item being deleted.

Dim chc As Integer

chc = MsgBox(prompt:="Are you certain you want to delete: '" & strName & "'?" & vbNewLine & vbNewLine & _

"Deletions are permanent and cannot be undone.", _

buttons:=vbExclamation + vbYesNo + vbDefaultButton2, Title:=CurrentProject.Name & " - Confirm Deletion")

If chc = vbNo Then

DeletionConfirmed = False

Else

DeletionConfirmed = True

End If

End Function

Public Function Confirm_Lookup_GlobalChange(varOldValue As Variant, varNewValue As Variant) As Boolean

'Asks user to confirm making a global change to the database, typically as the result of editing a lookup list.

'Returns True if user responds "Yes", False otherwise.

'varOldValue should contain the value being replaced, notice that varOldValue is a variant, so any data type may be passed.

'varNewValue should contain the new value, notice that varNewValue is a variant, so any data type may be passed.

If MsgBox(prompt:="You are about to change '" & varOldValue & "' to '" & varNewValue & "'." & vbNewLine & vbNewLine & _

"All existing entries will be changed. (This may take several moments.)" & vbNewLine & vbNewLine & _

"Click Yes to carry out the change; click No to restore the previous entry.", _

buttons:=vbExclamation + vbYesNo + vbDefaultButton2, Title:=CurrentProject.Name) = vbYes Then

'confirmation accepted

Confirm_Lookup_GlobalChange = True

Else

'confirmation refused

Confirm_Lookup_GlobalChange = False

End If

End Function

Page 70: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 70 of 137

The LIBDATAVALIDATION Module This section contains the entirety of the libDataValidation module. If you copy and paste this code into your own applications, you must keep the

copyright and license notices intact, per the GNU Lesser General Public License (pg. 128) and the GNU General Public License (pg. 130). This is not public domain software; it is licensed to you at no cost.

'COPYRIGHT NOTICE

'THIS VBA MODULE IS COPYRIGHT 2014-2017 BY MATTHEW S. HARRIS. All Rights Reserved.

'You can contact Matthew S. Harris at [email protected]

'Code Version: v2017-04

'COPYING PERMISSIONS

'The libDataValidation module code is free software: you can redistribute it and/or modify

'it under the terms of the GNU Lesser General Public License as published by

'the Free Software Foundation, either version 3 of the License, or any later version.

'This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;

'without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

'See the GNU Lesser General Public License for more details.

'You should have received a copy of the GNU General Public License

'along with this module. If not, see <http://www.gnu.org/licenses/>.

Option Compare Database

Option Explicit

Public Function Validate_YearEntryBox(ctl As TextBox) As Boolean

'Validates the contents of a TextBox control on a form.

'ctl is a reference to the TextBox control.

'Returns True if the contents of the textbox can be validated as being a 4-digit year entry, False otherwise.

'Entries of less than 4 digits are padded out to match the current century (and decade) and then validated

'NOTE that if a single digit is entered, the value is padded with the current century AND decade:

' For example, if the current year is 2017, and a user enters "02" the year is padded to "2002"; if user enters "2" the year is padded to

"2012"

Dim s As String

Dim k As Long

Const sTitle = "Year Entry Validation" 'message box title

Validate_YearEntryBox = False 'presume not valid

If Nz(ctl, "") = "" Then 'is blank, not allowed

MsgBox prompt:="Year may not be blank!", buttons:=vbInformation, Title:=sTitle

Else

'something to process

If Not IsInteger(ctl) Then 'not an integer

MsgBox prompt:="You must enter an integer for the Year!", buttons:=vbInformation, Title:=sTitle

Else

'an integer to process

If Len(ctl) > 4 Then 'number too big

MsgBox prompt:="Too many digits for the Year!", buttons:=vbInformation, Title:=sTitle

Else

'4 or less digits, we can process this

If Len(ctl) < 4 Then 'add leading digits for this century

s = Format(Date, "yyyy")

k = Len(ctl)

Page 71: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 71 of 137

s = Left(s, Len(s) - k)

ctl = s & ctl

End If

Validate_YearEntryBox = True

End If

End If

End If

End Function

Public Function Validate_YearEntryComboBox(ctl As ComboBox) As Boolean

'Validates the contents of a ComboBox control on a form.

'ctl is a reference to the TextBox control.

'Returns True if the contents of the ComboBox can be validated as being a 4-digit year entry, False otherwise

'Entries of less than 4 digits are padded out to match the current century (and decade) and then validated

'NOTE that if a single digit is entered, the value is padded with the current century AND decade:

' For example, if the current year is 2017, and a user enters "02" the year is padded to "2002"; if user enters "2" the year is padded to

"2012"

Dim s As String

Dim k As Long

Const sTitle = "Year Entry Validation" 'message box title

Validate_YearEntryComboBox = False 'presume not valid

If Nz(ctl, "") = "" Then 'is blank, not allowed

MsgBox prompt:="Year may not be blank!", buttons:=vbInformation, Title:=sTitle

Else

'something to process

If Not IsInteger(ctl) Then 'not an integer

MsgBox prompt:="You must enter an integer for the Year!", buttons:=vbInformation, Title:=sTitle

Else

'an integer to process

If Len(ctl) > 4 Then 'number too big

MsgBox prompt:="Too many digits for the Year!", buttons:=vbInformation, Title:=sTitle

Else

'4 or less digits, we can process this

If Len(ctl) < 4 Then 'add leading digits for this century

s = Format(Date, "yyyy")

k = Len(ctl)

s = Left(s, Len(s) - k)

ctl = s & ctl

End If

Validate_YearEntryComboBox = True

End If

End If

End If

End Function

Public Function Validate_ComboBoxItem(ByRef LstCtl As ComboBox, lstCol As Integer, _

sItem As String, sUpdateField As String) As Boolean

'Validates data in a ComboBox control on a form, and adds a new item to the ComboBox's RowSource table.

'The ComboBox is presumed to have a table as its RowSource, and not be limited to list.

'Provides a dynamic way of adding entries to the ComboBox's RowSource table.

'LstCtl is a reference to a ComboBox control.

'lstCol is the column of the ComboBox's list being checked.

'sItem is the item being searched for.

'sUpdateField is the name of the field in the table that provides the ComboBox list's RowSource.

Page 72: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 72 of 137

'Searches the data list associated with LstCtl for sItem.

'If sItem is not found in the list, the user is asked to confirm adding the item to the list

'Returns False if user refuses the addition.

'If user accepts the addition, this function uses the ControlSource property to add the entry to the appropriate lookup table

'NOTE: sUpdateField is the name of the field in the RowSource table to update... there's no other way of knowing this field name.

'NOTE: remember that ComboBox data lists and columns use 0-based numbering

Dim rst As DAO.Recordset

If IsItemInComboBoxControl(LstCtl, lstCol, sItem) Then 'item is already in the list

Validate_ComboBoxItem = True

Else 'user must be asked whether to add the item

If MsgBox(prompt:=sItem & " is not in the lookup list!" & vbNewLine & vbNewLine & "Add it to the list? (You must add it to the list to have

your entry accepted.)", _

buttons:=vbQuestion + vbYesNo + vbDefaultButton2, Title:=CurrentProject.Name) = vbYes Then 'add the item

'confirmed - add entry to the lookup table and requery the list

Set rst = CurrentDb.OpenRecordset(LstCtl.RowSource)

With rst

.AddNew

.Fields(sUpdateField) = sItem

.Update

.Close

End With

Validate_ComboBoxItem = True

Else 'don't add the item

Validate_ComboBoxItem = False

End If

End If

End Function

Public Function IsItemInComboBoxControl(ByRef lst As ComboBox, lstCol As Integer, sItem As String) As Boolean

'Verifies that an item is in a ComboBox control's list.

'lst is a reference to a ComboBox control.

'lstCol specifies the column in which to look for the entry.

'sItem is the item to be searched for.

'Searches the data list associated with lst for sItem.

'Returns True if the item is found in the list, False otherwise

'NOTE: remember that ComboBox data lists and columns use 0-based numbering

Dim k As Long

IsItemInComboBoxControl = False 'presume the item is not found

With lst

k = 0

Do While k < .ListCount

If StrComp(.Column(lstCol, k), sItem, vbTextCompare) = 0 Then

k = .ListCount 'stop the loop

IsItemInComboBoxControl = True 'we found the item

Else

k = k + 1 'continue with next item

End If

Loop

End With

End Function

Public Function IsItemInListBoxControl(ByRef lst As ListBox, lstCol As Integer, sItem As String) As Boolean

'Verifies that an item is in a ListBox control's list.

Page 73: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 73 of 137

'lst is a reference to a ListBox control.

'lstCol specifies the column in which to look for the entry.

'sItem is the item to be searched for.

'Searches the data list associated with lst for sItem.

'Returns True if the item is found in the list, False otherwise

'NOTE: remember that ListBox data lists and columns use 0-based numbering

Dim k As Long

IsItemInListBoxControl = False 'presume the item is not found

With lst

k = 0

Do While k < .ListCount

If StrComp(.Column(lstCol, k), sItem, vbTextCompare) = 0 Then

k = .ListCount 'stop the loop

IsItemInListBoxControl = True 'we found the item

Else

k = k + 1 'continue with next item

End If

Loop

End With

End Function

Public Function FindFirst_NullRequiredField(ByRef frm As Form) As String

'Intended to be called from a FormError event, this procedure finds the first required field that is blank/null.

'frm is a reference to the form calling this procedure.

'Finds the first control in the referenced form whose Required property is true, and which is also blank/null

'Returns a string containing the contents of the control's Tag property (presumed to be a human-friendly field description)

' along with the actual control name. If the control's Tag property is empty, returns the ControlSource.

'The return string of this function is expected to be parsed by the caller to:

' a) set focus to the field name returned (after the "::")

' b) display a user-friendly message using the portion of the return string preceding the "::" mark.

' the string preceding the "::" mark will be either text retrieved from the control's Tag property

' or the name of the bound field (the ControlSource property).

'NOTE that only TextBox, ComboBox, and ListBox controls are evaluated.

'NOTE that the field must be set as Required at the table level.

'NOTE that unbound controls are not evaluated.

Dim ctl As control

Dim s As String

s = ""

For Each ctl In frm.Controls

'evaluate only text box, combo box, and list box controls

If (TypeName(ctl) = "ComboBox") Or (TypeName(ctl) = "TextBox") Or (TypeName(ctl) = "ListBox") Then

If Nz(ctl.ControlSource, "") <> "" Then 'control is bound to a recordsource

If Nz(ctl.Value, "") = "" Then 'control value is empty

If frm.Recordset.Fields(ctl.ControlSource).Required Then 'the field is required

If Nz(ctl.Tag, "") <> "" Then 'return control's Tag property if available

s = ctl.Tag & " :: " & ctl.Name

Else 'otherwise return control source

s = ctl.ControlSource & "::" & ctl.Name

End If

FindFirst_NullRequiredField = s

Exit Function 'no more work after finding one

End If

End If

Page 74: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 74 of 137

End If

End If

Next

FindFirst_NullRequiredField = s

End Function

Public Function FieldHasAwkwardChars(sTest As String) As Boolean

'Screens a string (presumably from user entry) for characters that can be problematic if

' inserted into a query or list control.

'check sTest to see if it contains any of the awkward chars: (,) (;) (#) (')

Dim k As Long

FieldHasAwkwardChars = False 'presume none of the forbidden chars

For k = 1 To Len(sTest)

If InStr(1, ",;#'", Mid(sTest, k, 1), vbBinaryCompare) > 0 Then

MsgBox prompt:="The symbols (,) (;) (') and (#) are not allowed in this field.", _

buttons:=vbExclamation, Title:=CurrentProject.Name

FieldHasAwkwardChars = True

Exit Function 'do no more work

End If

Next

End Function

Page 75: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 75 of 137

The LIBDATES Module This section contains the entirety of the libDates module. If you copy and paste this code into your own applications, you must keep the copyright and

license notices intact, per the GNU Lesser General Public License (pg. 128) and the GNU General Public License (pg. 130). This is not public domain software; it is licensed to you at no cost.

'COPYRIGHT NOTICE

'THIS VBA MODULE IS COPYRIGHT 2014-2017 BY MATTHEW S. HARRIS. All Rights Reserved.

'You can contact Matthew S. Harris at [email protected]

'Code Version: v2017-04

'COPYING PERMISSIONS

'The libDates module code is free software: you can redistribute it and/or modify

'it under the terms of the GNU Lesser General Public License as published by

'the Free Software Foundation, either version 3 of the License, or any later version.

'This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;

'without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

'See the GNU Lesser General Public License for more details.

'You should have received a copy of the GNU General Public License

'along with this module. If not, see <http://www.gnu.org/licenses/>.

Option Compare Database

Option Explicit

Private Const Err_BadControlArg = vbObjectError + 513 'invalid control type passed

Private Const Err_BadControlArg_msg = "Argument must be a ListBox or ComboBox control!"

Private Const Err_BadControlType = vbObjectError + 514 'list or combo box is not a Value List

Private Const Err_BadControlType_msg = "The ListBox or ComboBox is not a Value List!"

'

Public Function IsFutureDate(dtDate As Date) As Boolean

'tests to see if the date value in dtDate is later than today.

If dtDate > Date Then IsFutureDate = True Else IsFutureDate = False

End Function

Public Sub msg_NoFutureDates()

MsgBox prompt:="Dates later than today are not allowed!", _

buttons:=vbExclamation, _

Title:="Future Dates Not Permitted"

End Sub

Public Function IsPastDate(dtDate As Date) As Boolean

'test to see if the date value in dtDate is prior to today.

If dtDate < Date Then IsPastDate = True Else IsPastDate = False

End Function

Public Sub msg_NoPastDates()

MsgBox prompt:="Dates earlier than today are not allowed!", _

buttons:=vbExclamation, _

Title:="Past Dates Not Permitted"

End Sub

Public Sub ListFill_YrQuarters(ByRef objList As Object)

'fills a list object (list box or combo-box) with an ordered list of quarters

Page 76: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 76 of 137

'objList is expected to reference either a ListBox control or a ComboBox control

'If the control type is not a ListBox or ComboBox, a runtime error is raised.

'sets default value of the list to current quarter based on today's date

'NOTE that the list object is expected to be configured to have TWO columns, with the 1st column being the bound column

'RowSourceType must be Value List; if it is not, a runtime error is raised.

Const MyProcName = "ListFill_YrQuarters"

'validate objList argument

If Not ((TypeName(objList) = "ListBox") Or (TypeName(objList) = "ComboBox")) Then

Err.Raise Err_BadControlArg, MyProcName, MyProcName & ": " & Err_BadControlArg_msg

End If

If objList.RowSourceType <> "Value List" Then

Err.Raise Err_BadControlType, MyProcName, MyProcName & ": " & Err_BadControlType_msg

End If

With objList

.RowSource = "" 'clear the list

.AddItem "1;1st"

.AddItem "2;2nd"

.AddItem "3;3rd"

.AddItem "4;4th"

.Value = CInt(Format(Date, "q"))

End With

End Sub

Public Sub ListFill_YrMonths(ByRef objList As Object)

'fills a list object (list box or combo-box) with an ordered list of months

'objList is expected to reference either a ListBox control or a ComboBox control

'If the control type is not a ListBox or ComboBox, a runtime error is raised

'sets default value of the list to current month based on today's date

'NOTE that the list object is expected to be configured to have TWO columns, with the 1st column being the bound column

'RowSourceType must be Value List; if it is not, a runtime error is raised.

Const MyProcName = "ListFill_YrMonths"

'validate objList argument

If Not ((TypeName(objList) = "ListBox") Or (TypeName(objList) = "ComboBox")) Then

Err.Raise Err_BadControlArg, MyProcName, MyProcName & ": " & Err_BadControlArg_msg

End If

If objList.RowSourceType <> "Value List" Then

Err.Raise Err_BadControlType, MyProcName, MyProcName & ": " & Err_BadControlType_msg

End If

With objList

.RowSource = "" 'clear the list

.AddItem "1;January"

.AddItem "2;February"

.AddItem "3;March"

.AddItem "4;April"

.AddItem "5;May"

.AddItem "6;June"

.AddItem "7;July"

.AddItem "8;August"

.AddItem "9;September"

.AddItem "10;October"

.AddItem "11;November"

Page 77: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 77 of 137

.AddItem "12;December"

.Value = Month(Date)

End With

End Sub

Public Function NextQuarter(dtDate As Date) As String

'returns the next annual quarter after the date supplied in the dtDate argument

'the string returned is in the format "4Q17" for "4th Quarter, 2017"

Dim s As String

s = Format(dtDate, "q""Q""yy")

Select Case Mid(s, 1, 1)

Case "1": Mid(s, 1, 1) = "2"

Case "2": Mid(s, 1, 1) = "3"

Case "3": Mid(s, 1, 1) = "4"

Case "4"

Mid(s, 1, 1) = "1" 'wrap quarter around and increment year

Mid(s, Len(s) - 1, 2) = Format((CInt(Right(s, 2)) + 1), "00")

End Select

NextQuarter = s

End Function

Page 78: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 78 of 137

The LIBEXTERNALDB Module This section contains the entirety of the libExternalDB module. If you copy and paste this code into your own applications, you must keep the copyright

and license notices intact, per the GNU Lesser General Public License (pg. 128) and the GNU General Public License (pg. 130). This is not public domain software; it is licensed to you at no cost.

'COPYRIGHT NOTICE

'THIS VBA MODULE IS COPYRIGHT 2014-2017 BY MATTHEW S. HARRIS. All Rights Reserved.

'You can contact Matthew S. Harris at [email protected]

'Code Version: v2017-04

'COPYING PERMISSIONS

'The libExternalDB module code is free software: you can redistribute it and/or modify

'it under the terms of the GNU Lesser General Public License as published by

'the Free Software Foundation, either version 3 of the License, or any later version.

'This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;

'without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

'See the GNU Lesser General Public License for more details.

'You should have received a copy of the GNU General Public License

'along with this module. If not, see <http://www.gnu.org/licenses/>.

Option Compare Database

Option Explicit

Public Sub Create_UserDefined_DB(sDBName As String)

'Intended to provide a means of creating a database with tables linked to the current database

' so that users may create custom reports and queries not provided by the current application.

'Creates an empty database, and then links to it all the tables from current database's TableDefs collection

'sDBName is the name of the new database file; it will be stored in the same folder as the current project.

'sDBName may omit the file extension, it will be supplied automatically.

' If you include a file extension, that extension is used

'NOTE: DO NOT include a full path in sDBName, this procedure defines the path. Supply only a file name and extension.

'NOTE: If you supply a file extension other than .accdb, you may not be able to open the new database file.

'NOTE: using the .accdb file extension is recommended.

Dim TargetName As String

Dim dbx As DAO.Database

Dim tblDefx As DAO.TableDef

Dim tblDefLocal As DAO.TableDef

Dim s As String

TargetName = CurrentProject.Path & "\" & sDBName 'store new database in current project's folder

'does the proposed new database file already exist?

If FileExists(TargetName) Then 'refuse to over-write

MsgBox prompt:=TargetName & " already exists. " & vbNewLine & vbNewLine & _

"If you want to make another linked copy of this database, you must " & _

"first rename, move, or delete " & TargetName, _

buttons:=vbInformation, _

Title:=CurrentProject.Name

Exit Sub 'do no work

End If

On Error GoTo CreationError

Page 79: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 79 of 137

Set dbx = DBEngine.CreateDatabase(TargetName, dbLangGeneral) 'create the new database

'iterate the TableDefs collection

For Each tblDefLocal In CurrentDb.TableDefs

'exclude all MSys tables, exclude all temp tables (name begins with "~")

If (Mid(tblDefLocal.Name, 1, 4) <> "MSys") And (Mid(tblDefLocal.Name, 1, 1) <> "~") Then

Set tblDefx = dbx.CreateTableDef(tblDefLocal.Name) 'create table definition in external db

If tblDefLocal.Connect <> "" Then

'NEED TO VALIDATE THE CONNECT STRING BEFORE ATTEMPTING TO COPY, OR WE GET A RUNTIME ERROR

s = Replace(tblDefLocal.Connect, ";DATABASE=", "")

If libFiles.FileExists(s) Then 'the link source exists

tblDefx.Connect = tblDefLocal.Connect 'linked table in local database, copy the connect string

Else

'ask user whether to skip this one table, or cancel entire operation

If MsgBox(prompt:="Cannot create linked table because the link source (" & s & ") does not exist!" & vbNewLine & vbNewLine & _

"Click OK to skip this table and continue, or click Cancel to halt the operation.", _

buttons:=vbExclamation + vbOKCancel, Title:="Create User Defined DB") = vbcancel Then

dbx.Close

If libFiles.FileExists(TargetName) Then Kill TargetName 'delete any incomplete copy

Exit Sub

Else

GoTo BottomOfLoop 'don't complete this iteration of loop, go on to next item

End If

End If

Else 'user chose to skip this table

tblDefx.Connect = ";DATABASE=" & CurrentDb.Name 'local table - create a connect string

End If

tblDefx.SourceTableName = tblDefLocal.Name 'name the new table def

dbx.TableDefs.Append tblDefx 'add tabledef to the new db

End If

BottomOfLoop:

Next

dbx.Close 'close the new database

Set dbx = Nothing 'dispose pointer

MsgBox prompt:=TargetName & " created successfully!", _

buttons:=vbInformation, _

Title:=CurrentProject.Name

Exit Sub 'skip error handling code

CreationError:

dbx.Close 'close the db copy

If libFiles.FileExists(TargetName) Then Kill TargetName 'delete any incomplete copy

MsgBox prompt:="Unable to Create New Database!" & vbNewLine & vbNewLine & Err.Number & ": " & Err.Description & _

vbNewLine & "New Database name: " & TargetName, _

buttons:=vbCritical + vbOKOnly, Title:="Procedure 'Create User Defined DB'"

End Sub

Public Function Report_Get_List(sDBName As String) As Variant

'Returns an array containing a list of all report objects in an external database.

'sDBName is the name and full path of the external database.

Dim dbx As DAO.Database

Dim rpt As DAO.Document

Dim rptList() As String

Page 80: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 80 of 137

Dim k As Long

Report_Get_List = "" 'presume failure

'ensure that the db file exists

If Not FileExists(sDBName) Then

MsgBox prompt:=sDBName & " does not exist!", _

buttons:=vbCritical + vbOKOnly, Title:=CurrentProject.Name

Exit Function

End If

Set dbx = DBEngine.OpenDatabase(sDBName) 'open the external db

On Error GoTo JobDone 'the following code produces a runtime error if the external DB has never had a report in it

'apparently, the Reports container does not exist until at least one report has been created

If dbx.Containers("Reports").Documents.Count = 0 Then GoTo JobDone 'if no reports in the user-defined, do no work

ReDim rptList(dbx.Containers("Reports").Documents.Count - 1)

'iterate Reports collection and fill the list

k = 0

For Each rpt In dbx.Containers("Reports").Documents

rptList(k) = rpt.Name

k = k + 1

Next rpt

Report_Get_List = rptList

JobDone:

dbx.Close

Report_Get_List = ""

End Function

Public Sub Report_Open(sDBName As String, sReportName As String)

'opens a specified report in an external database

'to show the report, it is necessary to create a new instance of MS Access

'sDBName is the filename and full path of the external database

'sReportName is the name of the report to be opened

Dim objAccess As Access.Application

'open another access instance

Set objAccess = GetObject(sDBName)

objAccess.DoCmd.OpenReport sReportName, acViewPreview

objAccess.visible = True

objAccess.DoCmd.Maximize

Set objAccess = Nothing

End Sub

Public Function Form_Get_List(sDBName As String) As Variant

'Returns an array containing a list of all form objects in an external database.

'sDBName is the name and full path of the external database.

Dim dbx As DAO.Database

Dim frm As DAO.Document

Dim frmList() As String

Dim k As Long

Page 81: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 81 of 137

Form_Get_List = "" 'presume failure

'ensure that the db file exists

If Not FileExists(sDBName) Then

MsgBox prompt:=sDBName & " does not exist!", _

buttons:=vbCritical + vbOKOnly, Title:=CurrentProject.Name

Exit Function

End If

Set dbx = DBEngine.OpenDatabase(sDBName) 'open the external db

On Error GoTo JobDone 'the following code produces a runtime error if the external DB has never had a form in it

'apparently, the Forms container does not exist until at least one report has been created

If dbx.Containers("Forms").Documents.Count = 0 Then GoTo JobDone 'if no forms in the user-defined, do no work

ReDim frmList(dbx.Containers("Forms").Documents.Count - 1)

'iterate Forms collection and fill the list

k = 0

For Each frm In dbx.Containers("Forms").Documents

frmList(k) = frm.Name

k = k + 1

Next frm

Form_Get_List = frmList

JobDone:

dbx.Close

Form_Get_List = ""

End Function

Public Sub Form_Open(sDBName As String, sFormName As String)

'opens a specified form in an external database

'to show the form, it is necessary to create a new instance of MS Access

'sDBName is the filename and full path of the external database

'sFormName is the name of the form to be opened

Dim objAccess As Access.Application

'open another access instance

Set objAccess = GetObject(sDBName)

objAccess.DoCmd.OpenForm sFormName

objAccess.visible = True

objAccess.DoCmd.Maximize

Set objAccess = Nothing

End Sub

Page 82: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 82 of 137

The LIBLISTBOXES Module This section contains the entirety of the libListBoxes module. If you copy and paste this code into your own applications, you must keep the copyright

and license notices intact, per the GNU Lesser General Public License (pg. 128) and the GNU General Public License (pg. 130). This is not public domain software; it is licensed to you at no cost.

'COPYRIGHT NOTICE

'THIS VBA MODULE IS COPYRIGHT 2014-2017 BY MATTHEW S. HARRIS. All Rights Reserved.

'You can contact Matthew S. Harris at [email protected]

'Code Version: v2017-04

'COPYING PERMISSIONS

'The libListBoxes module code is free software: you can redistribute it and/or modify

'it under the terms of the GNU Lesser General Public License as published by

'the Free Software Foundation, either version 3 of the License, or any later version.

'This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;

'without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

'See the GNU Lesser General Public License for more details.

'You should have received a copy of the GNU General Public License

'along with this module. If not, see <http://www.gnu.org/licenses/>.

Option Compare Database

Option Explicit

Private Const Err_ArgIsNotValueList = vbObjectError + 513

Private Const Err_ArgIsNotValueList_msg = "ListBox argument is not a Value List."

'

Public Sub ListBox_Selection_Clear(ByRef xListBox As ListBox)

'clear all selections in the specified list box control

Dim k As Long

With xListBox

For k = .ListCount - 1 To 0 Step -1

.Selected(k) = False

Next k

End With

End Sub

Public Sub ListBox_Selection_SelectAll(ByRef xListBox As ListBox)

'select all items in the specified list box control

'only selects all items if list is configured as multi-select

Dim k As Long

With xListBox

For k = .ListCount - 1 To 0 Step -1

.Selected(k) = True

Next k

End With

End Sub

Public Sub ListBox_Selection_Invert(ByRef xListBox As ListBox)

'invert the selections in the specified list box control

'only inverts selection if list is configured as multi-select

Page 83: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 83 of 137

Dim k As Long

With xListBox

For k = 0 To .ListCount - 1

.Selected(k) = Not .Selected(k)

Next

End With

End Sub

Public Sub ListBox_MoveItemDown(ByRef xListBox As ListBox)

'move the currently selected item in the referenced ListBox control down in the list order

'NOTE: xListBox must be a value list, or a runtime error will occur

'NOTE: this code will handle a ListBox with or without multi-select enabled

'REMINDER: if a list is multi-select, one must use the ItemsSelected collection to determine which items in the list are selected.

Dim tIndex As Integer

Dim sItem As String

Const MyProcName = "ListBox_MoveItemDown"

With xListBox

If .RowSourceType <> "Value List" Then 'raise runtime error if the list is not a Value List

Err.Raise Err_ArgIsNotValueList, MyProcName, MyProcName & ": " & Err_ArgIsNotValueList_msg

End If

If .ItemsSelected.Count < 1 Then Exit Sub 'can't move anything if nothing is selected

If .ItemsSelected.Count > 1 Then

msg_OnlyOneSelectedItemCanBeMoved 'can only move one item at a time

Exit Sub

End If

tIndex = .ItemsSelected.Item(0) 'get index of first selected item; if list is multi-select ListIndex is not useful

'the ListIndex property won't be reliable

If tIndex < (.ListCount - 1) Then 'can only move down an item other than the last (0-based indexing)

sItem = .ItemData(tIndex) 'preserve the item we're moving

.RemoveItem tIndex 'remove it

.AddItem sItem, tIndex + 1 're-insert it at new index

.Selected(tIndex + 1) = True 'select item just moved; if this is NOT multi-select, the ListIndex will update properly

End If

End With

End Sub

Public Sub ListBox_MoveItemUp(ByRef xListBox As ListBox)

'move the currently selected item in the referenced ListBox control up in the list order

'NOTE: xListBox must be a value list, or a runtime error will occur

'NOTE: this code will handle a ListBox with or without multi-select enabled

'REMINDER: if a list is multi-select, one must use the ItemsSelected collection to determine which items in the list are selected.

Dim tIndex As Integer

Dim sItem As String

Const MyProcName = "ListBox_MoveItemUp"

With xListBox

If .RowSourceType <> "Value List" Then 'raise runtime error if the list is not a Value List

Err.Raise Err_ArgIsNotValueList, MyProcName, MyProcName & ": " & Err_ArgIsNotValueList_msg

End If

If .ItemsSelected.Count < 1 Then Exit Sub 'can't move anything if nothing is selected

If .ItemsSelected.Count > 1 Then

msg_OnlyOneSelectedItemCanBeMoved 'can only move one item at a time

Page 84: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 84 of 137

Exit Sub

End If

tIndex = .ItemsSelected.Item(0) 'get index of first selected item; if list is multi-select ListIndex is not useful

'the ListIndex property won't be reliable

If tIndex > 0 Then 'can only move up an item other than the first

sItem = .ItemData(tIndex) 'preserve the item we're moving

.RemoveItem tIndex 'remove it

.AddItem sItem, tIndex - 1 're-insert it at new index

.Selected(tIndex - 1) = True 'select item just moved; if this is NOT multi-select, the ListIndex will update properly

End If

End With

End Sub

Private Sub msg_OnlyOneSelectedItemCanBeMoved()

'display message explaining that only one selected item can be moved up or down in a list

MsgBox prompt:="Only one selected item may be moved up or down in the list at one time." & vbNewLine & _

"Select only one item and try again.", _

buttons:=vbInformation + vbOKOnly, Title:="Move Item In List"

End Sub

Page 85: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 85 of 137

The LIBOPENARGS Module This section contains the entirety of the libOpenArgs module. If you copy and paste this code into your own applications, you must keep the copyright and

license notices intact, per the GNU Lesser General Public License (pg. 128) and the GNU General Public License (pg. 130). This is not public domain software; it is licensed to you at no cost.

'COPYRIGHT NOTICE

'THIS VBA MODULE IS COPYRIGHT 2014-2017 BY MATTHEW S. HARRIS. All Rights Reserved.

'You can contact Matthew S. Harris at [email protected]

'Code Version: v2017-04

'COPYING PERMISSIONS

'The libOpenArgs module code is free software: you can redistribute it and/or modify

'it under the terms of the GNU Lesser General Public License as published by

'the Free Software Foundation, either version 3 of the License, or any later version.

'This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;

'without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

'See the GNU Lesser General Public License for more details.

'You should have received a copy of the GNU General Public License

'along with this module. If not, see <http://www.gnu.org/licenses/>.

Option Compare Database

Option Explicit

Public Function OpenArgs_Build(ParamArray sArgList()) As String

'build a string to be passed as the value of the OpenArgs argument for the Access DoCmd.OpenForm and DoCmd.OpenReport methods

'sArgList is an array consisting of an argument name followed by an argument value

'the pairs of argument name and argument value are assembled into a single string where each argument is formatted as:

' <argname>:=<argvalue>[vbCr]

'ALL argument name/value pairs must be terminated with a vbCr character.

'This procedure raises a runtime error if:

' sArgList is an empty array

' the number of elements in sArgList is not even (that is, arguments must be in pairs of <argname> followed by the <argvalue>

Dim k As Long

Dim s As String

Const MyProcName = "OpenArgs_Build"

If UBound(sArgList) = -1 Then 'empty argument list

Err.Raise vbObjectError + 513, MyProcName, MyProcName & ": Argument List is Empty."

End If

If ((UBound(sArgList) + 1) Mod 2) <> 0 Then 'argument list is not even pairs (remember that the paramarray will be a 0-based array)

Err.Raise vbObjectError + 514, MyProcName, MyProcName & ": Argument list does not contain an even number of argument/value pairs."

End If

s = ""

For k = LBound(sArgList) To UBound(sArgList) Step 2

s = s & Trim(sArgList(k)) & ":=" & Trim(sArgList(k + 1)) & vbCr

Next

OpenArgs_Build = s

End Function

Public Function OpenArgs_GetArgument(sOpenArgs As String, ByVal sArgName As String) As String

'sOpenArgs is the complete OpenArgs string from a form or report, expected to have been created with the OpenArgs_Build function

Page 86: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 86 of 137

'sArgName is the name of the argument we are trying to parse

'the OpenArgs string is presumed to be formatted with each argument prefaced by sArgName, and terminated with a vbCr

'e.g. - "ArgName:=ArgumentText[vbCR]" -- all arguments, even the last one in the string, must have the vbCr terminator

'NOTE: returns only the argument value, not the argument name

'NOTE: all return values are strings, the calling code must convert strings to numeric values as needed

'NOTE: an empty string is returned if the specified argument name is not found in sOpenArgs. It is up to the calling

' code to test whether the return string is empty

Dim s As String

Dim k As Long

Dim k1 As Long

sArgName = Trim(sArgName) & ":="

k = InStr(1, sOpenArgs, sArgName, vbTextCompare)

If k > 0 Then

k1 = InStr(k, sOpenArgs, vbCr)

s = Mid(sOpenArgs, k, k1 - k)

s = Replace(s, sArgName, "")

Else

s = ""

End If

OpenArgs_GetArgument = s

End Function

Page 87: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 87 of 137

The LIBFILES Module This section contains the entirety of the libFiles module. If you copy and paste this code into your own applications, you must keep the copyright and

license notices intact, per the GNU Lesser General Public License (pg. 128) and the GNU General Public License (pg. 130). This is not public domain software; it is licensed to you at no cost.

'COPYRIGHT NOTICE

'THIS VBA MODULE IS COPYRIGHT 2014-17 BY MATTHEW S. HARRIS. All Rights Reserved.

'You can contact Matthew S. Harris at [email protected]

'Code Version: v2017-04

'COPYING PERMISSIONS

'The libFiles module code is free software: you can redistribute it and/or modify

'it under the terms of the GNU Lesser General Public License as published by

'the Free Software Foundation, either version 3 of the License, or any later version.

'This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;

'without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

'See the GNU Lesser General Public License for more details.

'You should have received a copy of the GNU General Public License

'along with this module. If not, see <http://www.gnu.org/licenses/>.

Option Compare Database

Option Explicit

Public Function FileExists(sFileName As String) As Boolean

'return true if the specified file exists

'sFileName is presumed to be a file name and a fully qualified path

'NOTE that this function only finds "Normal" files, i.e. it will not find system or hidden files

Dim strResult As String

strResult = Dir(sFileName, vbNormal)

If strResult <> "" Then

FileExists = True

Else

FileExists = False

End If

End Function

Public Function FolderExists(sFolderName As String) As Boolean

'return True if the specified folder exists

'sFolderName is presumed to be a fully qualified path, without a filename

'NOTE that this function only finds directory (folder) items; it will not find system or hidden folders

Dim strResult As String

If Trim(sFolderName) = "" Then

FolderExists = False

Else

strResult = Dir(sFolderName, vbDirectory)

If strResult <> "" Then

FolderExists = True

Else

FolderExists = False

Page 88: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 88 of 137

End If

End If

End Function

Public Function FileName_FromFullPath(sFullPath As String) As String

'Returns a string containing just the file name from a fully qualified path plus file name

Dim sFileName As String

Dim k As Long

k = Len(sFullPath)

Do While k > 0

If Mid(sFullPath, k, 1) = "\" Then

k = 0 'stop the loop

Else

sFileName = Mid(sFullPath, k, 1) & sFileName

k = k - 1

End If

Loop

FileName_FromFullPath = sFileName

End Function

Public Function PathName_FromFullPath(sFullPath As String) As String

'Returns a string containing just the path portion of a fully qualified path plus file name

Dim sPathName As String

Dim k As Long

k = Len(sFullPath)

Do While k > 0

If Mid(sFullPath, k, 1) = "\" Then

sPathName = Mid(sFullPath, 1, k)

k = 0 'stop the loop

Else

k = k - 1

End If

Loop

PathName_FromFullPath = sPathName

End Function

Public Function FileExtension_FromFullPath(sFullPath As String) As String

'Returns a string containing the file extension (e.g. .txt, .csv, .accdb, .xlsx and so on) extracted from sFullPath

'Returns an empty string if no file extension is found.

Dim sExtension As String

Dim k As Long

Dim bHasExtension As Boolean

bHasExtension = False 'presume there is no file extension

k = Len(sFullPath)

Do While k > 0

If Mid(sFullPath, k, 1) = "." Then

sExtension = "." & sExtension

bHasExtension = True

k = 0 'stop the loop

Else

sExtension = Mid(sFullPath, k, 1) & sExtension

k = k - 1

Page 89: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 89 of 137

End If

Loop

If Not bHasExtension Then sExtension = vbNullString 'if extension not found, return empty string

FileExtension_FromFullPath = sExtension

End Function

Public Function Get_FileDateCreated(sFileName As String) As Date

'returns a date value showing the date/time a specified file was created.

'Uses objects from the Microsoft Scripting Runtime library. This code is written to avoid having to set a reference to this library.

'sFileName is the fully-qualified path and filename to get the creation date for.

'If the specified file does not exist, or cannot be accessed, the function returns -1

Dim objFS As Object 'Scripting.FileSystemObject

Dim objFile As Object 'Scripting.File

On Error GoTo BadStuff

Get_FileDateCreated = -1 'assume file does not exist or some other problem occurs; -1 indicates failure

If Not FileExists(sFileName) Then Exit Function 'bail out if the file does not exist

Set objFS = CreateObject("Scripting.FileSystemObject") 'create a FileSystemObject reference

Set objFile = objFS.GetFile(sFileName) 'set a reference to the file object

Get_FileDateCreated = objFile.DateCreated

Set objFS = Nothing 'dispose our objects

Set objFile = Nothing

Exit Function 'skip error-handling code

BadStuff:

'report appropriate error messages for the error that occurred

MsgBox prompt:="Error Number: " & Err.Number & vbNewLine & "Description: " & Err.Description & vbNewLine, _

buttons:=vbCritical + vbOKOnly, Title:="Get File Date Created"

End Function

Public Function Get_FileDateLastAccessed(sFileName As String) As Date

'returns a date value showing the date/time a specified file was last accessed.

'Uses objects from the Microsoft Scripting Runtime library. This code is written to avoid having to set a reference to this library.

'sFileName is the fully-qualified path and filename to get the creation date for.

'If the specified file does not exist, or cannot be accessed, the function returns -1

Dim objFS As Object 'Scripting.FileSystemObject

Dim objFile As Object 'Scripting.File

On Error GoTo BadStuff

Get_FileDateLastAccessed = -1 'assume file does not exist or some other problem occurs; -1 indicates failure

If Not FileExists(sFileName) Then Exit Function 'bail out if the file does not exist

Set objFS = CreateObject("Scripting.FileSystemObject") 'create a FileSystemObject reference

Set objFile = objFS.GetFile(sFileName) 'set a reference to the file object

Page 90: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 90 of 137

Get_FileDateLastAccessed = objFile.DateLastAccessed

Set objFS = Nothing 'dispose our objects

Set objFile = Nothing

Exit Function 'skip error-handling code

BadStuff:

'report appropriate error messages for the error that occurred

MsgBox prompt:="Error Number: " & Err.Number & vbNewLine & "Description: " & Err.Description & vbNewLine, _

buttons:=vbCritical + vbOKOnly, Title:="Get File Date Last Accessed"

End Function

Public Function Get_FileDateLastModified(sFileName As String) As Date

'returns a date value showing the date/time a specified file was last modified.

'Uses objects from the Microsoft Scripting Runtime library. This code is written to avoid having to set a reference to this library.

'sFileName is the fully-qualified path and filename to get the creation date for.

'If the specified file does not exist, or cannot be accessed, the function returns -1

Dim objFS As Object 'Scripting.FileSystemObject

Dim objFile As Object 'Scripting.File

On Error GoTo BadStuff

Get_FileDateLastModified = -1 'assume file does not exist or some other problem occurs; -1 indicates failure

If Not FileExists(sFileName) Then Exit Function 'bail out if the file does not exist

Set objFS = CreateObject("Scripting.FileSystemObject") 'create a FileSystemObject reference

Set objFile = objFS.GetFile(sFileName) 'set a reference to the file object

Get_FileDateLastModified = objFile.DateLastModified

Set objFS = Nothing 'dispose our objects

Set objFile = Nothing

Exit Function 'skip error-handling code

BadStuff:

'report appropriate error messages for the error that occurred

MsgBox prompt:="Error Number: " & Err.Number & vbNewLine & "Description: " & Err.Description & vbNewLine, _

buttons:=vbCritical + vbOKOnly, Title:="Get File Date Last Modified"

End Function

Public Function GetFileSize(sFileName As String) As Double

'returns, in bytes, the size of the file specified by sFileName.

'Uses objects from the Microsoft Scripting Runtime library. This code is written to avoid having to set a reference to this library.

'sFileName is the fully-qualified path and filename to get the size of.

'If the specified file does not exist, or cannot be accessed, the function returns -1

Dim objFS As Object 'Scripting.FileSystemObject

Dim objFile As Object 'Scripting.File

On Error GoTo BadStuff

GetFileSize = -1 'assume file does not exist or some other problem occurs; -1 indicates failure

Page 91: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 91 of 137

If Not FileExists(sFileName) Then Exit Function 'bail out if the file does not exist

Set objFS = CreateObject("Scripting.FileSystemObject") 'create a FileSystemObject reference

Set objFile = objFS.GetFile(sFileName) 'set a reference to the file object

GetFileSize = objFile.Size

Set objFS = Nothing 'dispose our objects

Set objFile = Nothing

Exit Function 'skip error-handling code

BadStuff:

'report appropriate error messages for the error that occurred

MsgBox prompt:="Error Number: " & Err.Number & vbNewLine & "Description: " & Err.Description & vbNewLine, _

buttons:=vbCritical + vbOKOnly, Title:="Get File Size"

End Function

Public Function Make_Unique_FileName(ByVal sFileName As String) As String

'Returns a string containing a unique filename, obtained by adding a numeric suffix to the file name

'The returned file name includes the same full path found in sFileName

'The numeric suffix starts at 0.

'sFileName is expected to contain a file name (with extension) and a fully qualified path

'sFileExt is expected to contain the desired file name extension

'To add the numeric suffix to the file name, the extension must be stripped and re-added

'The file name is separated from the full path in case there is no file extension and a "." character

' occurs somewhere in the path

Dim sPath As String

Dim sFileExt As String

Dim s As String

Dim k As Long

'pop the file name and extension out of the fully qualified path and save the path information

sPath = PathName_FromFullPath(sFileName)

sFileName = FileName_FromFullPath(sFileName)

sFileExt = FileExtension_FromFullPath(sFileName)

'strip file extension from sFileName

k = Len(sFileName)

sFileName = Mid(sFileName, 1, Len(sFileName) - Len(sFileExt))

'check output filename for uniqueness and adjust as needed

k = 1

s = sFileName

Do While FileExists(sPath & s & sFileExt)

s = sFileName & " " & Format(k, "0#")

k = k + 1

Loop

Make_Unique_FileName = sPath & s & sFileExt

End Function

Public Function DriveFreeSpace(ByVal DrvLetter As String) As Double

'returns, in bytes, the amount of free space on a specified drive.

'Uses objects from the Microsoft Scripting Runtime library. This code is written to avoid having to set a reference to this library.

'DrvLetter is the letter of the drive from which to get the free space value; only the first letter of any string passed is used.

'If the specified drive letter does not exist, or cannot be accessed, the function returns -1

'If the specified drive is not ready, the function returns -2

Page 92: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 92 of 137

'NOTE that when using this function with read-only CD and DVD drives, the free space value returned is always 0, and will

' not match the value for free space shown in This PC in Windows.

Dim objFS As Object 'Scripting.FileSystemObject

Dim objDrv As Object 'Scripting.FileSystemObject.GetDriveName

On Error GoTo BadStuff

DriveFreeSpace = -1 'assume drive does not exist or some other problem occurs; -1 indicates failure

DrvLetter = Left(DrvLetter, 1) & ":\" 'only using the first letter of the DrvLetter argument

If Not DriveExists(DrvLetter) Then Exit Function 'bail out if the drive does not exist

Set objFS = CreateObject("Scripting.FileSystemObject") 'create a FileSystemObject reference

Set objDrv = objFS.GetDrive(objFS.GetDriveName(DrvLetter)) 'set a reference to the drive

If objDrv.IsReady Then

DriveFreeSpace = objDrv.FreeSpace 'return the amount of free space

Else

DriveFreeSpace = -2

End If

Set objFS = Nothing 'dispose our objects

Set objDrv = Nothing

Exit Function 'skip error-handling code

BadStuff:

'report appropriate error messages for the error that occurred

MsgBox prompt:="Error Number: " & Err.Number & vbNewLine & "Description: " & Err.Description & vbNewLine, _

buttons:=vbCritical + vbOKOnly, Title:="Drive Free Space"

End Function

Public Function DriveAvailableSpace(ByVal DrvLetter As String) As Double

'returns, in bytes, the amount of space on a specified drive available to the current user (which may be different from total free space,

' if the computer has disk usage quotas set up).

'Uses objects from the Microsoft Scripting Runtime library. This code is written to avoid having to set a reference to this library.

'DrvLetter is the letter of the drive from which to get the total space value; only the first letter of any string passed is used.

'If the specified drive letter does not exist, or cannot be accessed, the function returns -1

'If the specified drive is not ready, the function returns -2

'NOTE that when using this function with read-only CD and DVD drives, the total space value returned is the amount of used space on the

CD/DVD,

' and will not match the value for total space shown in This PC in Windows.

Dim objFS As Object 'Scripting.FileSystemObject

Dim objDrv As Object 'Scripting.FileSystemObject.GetDriveName

On Error GoTo BadStuff

DriveAvailableSpace = -1 'assume drive does not exist or some other problem occurs; -1 indicates failure

DrvLetter = Left(DrvLetter, 1) & ":\" 'only using the first letter of the DrvLetter argument

If Not DriveExists(DrvLetter) Then Exit Function 'bail out if the drive does not exist

Set objFS = CreateObject("Scripting.FileSystemObject") 'create a FileSystemObject reference

Page 93: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 93 of 137

Set objDrv = objFS.GetDrive(objFS.GetDriveName(DrvLetter)) 'set a reference to the drive

If objDrv.IsReady Then

DriveAvailableSpace = objDrv.AvailableSpace 'return the amount of available space

Else

DriveAvailableSpace = -2

End If

Set objFS = Nothing 'dispose our objects

Set objDrv = Nothing

Exit Function 'skip error-handling code

BadStuff:

'report appropriate error messages for the error that occurred

MsgBox prompt:="Error Number: " & Err.Number & vbNewLine & "Description: " & Err.Description & vbNewLine, _

buttons:=vbCritical + vbOKOnly, Title:="Drive Available Space"

End Function

Public Function DriveTotalSpace(ByVal DrvLetter As String) As Double

'returns, in bytes, the total amount of space on a specified drive.

'Uses objects from the Microsoft Scripting Runtime library. This code is written to avoid having to set a reference to this library.

'DrvLetter is the letter of the drive from which to get the total space value; only the first letter of any string passed is used.

'If the specified drive letter does not exist, or cannot be accessed, the function returns -1

'If the specified drive is not ready, the function returns -2

'NOTE that when using this function with read-only CD and DVD drives, the total space value returned is the amount of used space on the

CD/DVD,

' and will not match the value for total space shown in This PC in Windows.

Dim objFS As Object 'Scripting.FileSystemObject

Dim objDrv As Object 'Scripting.FileSystemObject.GetDriveName

On Error GoTo BadStuff

DriveTotalSpace = -1 'assume drive does not exist or some other problem occurs; -1 indicates failure

DrvLetter = Left(DrvLetter, 1) & ":\" 'only using the first letter of the DrvLetter argument

If Not DriveExists(DrvLetter) Then Exit Function 'bail out if the drive does not exist

Set objFS = CreateObject("Scripting.FileSystemObject") 'create a FileSystemObject reference

Set objDrv = objFS.GetDrive(objFS.GetDriveName(DrvLetter)) 'set a reference to the drive

If objDrv.IsReady Then

DriveTotalSpace = objDrv.TotalSize 'return the amount of total space

Else

DriveTotalSpace = -2

End If

Set objFS = Nothing 'dispose our objects

Set objDrv = Nothing

Exit Function 'skip error-handling code

BadStuff:

'report appropriate error messages for the error that occurred

MsgBox prompt:="Error Number: " & Err.Number & vbNewLine & "Description: " & Err.Description & vbNewLine, _

Page 94: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 94 of 137

buttons:=vbCritical + vbOKOnly, Title:="Drive Total Space"

End Function

Public Function DriveExists(ByVal DrvLetter As String) As Boolean

'returns True if the specified drive letter is found in the Drives collection of the FileSystemObject.

'Uses objects from the Microsoft Scripting Runtime library. This code is written to avoid having to set a reference to this library.

'DrvLetter is the letter of the drive whose existence will be tested; only the first letter of any string passed is used.

Dim objFS As Object 'Scripting.FileSystemObject

Dim objDrvs As Object 'Scripting.FileSystemObject.Drives

Dim obj As Object

DriveExists = False 'assume drive does not exist

DrvLetter = UCase(Left(DrvLetter, 1)) & ":"

Set objFS = CreateObject("Scripting.FileSystemObject") 'create a FileSystemObject reference

Set objDrvs = objFS.Drives 'set a reference to the Drives collection

For Each obj In objDrvs

If DrvLetter = obj Then

DriveExists = True

Exit For 'found item, no more looping

End If

Next

Set objFS = Nothing 'dispose objects

Set objDrvs = Nothing

End Function

Public Function Get_DriveList() As String()

'returns an array of drive letters, containing all of the drives currently connected to the computer.

'Uses objects from the Microsoft Scripting Runtime library. This code is written to avoid having to set a reference to this library.

Dim objFS As Object 'Scripting.FileSystemObject

Dim objDrvs As Object 'Scripting.FileSystemObject.Drives

Dim obj As Object

Dim DrvList() As String

Dim k As Long

Set objFS = CreateObject("Scripting.FileSystemObject") 'create a FileSystemObject reference

Set objDrvs = objFS.Drives 'set a reference to the Drives collection

ReDim DrvList(0 To objDrvs.Count - 1)

k = 0

For Each obj In objDrvs

DrvList(k) = obj

k = k + 1

Next

Get_DriveList = DrvList

Set objFS = Nothing 'dispose objects

Set objDrvs = Nothing

End Function

Page 95: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 95 of 137

Public Function Bytes2KB(dblBytes As Double) As Double

'converts a value given in Bytes to a value in Kilobytes (KB)

Bytes2KB = dblBytes / 1024

End Function

Public Function Bytes2MB(dblBytes As Double) As Double

'converts a value given in Bytes to a value in Megabytes (MB)

Bytes2MB = (dblBytes / 1024) / 1024

End Function

Public Function Bytes2GB(dblBytes As Double) As Double

'converts a value given in Bytes to a value in Gigabytes (GB)

Bytes2GB = ((dblBytes / 1024) / 1024) / 1024

End Function

Public Function Bytes2String(dblBytes As Double, Optional intDecPlaces As Integer = 0) As String

'returns a string in the format x MB, x KB, x GB; where x is the value of dblBytes automatically scaled to a larger unit.

'the scale for the new value is chosen according to these rules:

' if dblBytes is less than 1 MB, it is scaled to KB

' if dblBytes is greater than or equal to 1 MB and less than 1GB, it is scaled to MB

' if dblBytes is greater than or equal to 1 GB, it is scaled to GB

'intDecPlaces optionally specifies the number of decimal places to include in the result; it defaults to 0

Dim dblKB As Double

Dim dblMB As Double

Dim dblGB As Double

dblKB = 1 * 1024

dblMB = dblKB * 1024

dblGB = dblMB * 1024

If intDecPlaces < 0 Then intDecPlaces = 0 'sanity check, can't have negative decimal places

If dblBytes < dblMB Then 'less than 1 MB, scale to KB

Bytes2String = Round((dblBytes / dblKB), intDecPlaces) & " Kb"

ElseIf (dblBytes >= dblMB) And (dblBytes < dblGB) Then '>= 1 MB and < 1 GB, scale to MB

Bytes2String = Round((dblBytes / dblMB), intDecPlaces) & " Mb"

ElseIf (dblBytes >= dblGB) Then 'greater than 1 GB, scale to GB

Bytes2String = Round((dblBytes / dblGB), intDecPlaces) & " Gb"

End If

End Function

'

Page 96: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 96 of 137

The LIBNUMERIC Module This section contains the entirety of the libNumeric module. If you copy and paste this code into your own applications, you must keep the copyright and

license notices intact, per the GNU Lesser General Public License (pg. 128) and the GNU General Public License (pg. 130). This is not public domain software; it is licensed to you at no cost.

'COPYRIGHT NOTICE

'THIS VBA MODULE IS COPYRIGHT 2014-2017 BY MATTHEW S. HARRIS. All Rights Reserved.

'You can contact Matthew S. Harris at [email protected]

'Code Version: v2017-04

'COPYING PERMISSIONS

'The libNumeric module code is free software: you can redistribute it and/or modify

'it under the terms of the GNU Lesser General Public License as published by

'the Free Software Foundation, either version 3 of the License, or any later version.

'This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;

'without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

'See the GNU Lesser General Public License for more details.

'You should have received a copy of the GNU General Public License

'along with this module. If not, see <http://www.gnu.org/licenses/>.

Option Compare Database

Option Explicit

Public Function IsInteger(s As Variant) As Boolean

'tests the value in "s" to find out if it is an integer number

IsInteger = False 'assume it isn't an integer

On Error GoTo BadConversion

s = CStr(s)

If (CDbl(s) - CLng(s)) <> 0 Then Exit Function

IsInteger = True

BadConversion:

End Function

Page 97: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 97 of 137

The LIBSTRING Module This section contains the entirety of the libString module. If you copy and paste this code into your own applications, you must keep the copyright and

license notices intact, per the GNU Lesser General Public License (pg. 128) and the GNU General Public License (pg. 130). This is not public domain software; it is licensed to you at no cost.

'COPYRIGHT NOTICE

'THIS VBA MODULE IS COPYRIGHT 2014-2017 BY MATTHEW S. HARRIS. All Rights Reserved.

'You can contact Matthew S. Harris at [email protected]

'Code Version: v2017-04

'COPYING PERMISSIONS

'The libString module code is free software: you can redistribute it and/or modify

'it under the terms of the GNU Lesser General Public License as published by

'the Free Software Foundation, either version 3 of the License, or any later version.

'This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;

'without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

'See the GNU Lesser General Public License for more details.

'You should have received a copy of the GNU General Public License

'along with this module. If not, see <http://www.gnu.org/licenses/>.

Option Compare Database

Option Explicit

Public Function ConcatStringList(s1 As String, s2 As String, sDelim As String, Optional NoSpace As Boolean = False) As String

'return s2 concatenated to the end of s1, delimited by sDelim (typically 1 char in length, but may be more) plus one space char.

'if s2 is blank, s1 is returned trimmed

'if the optional NoSpace argument is included and is True, the delimiter is added without the space padding after it

If s1 = "" Then

ConcatStringList = Trim(s2) 'if s1 is empty, return s2 by itself; returns empty string if s2 is also blank

Else

If s2 = "" Then

ConcatStringList = Trim(s1) 's2 is blank, return s1 without adding delimiter

Else

If NoSpace Then

ConcatStringList = Trim(s1) & sDelim & s2

Else

ConcatStringList = Trim(s1) & sDelim & " " & s2

End If

End If

End If

End Function

Public Function AssembleName_FirstNameFirst(sFirstName As String, sLastName As String, _

Optional sMI As String = "", _

Optional sOrg As String = "", _

Optional sRespect As String = "", _

Optional sSuffix As String = "") As String

'Returns a person's name assembled from the function arguments, First Name first, e.g. John Smith.

'sFirstName is a person's first name, sLastName is a person's last name.

'Optional argument sMI is a person's middle initial (may also be full middle name),

'Optional argument sOrg is the person's organization/business/job title, etc.

'Optional argument sRespect is the title of respect (Mr., Mrs., Dr., etc.)

Page 98: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 98 of 137

'Optional argument sSuffix is whatever comes after the name, such as Jr., Esq., Phd., etc.

Dim s As String

s = sRespect 'title of respect, blank if not included

s = ConcatStringList(s, sFirstName, "")

s = ConcatStringList(s, sMI, "")

s = ConcatStringList(s, sLastName, "")

s = ConcatStringList(s, sSuffix, "") 'name suffix, blank if not included

s = ConcatStringList(s, sOrg, ";")

AssembleName_FirstNameFirst = s

End Function

Public Function AssembleName_LastNameFirst(sFirstName As String, sLastName As String, _

Optional sMI As String = "", _

Optional sOrg As String = "") As String

'Returns a person's name assembled from the function arguments, Last Name first, e.g. Smith, John

'sFirstName is a person's first name, sLastName is a person's last name.

'Optional argument sMI is a person's middle initial (may also be full middle name),

'Optional argument sOrg is the person's organization/business/job title, etc.

Dim s As String

s = sLastName

s = ConcatStringList(s, sFirstName, ",")

s = ConcatStringList(s, sMI, "")

s = ConcatStringList(s, sOrg, ";")

AssembleName_LastNameFirst = s

End Function

Page 99: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 99 of 137

The LIBSYSTEM Module This section contains the entirety of the libSystem module. If you copy and paste this code into your own applications, you must keep the copyright and

license notices intact, per the GNU Lesser General Public License (pg. 128) and the GNU General Public License (pg. 130). This is not public domain software; it is licensed to you at no cost.

'COPYRIGHT NOTICE

'THIS VBA MODULE IS COPYRIGHT 2014-2017 BY MATTHEW S. HARRIS. All Rights Reserved.

'You can contact Matthew S. Harris at [email protected]

'Code Version: v2017-04

'COPYING PERMISSIONS

'The libSystem module code is free software: you can redistribute it and/or modify

'it under the terms of the GNU Lesser General Public License as published by

'the Free Software Foundation, either version 3 of the License, or any later version.

'This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;

'without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

'See the GNU Lesser General Public License for more details.

'You should have received a copy of the GNU General Public License

'along with this module. If not, see <http://www.gnu.org/licenses/>.

Option Compare Database

Option Explicit

'Make a usable reference to the GetUserNameA function in advapi32.dll as a private function: APICall_GetUserName

'use appropriate declaration for this VBA version

#If VBA7 Then

Private Declare PtrSafe Function APICall_GetUserName Lib "advapi32.dll" _

Alias "GetUserNameA" (ByVal lpBuffer As String, nSize As Long) As Long

#Else

Private Declare Function APICall_GetUserName Lib "advapi32.dll" _

Alias "GetUserNameA" (ByVal lpBuffer As String, nSize As Long) As Long

#End If

'Make a usable reference to the GetComputerNameA function in kernel32.dll as a private function: APICall_GetComputerName

'use appropriate declaration for this VBA version

#If VBA7 Then

Private Declare PtrSafe Function APICall_GetComputerName Lib "kernel32" _

Alias "GetComputerNameA" (ByVal lpBuffer As String, nSize As Long) As Long

#Else

Private Declare Function APICall_GetComputerName Lib "kernel32" _

Alias "GetComputerNameA" (ByVal lpBuffer As String, nSize As Long) As Long

#End If

'

Public Function GetLogonName_API() As String

'Retrieve system login user name, utilising the APICall_GetUserName function which accesses GetUserNameA in advapi32.dll

Dim lpBuff As String * 255

'Get the user name minus any trailing spaces found in the name.

If APICall_GetUserName(lpBuff, 255) > 0 Then

GetLogonName_API = Left(lpBuff, InStr(lpBuff, Chr(0)) - 1)

Else

GetLogonName_API = vbNullString

Page 100: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 100 of 137

End If

End Function

Public Function GetComputerName_API() As String

'Retrieve computer name, utilising the APICall_GetComputerName function which accesses GetComputerNameA in kernel32.dll

Dim lpBuff As String * 255

If APICall_GetComputerName(lpBuff, 255) > 0 Then

GetComputerName_API = Left(lpBuff, InStr(lpBuff, Chr(0)) - 1)

Else

GetComputerName_API = vbNullString

End If

End Function

Public Function GetLogonName() As String

'Retrieve system login user name, utilising the Environ information

GetLogonName = Environ("username")

End Function

Public Function GetComputerName() As String

'Retrieve computer name, utilising the Environ information

GetComputerName = Environ("computername")

End Function

Public Sub CreateLNKShortcut(ByVal ShortCutFileName As String, _

PointsTo As String, _

WorkDir As String, _

HotKey As String, _

Description As String)

'Uses WScript.Shell to create a new shortcut link (LNK)

'ShortCutFileName is the full path and filename for the new shortcut, must include the .lnk extension

' ShortCutFileName is typically in the format: "C:\Users\[USERNAME]\Desktop\[LINKNAME].lnk"

' where [USERNAME] is the user's logon name and [LINKNAME] is the desired name of the shortcut.

' If you omit the .lnk file extension, it will be automatically added to ShortCutFileName

'PointsTo is the target of the shortcut, must include a fully qualified path

'WorkDir is the working directory/folder of the item opened by the shortcut, must contain a fully qualified path

'HotKey is a string containing a representation of the hotkey you want to use for the shortcut, such as "Ctrl+Alt+T"

' Use an empty string if you do not want to set a hotkey (this is usually preferred).

'Description appears in Comment box of the shortcut's properties window.

Dim WS As Object

Dim SC As Object

'ensure ShortCutFileName has proper extension

If LCase(libFiles.FileExtension_FromFullPath(ShortCutFileName)) <> ".lnk" Then ShortCutFileName = ShortCutFileName & ".lnk"

Set WS = CreateObject("WScript.Shell")

Set SC = WS.CreateShortcut(ShortCutFileName) 'create shortcut object with specified file name

SC.TargetPath = PointsTo 'target to be opened with shortcut

SC.HotKey = HotKey 'hotkey to invoke the shortcut

SC.Description = Description 'description for the shortcut

SC.WorkingDirectory = WorkDir 'working directory/folder for shortcut target

SC.Save 'save the shortcut

End Sub

Public Sub CreateURLShortcut(ByVal ShortCutFileName As String, _

Page 101: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 101 of 137

PointsTo As String)

'Uses WScript.Shell to create a new URL shortcut link

'ShortCutFileName is the full path and filename for the new shortcut, must include the .URL extension

' ShortCutFileName is typically in the format: "C:\Users\[USERNAME]\Desktop\[LINKNAME].URL"

' where [USERNAME] is the user's logon name and [LINKNAME] is the desired name of the shortcut.

' If you omit the .url file extension, it will be automatically added to ShortCutFileName

'PointsTo is the target of the shortcut, must be a well-formed url address

Dim WS As Object

Dim SC As Object

'ensure ShortCutFileName has proper extension

If LCase(libFiles.FileExtension_FromFullPath(ShortCutFileName)) <> ".url" Then ShortCutFileName = ShortCutFileName & ".url"

Set WS = CreateObject("WScript.Shell")

Set SC = WS.CreateShortcut(ShortCutFileName) 'create shortcut object with specified file name

SC.TargetPath = PointsTo 'target to be opened with shortcut

SC.Save 'save the shortcut

End Sub

Page 102: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 102 of 137

The LIBTABLES Module This section contains the entirety of the libTables module. If you copy and paste this code into your own applications, you must keep the copyright and

license notices intact, per the GNU Lesser General Public License (pg. 128) and the GNU General Public License (pg. 130). This is not public domain software; it is licensed to you at no cost.

'COPYRIGHT NOTICE

'THIS VBA MODULE IS COPYRIGHT 2014-2017 BY MATTHEW S. HARRIS. All Rights Reserved.

'You can contact Matthew S. Harris at [email protected]

'Code Version: v2017-04

'COPYING PERMISSIONS

'The libTables module code is free software: you can redistribute it and/or modify

'it under the terms of the GNU Lesser General Public License as published by

'the Free Software Foundation, either version 3 of the License, or any later version.

'This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;

'without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

'See the GNU Lesser General Public License for more details.

'You should have received a copy of the GNU General Public License

'along with this module. If not, see <http://www.gnu.org/licenses/>.

Option Compare Database

Option Explicit

Public Function TableExists(ByVal sTableName As String) As Boolean

'returns true if the table named in TableName exists in the current database's TableDefs collection

'name matching is case-insensitive (all text values are compared as upper-case)

Dim objTable As TableDef

TableExists = False 'presume failure

sTableName = UCase(sTableName)

For Each objTable In CurrentDb.TableDefs

'table name must match

If (sTableName = UCase(objTable.Name)) Then

TableExists = True

Exit Function 'found it, process no more

End If

Next

End Function

Public Function TableExists_Local(ByVal sTableName As String) As Boolean

'returns true if the table named in TableName exists in the current database's TableDefs collection

' AND if the table is defined locally; i.e. NOT linked. Locally defined tables have an empty Connect property

'name matching is case-insensitive (all text values are compared as upper-case)

Dim objTable As TableDef

TableExists_Local = False 'presume failure

sTableName = UCase(sTableName)

For Each objTable In CurrentDb.TableDefs

'local table: name must match AND Connect property must be empty

Page 103: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 103 of 137

If (sTableName = UCase(objTable.Name)) And (objTable.Connect = "") Then

TableExists_Local = True

Exit Function 'found it, process no more

End If

Next

End Function

Public Function TableExists_Linked(ByVal sTableName As String) As Boolean

'returns true if the table named in TableName exists in the current database's TableDefs collection

' AND if the table is linked; linked tables have a non-blank Connect property.

'name matching is case-insensitive (all text values are compared as upper-case)

Dim objTable As TableDef

TableExists_Linked = False 'presume failure

sTableName = UCase(sTableName)

For Each objTable In CurrentDb.TableDefs

'linked table: name must match AND Connect property must contain something

If (sTableName = UCase(objTable.Name)) And (objTable.Connect <> "") Then

TableExists_Linked = True

Exit Function 'found it, process no more

End If

Next

End Function

Public Function Make_TempTableName() As String

'returns a temporary table name based on the current date and time, ensures that it is unique

'temp table name is in the format "TEMP yyyymmddhhmmssN" where yyyy is the year, mm the month, dd the day,

' hh the hour, mm the minutes, ss the seconds, and N is a number >= 0

' for example, a temp table created on 9/10/17 at 11:02:45a would end up named "TEMP201709101102450"

Dim s As String

Dim k As Long

On Error GoTo BadName 'return empty string if there is an error at any point

s = "TEMP" & Format(Now, "yyyymmddhhmmss")

'ensure the name is unique

k = 0

Do While TableExists(s & k)

k = k + 1

Loop

s = s & k

Make_TempTableName = s

Exit Function 'skip error-handler

BadName:

Make_TempTableName = ""

End Function

Page 104: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 104 of 137

The LIBUICONTROL Module This section contains the entirety of the libUIControl module. If you copy and paste this code into your own applications, you must keep the copyright

and license notices intact, per the GNU Lesser General Public License (pg. 128) and the GNU General Public License (pg. 130). This is not public domain software; it is licensed to you at no cost.

'COPYRIGHT NOTICE

'THIS VBA MODULE IS COPYRIGHT 2014-2017 BY MATTHEW S. HARRIS. All Rights Reserved.

'You can contact Matthew S. Harris at [email protected]

'Code Version: v2017-04

'COPYING PERMISSIONS

'The libUIControl module code is free software: you can redistribute it and/or modify

'it under the terms of the GNU Lesser General Public License as published by

'the Free Software Foundation, either version 3 of the License, or any later version.

'This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;

'without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

'See the GNU Lesser General Public License for more details.

'You should have received a copy of the GNU General Public License

'along with this module. If not, see <http://www.gnu.org/licenses/>.

Option Compare Database

Option Explicit

Public Sub NavPane_Hide(Optional ByRef FormCalling As Form = Nothing)

'this procedure completely hides the Access Navigation pane in Access 2007 and higher.

'If called from a modal form, include a reference to the form object in the optional FormCalling argument.

'Because of a quirk with modal forms when trying to hide the Nav Pane, when called from code inside a modal form

' this procedure (if supplied with the calling form reference) temporarily makes the form non-modal, enabling

' the Nav Pane to be hidden. When called from code inside a modal form without the form reference, the hide

' operation hides whatever object was last selected before opening the modal form.

If Not (FormCalling Is Nothing) Then

'a form reference has been supplied, see if we need to do the trick for a modal form

If (FormCalling.Modal = True) Then

'form is modal, make it non-modal to hide Nav Pane, then restore modal property

FormCalling.Modal = False

DoCmd.NavigateTo "acNavigationCategoryObjectType" 'Select Nav Pane so we can hide it

DoCmd.RunCommand acCmdWindowHide 'hide currently selected object

FormCalling.Modal = True 'restore modal state of calling form

Else 'Else not modal

'nothing special needed for this form, just select and hide

DoCmd.NavigateTo "acNavigationCategoryObjectType"

DoCmd.RunCommand acCmdWindowHide

End If

Else

'not a form, just select and hide

DoCmd.NavigateTo "acNavigationCategoryObjectType"

DoCmd.RunCommand acCmdWindowHide

End If

End Sub

Public Sub NavPane_UnHide()

'this procedure unhides (makes visible) the Access Navigation pane in Access 2007 and higher.

Page 105: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 105 of 137

'NOTE when unhidden, the Nav Pane is expanded/maximized.

DoCmd.SelectObject acTable, , True

End Sub

Public Sub NavPane_Minimize(Optional bCustom As Boolean = False)

'this procedure minimizes the Access 2007/2010 Navigation pane.

'Because the NavigateTo argument changes the selected sort order in the nav pane, the optional bCustom argument

'indicates whether to select the nav pane custom groups or the nav pane category group.

If Not bCustom Then

DoCmd.NavigateTo "acNavigationCategoryObjectType" 'Select Nav Pane with category object type

Else

DoCmd.NavigateTo "Custom" 'Select Nav Pane with Custom groups displayed

End If

DoCmd.Minimize 'minimize currently selected object

End Sub

Public Sub NavPane_Maximize(Optional bCustom As Boolean = False)

'this procedure maximizes/expands the Access Navigation pane in Access 2007 and higher.

'Because the NavigateTo argument changes the selected sort order in the nav pane, the optional bCustom argument

'indicates whether to select the nav pane custom groups or the nav pane category group.

If Not bCustom Then

DoCmd.NavigateTo "acNavigationCategoryObjectType" 'Select Nav Pane with category object type

Else

DoCmd.NavigateTo "Custom" 'Select Nav Pane with Custom groups displayed

End If

DoCmd.Maximize 'maximize currently selected object

End Sub

Page 106: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 106 of 137

The LIBUNITCONVERSION Module This section contains the entirety of the libUnitConversion module. If you copy and paste this code into your own applications, you must keep the

copyright and license notices intact, per the GNU Lesser General Public License (pg. 128) and the GNU General Public License (pg. 130). This is not public domain software; it is licensed to you at no cost.

'COPYRIGHT NOTICE

'THIS VBA MODULE IS COPYRIGHT 2014-2017 BY MATTHEW S. HARRIS. All Rights Reserved.

'You can contact Matthew S. Harris at [email protected]

'Code Version: v2017-04

'COPYING PERMISSIONS

'The libUnitConversion module code is free software: you can redistribute it and/or modify

'it under the terms of the GNU Lesser General Public License as published by

'the Free Software Foundation, either version 3 of the License, or any later version.

'This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;

'without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

'See the GNU Lesser General Public License for more details.

'You should have received a copy of the GNU General Public License

'along with this module. If not, see <http://www.gnu.org/licenses/>.

Option Compare Database

Option Explicit

'************************************

'*** LINEAR (DISTANCE) ***

'************************************

Public Function Convert_Inch2Centimeter(dblInches As Double) As Double

'converts a value given in inches to centimeters

Convert_Inch2Centimeter = dblInches * 2.54

End Function

Public Function Convert_Centimeter2Inch(dblCM As Double) As Double

'converts a value given in centimeters to inches

Convert_Centimeter2Inch = dblCM * 0.3937

End Function

Public Function Convert_Feet2Meter(dblFeet As Double) As Double

'converts a value given in Feet to Meters

Convert_Feet2Meter = dblFeet / 3.280839895

End Function

Public Function Convert_Meter2Feet(dblMeter As Double) As Double

'converts a value given in Meters to Feet

Convert_Meter2Feet = dblMeter * 3.280839895

End Function

Public Function Convert_Kilometer2Mile(dblKilometer As Double) As Double

'converts a value given in Kilometers to Miles

'NOTE this is land miles, not nautical miles

Convert_Kilometer2Mile = dblKilometer / 1.609

End Function

Page 107: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 107 of 137

Public Function Convert_Mile2Kilometer(dblMiles As Double) As Double

'converts a value given in Miles to Kilometers

'NOTE this is land miles, not nautical miles

Convert_Mile2Kilometer = dblMiles * 1.609

End Function

Public Function Convert_USLandMiles2Feet(dblMiles As Double) As Double

'converts a value given in US statute miles to feet

Convert_USLandMiles2Feet = dblMiles * 5280

End Function

Public Function Convert_Meter2Centimeter(dblMeter As Double) As Double

'converts a value given in meters to a value in centimeters

'there are 100 centimeters in a meter.

Convert_Meter2Centimeter = dblMeter * 100

End Function

Public Function Convert_Centimeter2Meter(dblCM As Double) As Double

'converts a value given in centimeters to a value in meters

Convert_Centimeter2Meter = dblCM / 100

End Function

Public Function Convert_Feet2Yards(dblFeet As Double) As Double

'converts a value given in feet to a value in yards

'there are 3 feet in a yard

Convert_Feet2Yards = dblFeet / 3

End Function

Public Function Convert_Yards2Feet(dblYards As Double) As Double

'converts a value given in yards to a value in feet

Convert_Yards2Feet = dblYards * 3

End Function

Public Function Convert_Feet2Inches(dblFeet As Double) As Double

'converts a value given in feet to a value in inches.

Convert_Feet2Inches = dblFeet * 12

End Function

Public Function Convert_Inches2Feet(dblInches As Double) As Double

'converts a value in Inches to a value in Feet.

Convert_Inches2Feet = dblInches / 12

End Function

Public Function Convert_Feet2FtInchesStr(dblFeet As Double) As String

'returns a string showing feet and inches, based on a value given in feet.

'For example:

' 1.5 feet = 1' 6"

' 1.53125 feet = 1' 6.375"

Dim dblInch As Double

Dim intFeet As Long

intFeet = ((dblFeet * 12) \ 12) 'extract the integer number of feet

dblInch = (dblFeet - intFeet) * 12 'convert the remainder into inches (x12); this correctly renders decimal fraction inches

Convert_Feet2FtInchesStr = intFeet & "' " & Format(dblInch, "#.000") & """"

End Function

Page 108: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 108 of 137

Public Function Convert_Inches2FtInchesStr(dblInches As Double) As String

'returns a string showing feet and inches, based on a value given in inches

'For example:

' 18 inches = 1' 6"

' 18.375 inches = 1' 6.375"

Dim dblInch As Double

Dim intFeet As Long

intFeet = dblInches \ 12 'compute number of integer feet

dblInch = dblInches - (intFeet * 12) 'get the number of inches remaining after subtracting integer value of feet; this correctly renders

decimal fraction inches

Convert_Inches2FtInchesStr = intFeet & "' " & Format(dblInch, "#.000") & """"

End Function

'************************************

'*** AREA ***

'************************************

Public Function Convert_SqCentimeters2SqInches(dblSqCm As Double) As Double

'converts a value given in square centimeters to square inches

Convert_SqCentimeters2SqInches = dblSqCm * 0.15500031

End Function

Public Function Convert_SqInches2SqCentimeters(dblSqInches As Double) As Double

'converts a value given in square inches to square centimeters

Convert_SqInches2SqCentimeters = dblSqInches * 6.4516

End Function

Public Function Convert_SqMeters2SqFeet(dblSqMeters As Double) As Double

'converts a value given in square meters to square feet

Convert_SqMeters2SqFeet = dblSqMeters * 10.7639

End Function

Public Function Convert_SqFeet2SqMeters(dblSqFeet As Double) As Double

'converts a value given in square feet to square meters

Convert_SqFeet2SqMeters = dblSqFeet * 0.092903

End Function

Public Function Convert_Acres2SqFeet(dblAcres As Double) As Double

'converts a value given in acres to square feet

Convert_Acres2SqFeet = dblAcres * 43560

End Function

Public Function Convert_SqFeet2Acres(dblSqFeet As Double) As Double

'converts a value given in square feet to acres

Convert_SqFeet2Acres = dblSqFeet / 43560

End Function

Public Function Convert_SqFeet2SqInches(dblSqFeet As Double) As Double

'converts a value given in square feet to square inches

'144 sq. inches in a square foot

Convert_SqFeet2SqInches = dblSqFeet * 144

End Function

Public Function Convert_SqInches2SqFeet(dblSqInches As Double) As Double

Page 109: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 109 of 137

'converts a value given in square inched to square feet

Convert_SqInches2SqFeet = dblSqInches / 144

End Function

Public Function Convert_SqFeet2SqYards(dblSqFeet As Double) As Double

'converts a value given in square feet to square yards

Convert_SqFeet2SqYards = dblSqFeet * 0.111111

End Function

Public Function Convert_SqYards2SqFeet(dblSqYards As Double) As Double

'converts a value given in square yards to square feet

Convert_SqYards2SqFeet = dblSqYards * 9

End Function

Public Function Convert_SqCentimeter2SqMeter(dblSqCm As Double) As Double

'converts a value given in square centimeters to a value in square meters

Convert_SqCentimeter2SqMeter = dblSqCm / 10000

End Function

Public Function Convert_SqMeters2SqCentimeters(dblSqMeter As Double) As Double

'converts a value given in square meters to a value in square centimeters

Convert_SqMeters2SqCentimeters = dblSqMeter / 0.0001

End Function

'************************************

'*** WEIGHT ***

'************************************

Public Function Convert_Gram2Ounce(dblGram As Double) As Double

'converts a value given in grams to ounces

Convert_Gram2Ounce = dblGram * 0.0353

End Function

Public Function Convert_Ounce2Gram(dblOZ As Double) As Double

'converts a value given in ounces to grams

'Ounces to grams: Multiply by 28.3495

Convert_Ounce2Gram = dblOZ * 28.3495

End Function

Public Function Convert_Kilo2Pound(dblKilo As Double) As Double

'converts a value given in kilos to pounds

Convert_Kilo2Pound = dblKilo * 2.2046

End Function

Public Function Convert_Pound2Kilo(dblPounds As Double) As Double

'converts a value given in pounds to kilos

Convert_Pound2Kilo = dblPounds * 0.45

End Function

Public Function Convert_OZ2LB(dblOZS As Double) As Double

'converts a value given in ounces to a value in pounds

Convert_OZ2LB = dblOZS / 16

End Function

Public Function Convert_LB2OZ(dblLBS As Double) As Double

Page 110: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 110 of 137

'converts a value given in pounds to a value in ounces

Convert_LB2OZ = dblLBS * 16

End Function

Public Function Convert_Gram2Kilo(dblGram As Double) As Double

'converts a value given in grams to a value in kilos

Convert_Gram2Kilo = dblGram / 1000

End Function

Public Function Convert_Kilos2Grams(dblKilo As Double) As Double

'converts a value given in Kilos to a value in Grams

Convert_Kilos2Grams = dblKilo * 1000

End Function

'************************************

'*** VOLUME ***

'************************************

Public Function Convert_CubicCentimeters2CubicInches(dblCC As Double) As Double

'converts a value in cubic centimeters to cubic inches

Convert_CubicCentimeters2CubicInches = dblCC * 0.0610237441

End Function

Public Function Convert_CubicInches2CubicCentimeters(dblCubicInches As Double) As Double

'converts a value in cubic inches to cubic centimeters

Convert_CubicInches2CubicCentimeters = dblCubicInches * 16.387064

End Function

Public Function Convert_CubicMeters2CubicFeet(dblCubicMeters As Double) As Double

'converts a value given in cubic meters to cubic feet

Convert_CubicMeters2CubicFeet = dblCubicMeters * 35.3147

End Function

Public Function Convert_CubicFeet2CubicMeters(dblCubicFeet As Double) As Double

'converts a value given in cubic feet to cubic meters

Convert_CubicFeet2CubicMeters = dblCubicFeet * 0.0283168

End Function

Public Function Convert_CubicInches2CubicFeet(dblCubicInches As Double) As Double

'converts a value given in cubic inches to cubic feet

Convert_CubicInches2CubicFeet = dblCubicInches * 0.000578704

End Function

Public Function Convert_CubicFeet2CubicInches(dblCubicFeet As Double) As Double

'converts a value given in cubic feet to cubic inches

Convert_CubicFeet2CubicInches = dblCubicFeet * 1728

End Function

Public Function Convert_CubicCentimeters2CubicMeters(dblCC As Double) As Double

'converts a value given in cubic centimeters to cubic meters

Convert_CubicCentimeters2CubicMeters = dblCC * (1 * 10 ^ -6)

End Function

Public Function Convert_CubicMeters2CubicCentimeters(dblCubicMeters As Double) As Double

'converts a value given in cubic centimeters to cubic meters

Convert_CubicMeters2CubicCentimeters = dblCubicMeters * 1000000

Page 111: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 111 of 137

End Function

Public Function Convert_CubicCentimeters2Liters(dblCC As Double) As Double

'converts a value in cubic centimeters to liters

Convert_CubicCentimeters2Liters = dblCC * 0.001

End Function

Public Function Convert_Liters2CubicCentimeters(dblLiters As Double) As Double

'converts a value in liters to cubic centimeters

Convert_Liters2CubicCentimeters = dblLiters * 1000

End Function

'************************************

'*** LIQUID ***

'************************************

Public Function Convert_Liters2Gallons(dblLiters As Double) As Double

'converts a value given in liters to US gallons

Convert_Liters2Gallons = dblLiters * 0.264172

End Function

Public Function Convert_USGallons2Liters(dblGallons As Double) As Double

'converts a value given in US gallons to liters

Convert_USGallons2Liters = dblGallons * 3.78541

End Function

Public Function Convert_FluidOZ2Pints(dblOZ As Double) As Double

'converts a value in US fluid ounces to US Pints

Convert_FluidOZ2Pints = dblOZ / 16

End Function

Public Function Convert_USPints2FluidOZ(dblPints As Double) As Double

'converts a value in US Pints to US fluid ounces

Convert_USPints2FluidOZ = dblPints * 16

End Function

Public Function Convert_USPints2Quarts(dblPints As Double) As Double

'converts a value in US Pints to quarts

Convert_USPints2Quarts = dblPints / 2

End Function

Public Function Convert_USQuarts2Pints(dblQuarts As Double) As Double

'converts a value in US Quarts to pints

Convert_USQuarts2Pints = dblQuarts * 2

End Function

Public Function Convert_Quarts2USGallons(dblQuarts As Double) As Double

'converts a value in quarts to US gallons

Convert_Quarts2USGallons = dblQuarts / 4

End Function

Public Function Convert_USGallons2Quarts(dblGallons As Double) As Double

'converts a value in US gallons to quarts

Convert_USGallons2Quarts = dblGallons * 4

End Function

Page 112: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 112 of 137

Public Function Convert_ML2FluidOZ(dblML As Double) As Double

'converts a value given in milli-liters to a value in US Fluid Ounces

Convert_ML2FluidOZ = dblML * 0.033814

End Function

Public Function Convert_FluidOZ2ML(dblFlOz As Double) As Double

'converts a value given in US Fluid Ounces to a value in Milli-liters

Convert_FluidOZ2ML = dblFlOz / 0.033814

End Function

Public Function Convert_ML2Liter(dblML As Double) As Double

'converts a value given in Milli-liters to a value in Liters

Convert_ML2Liter = dblML / 1000

End Function

Public Function Convert_Liter2ML(dblLiter As Double) As Double

'converts a value given in Liters to a value in milli-liters

Convert_Liter2ML = dblLiter * 1000

End Function

Public Function Convert_Liter2Pint(dblLiter As Double) As Double

'converts a value given in Liters to a value in US Pints

Convert_Liter2Pint = dblLiter / 0.473176473

End Function

Public Function Convert_Pint2Liter(dblPint As Double) As Double

'converts a value given in US Pints to a value in Liters

Convert_Pint2Liter = dblPint * 0.473176473

End Function

'************************************

'*** SPEED ***

'************************************

Public Function Convert_MPH2KPH(dblMPH As Double) As Double

'converts a value in miles per hour (MPH) to kilometers per hour (KPH)

Convert_MPH2KPH = dblMPH * 1.609344

End Function

Public Function Convert_KPH2MPH(dblKPH As Double) As Double

'converts a value given in Kilometers per hour (KPH) to miles per hour (MPH)

Convert_KPH2MPH = dblKPH / 1.609344

End Function

Public Function Convert_MPH2FPS(dblMPH As Double) As Double

'converts a value given in Miles Per Hour (MPH) to a value in Feet Per Second (FPS)

Convert_MPH2FPS = (dblMPH * 5280) / 3600

End Function

Public Function Convert_FPS2MPH(dblFPS As Double) As Double

'converts a value given in Feet Per Second (FPS) to a value in Miles Per Hour (MPH)

Convert_FPS2MPH = (dblFPS * 3600) / 5280

End Function

Public Function Convert_KPH2MPS(dblKPH As Double) As Double

'converts a value given in Kilometers Per Hour (KPH) to a value in Meters Per Second (MPS)

Page 113: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 113 of 137

Convert_KPH2MPS = (dblKPH * 1000) / 3600

End Function

Public Function Convert_MPS2KPH(dblMPS As Double) As Double

'converts a value given in Meters Per Second (MPH) to a value in Kilometers Per Hour (KPH)

Convert_MPS2KPH = (dblMPS * 3600) / 1000

End Function

'************************************

'*** TEMPERATURE ***

'************************************

Public Function Convert_Centigrade2Fahrenheit(dblCentigrade As Double) As Double

'converts a value given in Centigrade degrees to Fahrenheit

'To convert temperatures in degrees Celsius to Fahrenheit, multiply by 1.8 (or 9/5) and add 32.

Convert_Centigrade2Fahrenheit = (dblCentigrade * (9 / 5)) + 32

End Function

Public Function Convert_Fahrenheit2Centigrade(dblFahrenheit As Double) As Double

'converts a value given in Fahrenheit degrees to Celsius/Centigrade

'To convert temperatures in degrees Fahrenheit to Celsius, subtract 32 and multiply by .5556 (or 5/9).

Convert_Fahrenheit2Centigrade = (dblFahrenheit - 32) * (5 / 9)

End Function

Public Function Convert_Fahrenheit2Kelvin(dblFahrenheit As Double) As Double

'converts a value given in Fahrenheit degrees to a value in Kelvin degrees

Convert_Fahrenheit2Kelvin = (dblFahrenheit + 459.67) * (5 / 9)

End Function

Public Function Convert_Centigrade2Kelvin(dblCentigrade As Double) As Double

'converts a value given in Centigrade/Celsius degrees to Kelvin degrees

Convert_Centigrade2Kelvin = dblCentigrade + 273.15

End Function

Public Function Convert_Kelvin2Fahrenheit(dblKelvin As Double) As Double

'converts a value given in Kelvin degrees to Fahrenheit

Convert_Kelvin2Fahrenheit = 1.8 * (dblKelvin - 273.15) + 32

End Function

Public Function Convert_Kelvin2Centigrade(dblKelvin As Double) As Double

'converts a value given in Kelvin degrees to Centigrade/Celsius degrees.

Convert_Kelvin2Centigrade = dblKelvin - 273.15

End Function

'************************************

'*** PRINT AND SCREEN MEASUREMENT ***

'************************************

Function Convert_Inches2Twips(dblInches As Double) As Long

'returns the number of twips equivalent to the number of inches specified

'there are 1440 twips to the inch, therefore: inches * 1440 = twips

Convert_Inches2Twips = CLng(dblInches * 1440)

End Function

Function Convert_Twips2Inches(lngTwips As Long) As Double

Page 114: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 114 of 137

'returns the number of inches equivalent to the number of twips specified

'there are 1440 twips to the inch, therefore: twips / 1440 = inches

Convert_Twips2Inches = CDbl(lngTwips / 1440)

End Function

'************************************

'*** COLOR NOTATION ***

'************************************

Function Convert_HexRGB2RedGreenBlue(sHexVal As String) As String

'return a Hex RGB value formatted as "rrr-ggg-bbb" where rrr= red value, ggg= green, and bbb=blue

'always show 3 digits

Dim sRed As String

Dim sGreen As String

Dim sBlue As String

Dim k As Integer

Dim i As Integer

sRed = "&H" & Mid(sHexVal, 1, 2)

sGreen = "&H" & Mid(sHexVal, 3, 2)

sBlue = "&H" & Mid(sHexVal, 5, 2)

sRed = CLng(sRed)

Do While Len(sRed) < 3

sRed = "0" & sRed

Loop

sGreen = CLng(sGreen)

Do While Len(sGreen) < 3

sGreen = "0" & sGreen

Loop

sBlue = CLng(sBlue)

Do While Len(sBlue) < 3

sBlue = "0" & sBlue

Loop

Convert_HexRGB2RedGreenBlue = sRed & "-" & sGreen & "-" & sBlue

End Function

Function Convert_RedGreenBlue2HexRGB(ByVal sRed As String, ByVal sGreen As String, ByVal sBlue As String) As String

'return a "rrr-ggg-bbb" formatted color spec as a Hex RGB value (where rrr= red value, ggg= green, and bbb=blue)

'convert the integer strings into Hex strings

sRed = Hex(sRed)

sGreen = Hex(sGreen)

sBlue = Hex(sBlue)

Convert_RedGreenBlue2HexRGB = sRed & sGreen & sBlue

End Function

Page 115: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 115 of 137

The LIBWINDOW Module This section contains the entirety of the libWindow module. If you copy and paste this code into your own applications, you must keep the copyright and

license notices intact, per the GNU Lesser General Public License (pg. 128) and the GNU General Public License (pg. 130). This is not public domain software; it is licensed to you at no cost.

'COPYRIGHT NOTICE

'THIS VBA MODULE IS COPYRIGHT 2014-2017 BY MATTHEW S. HARRIS. All Rights Reserved.

'You can contact Matthew S. Harris at [email protected]

'Code Version: v2017-04

'COPYING PERMISSIONS

'The libWindow module code is free software: you can redistribute it and/or modify

'it under the terms of the GNU Lesser General Public License as published by

'the Free Software Foundation, either version 3 of the License, or any later version.

'This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;

'without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

'See the GNU Lesser General Public License for more details.

'You should have received a copy of the GNU General Public License

'along with this module. If not, see <http://www.gnu.org/licenses/>.

Option Compare Database

Option Explicit

Public Sub Reset_WindowSize(frm As Form)

'resizes a form to its size as specified at design time

Dim intWindowHeight As Integer

Dim intWindowWidth As Integer

Dim intTotalFormHeight As Integer

Dim intTotalFormWidth As Integer

Dim intHeightHeader As Integer

Dim intHeightDetail As Integer

Dim intHeightFooter As Integer

On Error Resume Next 'in case a section doesn't exist, just skip over it

'Determine form's height.

intHeightHeader = frm.Section(acHeader).Height

intHeightDetail = frm.Section(acDetail).Height

intHeightFooter = frm.Section(acFooter).Height

intTotalFormHeight = intHeightHeader + intHeightDetail + intHeightFooter

intTotalFormWidth = frm.Width 'Determine form's width.

intWindowHeight = frm.InsideHeight 'Determine window's height and width.

intWindowWidth = frm.InsideWidth

If intWindowWidth <> intTotalFormWidth Then frm.InsideWidth = intTotalFormWidth

If intWindowHeight <> intTotalFormHeight Then frm.InsideHeight = intTotalFormHeight

End Sub

Public Sub Reset_Window_Width(frm As Form)

'resizes a form's width to the width specified at design time

Page 116: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 116 of 137

Dim intWindowWidth As Integer

Dim intTotalFormWidth As Integer

On Error Resume Next 'in case a section doesn't exist, just skip over it

intTotalFormWidth = frm.Width 'Determine form's width.

intWindowWidth = frm.InsideWidth 'Determine window's width.

If intWindowWidth <> intTotalFormWidth Then frm.InsideWidth = intTotalFormWidth

End Sub

Page 117: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 117 of 137

The SORTER Module This section contains the entirety of the Sorter module. If you copy and paste this code into your own applications, you must keep the copyright and license

notices intact, per the GNU Lesser General Public License (pg. 128) and the GNU General Public License (pg. 130). This is not public domain software; it is licensed to you at no cost.

'COPYRIGHT NOTICE

'THIS VBA MODULE IS COPYRIGHT 2014-2017 BY MATTHEW S. HARRIS. All Rights Reserved.

'You can contact Matthew S. Harris at [email protected]

'Code Version: v2017-04

'COPYING PERMISSIONS

'The Sorter module code is free software: you can redistribute it and/or modify

'it under the terms of the GNU Lesser General Public License as published by

'the Free Software Foundation, either version 3 of the License, or any later version.

'This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;

'without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

'See the GNU Lesser General Public License for more details.

'You should have received a copy of the GNU General Public License

'along with this module. If not, see <http://www.gnu.org/licenses/>.

Option Compare Database

Option Explicit

'CONSTANTS FOR RAISING RUNTIME ERRORS

Private Const Err_BadArgument = vbObjectError + 513 'error - invalid object type passed to sorting procedure

Private Const Err_BadArgument_msg = "Only ListBox or ComboBox references may be passed to this procedure!"

Private Const Err_BadListType = vbObjectError + 514 'error - list control to be sorted is not a Value List

Private Const Err_BadListType_msg = "ListBox and ComboBox controls to be sorted must have a Row Source Type of 'Value List'!"

Private Const Err_ArrayArgReq = vbObjectError + 515 'error - argument must be an array

Private Const Err_ArrayArgReq_msg = "The aList argument must be an array!"

'

Private Sub Swap(ByRef a As Variant, ByRef b As Variant)

'exchange the values in A and B

Dim c As Variant

c = a: a = b: b = c

End Sub

Private Sub RowSwap(ByRef xList As Variant, rowIndexA As Long, rowIndexB As Long)

'exchange the values in an entire row of an array

Dim tmpRow() As Variant

Dim idx As Long

Dim iStart As Long

Dim iEnd As Long

iStart = LBound(xList, 2) 'we presume row data is in array's 2nd dimension

iEnd = UBound(xList, 2)

ReDim tmpRow(iStart To iEnd) 'size temp row array to match 2nd dimension of input

'copy row indexed by rowindexA to temp row

For idx = iStart To iEnd

tmpRow(idx) = xList(rowIndexA, idx)

Page 118: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 118 of 137

Next

'copy row indexed by rowindexB into rowindexA

For idx = iStart To iEnd

xList(rowIndexA, idx) = xList(rowIndexB, idx)

Next

'copy temp row into rowindexB

For idx = iStart To iEnd

xList(rowIndexB, idx) = tmpRow(idx)

Next

End Sub

Public Sub SortListBox(ByRef LstBox As control, Optional SortAsc As Boolean = True)

'sorts the contents of a ListBox or ComboBox control in ascending or descending order

'uses standard bubble-sort algorithm

'LstBox is a reference to a ListBox or ComboBox control

'Optional SortAsc controls the direction of the sort; if True, ascending sort; if False, descending sort

'NOTE: assumes this is a single-column ListBox

'NOTE: assumes 0-based array

'NOTE: if LstBox does not reference a ListBox or ComboBox control, this procedure raises a runtime error

'NOTE: if the LstBox Row Source Type property is not "Value List", this procedure raises a runtime error.

'NOTE: remember that ListBox and ComboBox lists/columns use 0-based numbering.

Dim idx As Long

Dim Swapped As Boolean

Dim tList() As Variant

Const MyProcName = "SortListBox Procedure"

'copy the listbox into an array (we can't actually swap elements directly in the listbox)

With LstBox

If Not ((TypeName(LstBox) = "ListBox") Or (TypeName(LstBox) = "ComboBox")) Then 'bad control type - raise error

Err.Raise Err_BadArgument, MyProcName, MyProcName & ": " & Err_BadArgument_msg

End If

If LstBox.RowSourceType <> "Value List" Then 'not a Value List - raise error

Err.Raise Err_BadListType, MyProcName, MyProcName & ": " & Err_BadListType_msg

End If

If .ListCount = 0 Then Exit Sub 'nothing to sort, bail out

ReDim tList(0 To .ListCount - 1)

For idx = 0 To .ListCount - 1

tList(idx) = .ItemData(idx)

Next

End With

Do

Swapped = False 'no swaps at this point

idx = LBound(tList) 'start at beginning of array

Do While idx < UBound(tList)

If SortAsc Then 'sort ascending

If tList(idx) > tList(idx + 1) Then

Swap tList(idx), tList(idx + 1)

Swapped = True

End If

Else 'sort descending

If tList(idx) < tList(idx + 1) Then

Swap tList(idx), tList(idx + 1)

Page 119: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 119 of 137

Swapped = True

End If

End If

idx = idx + 1

Loop

Loop Until Not Swapped

're-populate listbox from sorted array

With LstBox

.RowSource = "" 'clear listbox

'copy the sorted list back into the listbox

For idx = LBound(tList) To UBound(tList)

.AddItem tList(idx)

Next

End With

End Sub

Public Sub RowSortListBox(ByRef LstBox As control, _

Optional SortAsc As Boolean = True, _

Optional SortCol As Long = 0, _

Optional NumCompare As Boolean = False)

'sorts the contents of a ListBox or ComboBox control in ascending or descending order

'uses standard bubble-sort algorithm

'LstBox is a reference to a ListBox control

'Optional SortAsc controls the direction of the sort; if True, ascending sort; if False, descending sort

'Optional SortCol specifies which column the sort is based on; default to 0 (the first column)

'Optional NumCompare specifies whether values are compared as numeric values

'NOTE: assumes this is a multi-column ListBox

'NOTE: uses 0-based array

'NOTE: if LstBox does not reference a ListBox or ComboBox control, this procedure raises a runtime error

'NOTE: If LstBox references a list whose Row Source Type property is not "Value List", a runtime error is raised.

'NOTE: remember that ListBox and ComboBox lists/columns use 0-based numbering.

Dim idx As Long

Dim ColNum As Long

Dim Swapped As Boolean

Dim tList() As Variant

Dim s As String

Const MyProcName = "RowSortListBox"

'copy the listbox into an array (we can't actually swap elements directly in the listbox)

With LstBox

If Not ((TypeName(LstBox) = "ListBox") Or (TypeName(LstBox) = "ComboBox")) Then 'bad control type - raise error

Err.Raise Err_BadArgument, MyProcName, MyProcName & ": " & Err_BadArgument_msg

End If

If LstBox.RowSourceType <> "Value List" Then 'not a Value List - raise error

Err.Raise Err_BadListType, MyProcName, MyProcName & ": " & Err_BadListType_msg

End If

If .ListCount = 0 Then Exit Sub 'nothing to sort, bail out

If (SortCol < 0) Or (SortCol > .ListCount) Then SortCol = 0 'sanity check - force column 0 if SortCol out of range

ReDim tList(0 To .ListCount - 1, 0 To .ColumnCount - 1)

For idx = 0 To .ListCount - 1

For ColNum = 0 To .ColumnCount - 1

Page 120: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 120 of 137

tList(idx, ColNum) = .Column(ColNum, idx)

Next

Next

End With

Do

Swapped = False 'no swaps at this point

idx = LBound(tList, 1) 'start at beginning of array's first dimension

Do While idx < UBound(tList, 1)

If SortAsc Then 'sort ascending

If NumCompare Then 'make a numeric comparison

If CDbl(tList(idx, SortCol)) > CDbl(tList(idx + 1, SortCol)) Then

RowSwap tList(), idx, idx + 1

Swapped = True

End If

Else 'make a string comparison or comparison using the default data types

If tList(idx, SortCol) > tList(idx + 1, SortCol) Then

RowSwap tList(), idx, idx + 1

Swapped = True

End If

End If

Else 'sort descending

If NumCompare Then

If CDbl(tList(idx, SortCol)) < CDbl(tList(idx + 1, SortCol)) Then

RowSwap tList(), idx, idx + 1

Swapped = True

End If

Else

If tList(idx, SortCol) < tList(idx + 1, SortCol) Then

RowSwap tList(), idx, idx + 1

Swapped = True

End If

End If

End If

idx = idx + 1

Loop

Loop Until Not Swapped

With LstBox

.RowSource = "" 'clear the listbox

'copy the sorted list back into the listbox

For idx = LBound(tList, 1) To UBound(tList, 1)

s = ""

For ColNum = LBound(tList, 2) To UBound(tList, 2) 'build the row

If ColNum < UBound(tList, 2) Then

s = s & tList(idx, ColNum) & ";"

Else

s = s & tList(idx, ColNum)

End If

Next

.AddItem s

Next

End With

End Sub

Public Sub Sort_ArrayList(ByRef aList As Variant, Optional SortAsc As Boolean = True)

'sorts the contents of a single-dimensional array in ascending or descending order

Page 121: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 121 of 137

'uses standard bubble-sort algorithm

'aList is a variant expected to contain the array to be sorted

'Optional SortAsc controls the direction of the sort; if True, ascending sort; if False, descending sort

'NOTE: assumes this is a single-dimensional array

'NOTE: if type name of aList does not end with () then this procedure raises a runtime error.

Dim idx As Long

Dim Swapped As Boolean

Const MyProcName = "Sort_ArrayList"

If Right(TypeName(aList), 2) <> "()" Then 'not an array, raise a runtime error

Err.Raise Err_ArrayArgReq, MyProcName, MyProcName & ": " & Err_ArrayArgReq_msg

End If

Do

Swapped = False 'no swaps at this point

idx = LBound(aList) 'start at beginning of array

Do While idx < UBound(aList)

If SortAsc Then 'sort ascending

If aList(idx) > aList(idx + 1) Then

Swap aList(idx), aList(idx + 1)

Swapped = True

End If

Else 'sort descending

If aList(idx) < aList(idx + 1) Then

Swap aList(idx), aList(idx + 1)

Swapped = True

End If

End If

idx = idx + 1

Loop

Loop Until Not Swapped

End Sub

Public Sub Sort_Array2D(ByRef aList As Variant, _

Optional SortAsc As Boolean = True, _

Optional SortCol As Long = 0, _

Optional NumCompare As Boolean = False)

'sorts the contents of a multi-dimensional array in ascending or descending order

'uses standard bubble-sort algorithm

'aList is expected to contain a multi-dimensional array

'Optional SortAsc controls the direction of the sort; if True, ascending sort; if False, descending sort

'Optional SortCol specifies which column the sort is based on; default to 0 (the first column)

'Optional NumCompare specifies whether values are compared as numeric values

'NOTE: assumes this is a multi-dimensional array

'NOTE: uses 0-based array

'NOTE: if aList is not an array, this procedure raises a runtime error.

Dim idx As Long

Dim Swapped As Boolean

Dim s As String

Const MyProcName = "Sort_Array2D"

If Right(TypeName(aList), 2) <> "()" Then 'not an array, raise runtime error

Err.Raise Err_ArrayArgReq, MyProcName, MyProcName & ": " & Err_ArrayArgReq_msg

Page 122: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 122 of 137

End If

Do

Swapped = False 'no swaps at this point

idx = LBound(aList, 1) 'start at beginning of array's first dimension

Do While idx < UBound(aList, 1)

If SortAsc Then 'sort ascending

If NumCompare Then 'make a numeric comparison

If CDbl(aList(idx, SortCol)) > CDbl(aList(idx + 1, SortCol)) Then

RowSwap aList, idx, idx + 1

Swapped = True

End If

Else 'make a string comparison or comparison using the default data types

If aList(idx, SortCol) > aList(idx + 1, SortCol) Then

RowSwap aList, idx, idx + 1

Swapped = True

End If

End If

Else 'sort descending

If NumCompare Then

If CDbl(aList(idx, SortCol)) < CDbl(aList(idx + 1, SortCol)) Then

RowSwap aList, idx, idx + 1

Swapped = True

End If

Else

If aList(idx, SortCol) < aList(idx + 1, SortCol) Then

RowSwap aList, idx, idx + 1

Swapped = True

End If

End If

End If

idx = idx + 1

Loop

Loop Until Not Swapped

End Sub

Page 123: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 123 of 137

GNU Free Documentation License (FDL) GNU Free Documentation License Version 1.3, 3 November 2008 Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

0. PREAMBLE The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.

This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.

We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.

1. APPLICABILITY AND DEFINITIONS This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.

A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.

A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.

The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.

The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.

A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".

Page 124: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 124 of 137

Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.

The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.

The "publisher" means any person or entity that distributes copies of the Document to the public.

A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.

The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.

2. VERBATIM COPYING You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.

You may also lend copies, under the same conditions stated above, and you may publicly display copies.

3. COPYING IN QUANTITY If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.

If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.

If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.

It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.

4. MODIFICATIONS

Page 125: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 125 of 137

You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:

A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.

B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.

C. State on the Title page the name of the publisher of the Modified Version, as the publisher.

D. Preserve all the copyright notices of the Document.

E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.

F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.

G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.

H. Include an unaltered copy of this License.

I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.

J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.

K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.

L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.

M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version.

N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section.

O. Preserve any Warranty Disclaimers.

If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.

You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.

You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one

Page 126: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 126 of 137

entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.

The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.

5. COMBINING DOCUMENTS You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.

The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.

In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements".

6. COLLECTIONS OF DOCUMENTS You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.

You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.

7. AGGREGATION WITH INDEPENDENT WORKS A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.

If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.

8. TRANSLATION Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.

If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.

9. TERMINATION

Page 127: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 127 of 137

You may not copy, modify, sublicense, or distribute the Document except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, or distribute it is void, and will automatically terminate your rights under this License.

However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.

Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, receipt of a copy of some or all of the same material does not give you any rights to use it.

10. FUTURE REVISIONS OF THIS LICENSE The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.

Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. If the Document specifies that a proxy can decide which future versions of this License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Document.

11. RELICENSING "Massive Multiauthor Collaboration Site" (or "MMC Site") means any World Wide Web server that publishes copyrightable works and also provides prominent facilities for anybody to edit those works. A public wiki that anybody can edit is an example of such a server. A "Massive Multiauthor Collaboration" (or "MMC") contained in the site means any set of copyrightable works thus published on the MMC site.

"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 license published by Creative Commons Corporation, a not-for-profit corporation with a principal place of business in San Francisco, California, as well as future copyleft versions of that license published by that same organization.

"Incorporate" means to publish or republish a Document, in whole or in part, as part of another Document.

An MMC is "eligible for relicensing" if it is licensed under this License, and if all works that were first published under this License somewhere other than this MMC, and subsequently incorporated in whole or in part into the MMC, (1) had no cover texts or invariant sections, and (2) were thus incorporated prior to November 1, 2008.

The operator of an MMC Site may republish an MMC contained in the site under CC-BY-SA on the same site at any time before August 1, 2009, provided the MMC is eligible for relicensing.

Page 128: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 128 of 137

Software Licenses

All source code in this document is licensed according to the terms of the GNU LGPL and GNU GPL, following.

GNU Lesser General Public License (LGPL) GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below.

0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License.

"The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below.

An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library.

A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version".

The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version.

The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work.

1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL.

2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version:

a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or

b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy.

3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following:

a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License.

Page 129: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 129 of 137

b) Accompany the object code with a copy of the GNU GPL and this license document.

4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following:

a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License.

b) Accompany the Combined Work with a copy of the GNU GPL and this license document.

c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document.

d) Do one of the following:

1) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.

2) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version.

e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.)

5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following:

a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License.

b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.

6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation.

If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library.

Page 130: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 130 of 137

GNU General Public License (GPL)

GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works.

The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.

When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.

To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.

For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.

Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.

For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.

Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.

Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.

The precise terms and conditions for copying, distribution and modification follow.

Page 131: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 131 of 137

TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License.

"Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.

"The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations.

To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work.

A "covered work" means either the unmodified Program or a work based on the Program.

To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.

To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.

An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.

1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work.

A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.

The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.

The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.

The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.

The Corresponding Source for a work in source code form is that same work.

Page 132: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 132 of 137

2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.

You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.

Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.

3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.

When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.

4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.

You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.

5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:

a) The work must carry prominent notices stating that you modified it, and giving a relevant date.

b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices".

c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.

d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.

A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.

Page 133: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 133 of 137

6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:

a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.

b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.

c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.

d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.

e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.

A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.

A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.

"Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.

If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).

The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.

Page 134: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 134 of 137

Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.

7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.

When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.

Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:

a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or

b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or

c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or

d) Limiting the use for publicity purposes of names of licensors or authors of the material; or

e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or

f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.

All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.

If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.

Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.

Page 135: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 135 of 137

8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).

However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.

Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.

9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.

10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.

An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.

You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.

11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version".

A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.

Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.

In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.

Page 136: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 136 of 137

If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.

If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.

A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.

Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.

12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.

13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.

14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.

If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.

Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.

Page 137: MMSSHH LLiibbrraarryy –– TToooollss && … Library - Tools And...MMSSHH LLiibbrraarryy –– TToooollss && UUttiilliittiieess DDooccuummeennttaattiioonn Veerrssiioonn vv22001177--0044

MSH Library - Tools And Utilities

Copyright 2014-2017 by Matthew S. Harris. All Rights Reserved. Page 137 of 137

15. Disclaimer of Warranty.

THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

16. Limitation of Liability.

IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

17. Interpretation of Sections 15 and 16.

If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.

END OF TERMS AND CONDITIONS