This example shows how to use VBS to call built-in Excel functions, as well as custom developed Excel functions from a VBS file. Additionally, the VBS code is able to take advantage of an Excel userform that connects to the Oracle database.
We will start by creating a couple of tables – ideally, we would have more of the application logic and information in each of the database tables, but in this example most of the logic is in Excel.
CREATE TABLE CABINET_BUILDER_WOOD ( WOOD_TYPE VARCHAR2(30), PRIMARY KEY (WOOD_TYPE)); INSERT INTO CABINET_BUILDER_WOOD VALUES ('Birch'); INSERT INTO CABINET_BUILDER_WOOD VALUES ('Cedar'); INSERT INTO CABINET_BUILDER_WOOD VALUES ('Cherry'); INSERT INTO CABINET_BUILDER_WOOD VALUES ('Douglas Fir'); INSERT INTO CABINET_BUILDER_WOOD VALUES ('Ebony'); INSERT INTO CABINET_BUILDER_WOOD VALUES ('Maple'); INSERT INTO CABINET_BUILDER_WOOD VALUES ('Pine'); INSERT INTO CABINET_BUILDER_WOOD VALUES ('Poplar'); INSERT INTO CABINET_BUILDER_WOOD VALUES ('Red Oak'); INSERT INTO CABINET_BUILDER_WOOD VALUES ('Redwood'); INSERT INTO CABINET_BUILDER_WOOD VALUES ('Spruce'); INSERT INTO CABINET_BUILDER_WOOD VALUES ('White Oak'); COMMIT; CREATE TABLE CABINET_BUILDER_CAB_TYPE ( CABINET_TYPE VARCHAR2(40), PRIMARY KEY (CABINET_TYPE)); INSERT INTO CABINET_BUILDER_CAB_TYPE VALUES ('Base Cabinet Full Depth'); INSERT INTO CABINET_BUILDER_CAB_TYPE VALUES ('Base Cabinet Half Depth'); INSERT INTO CABINET_BUILDER_CAB_TYPE VALUES ('Full Height Base Cabinet'); INSERT INTO CABINET_BUILDER_CAB_TYPE VALUES ('Upper Cabinet'); INSERT INTO CABINET_BUILDER_CAB_TYPE VALUES ('Upper Cabinet Narrow'); COMMIT;
Next, we will start preparing the Excel workbook. Create a new Excel workbook and save it as C:\ExcelMacroFunctionDemo.xls. Next, right-click the first worksheet in the workbook and select View Code. From the Tools menu, select References. Add a reference to a recent release of the Microsoft ActiveX Data Objects Library, and Microsoft Windows Common Controls 6.0 (hopefully, that version is available on your computer).
From the Tools menu, select Additional Controls. Place a check next to one of the Microsoft TreeView Controls and then click the OK button.
Next, from the Insert menu, select Module – this is where we will need to add the code so that it may be accessed by the VBS script. Module1 should appear in the tree structure at the left (officially called the Project Explorer). Add the following code into the Module1 module (this example is adapted from one that I showed in a presentation to an ERP user’s group, so the contents of some of the functions may seem to be a bit odd):
Option Explicit 'Constants for registry access Private Const ERROR_SUCCESS = 0& Private Const SYNCHRONIZE = &H100000 Private Const READ_CONTROL = &H20000 Private Const STANDARD_RIGHTS_ALL = &H1F0000 Private Const STANDARD_RIGHTS_READ = (READ_CONTROL) Private Const STANDARD_RIGHTS_WRITE = (READ_CONTROL) Private Const KEY_QUERY_VALUE = &H1 Private Const KEY_SET_VALUE = &H2 Private Const KEY_CREATE_LINK = &H20 Private Const KEY_CREATE_SUB_KEY = &H4 Private Const KEY_ENUMERATE_SUB_KEYS = &H8 Private Const KEY_NOTIFY = &H10 Private Const KEY_READ = ((STANDARD_RIGHTS_READ Or KEY_QUERY_VALUE Or KEY_ENUMERATE_SUB_KEYS Or KEY_NOTIFY) And (Not SYNCHRONIZE)) Private Const KEY_ALL_ACCESS = ((STANDARD_RIGHTS_ALL Or KEY_QUERY_VALUE Or KEY_SET_VALUE Or KEY_CREATE_SUB_KEY Or KEY_ENUMERATE_SUB_KEYS Or KEY_NOTIFY Or KEY_CREATE_LINK) And (Not SYNCHRONIZE)) Private Const KEY_EXECUTE = ((KEY_READ) And (Not SYNCHRONIZE)) Private Const KEY_WRITE = ((STANDARD_RIGHTS_WRITE Or KEY_SET_VALUE Or KEY_CREATE_SUB_KEY) And (Not SYNCHRONIZE)) 'For FindWindow Private Const GW_HWNDFIRST = 0 Private Const GW_HWNDNEXT = 2 Private Const SW_SHOW = 5 Private Const SW_RESTORE = 9 'Type declaration for Zebra label printing Public Type DOC_INFO_1 pDocName As String pOutputFile As String pDatatype As String End Type 'Registry API functions Private Declare Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As Long) As Long Private Declare Function RegCreateKey Lib "advapi32.dll" Alias "RegCreateKeyA" (ByVal hKey As Long, ByVal lpSubKey As String, phkResult As Long) As Long Private Declare Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA" (ByVal hKey As Long, ByVal lpSubKey As String, ByVal ulOptions As Long, ByVal samDesired As Long, phkResult As Long) As Long Private Declare Function RegQueryValueEx Lib "advapi32.dll" Alias "RegQueryValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal lpReserved As Long, lpType As Long, lpData As Any, lpcbData As Long) As Long Private Declare Function RegSetValueEx Lib "advapi32.dll" Alias "RegSetValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal Reserved As Long, ByVal dwType As Long, lpData As Any, ByVal cbData As Long) As Long ' Note that if you declare the lpData parameter as String, you must pass it By Value. 'INI API functions Public Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long Public Declare Function WritePrivateProfileString Lib "kernel32" Alias "WritePrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpString As Any, ByVal lpFileName As String) As Long 'Zebra Printing API functions Public Declare Function ClosePrinter Lib "winspool.drv" (ByVal hPrinter As Long) As Long Public Declare Function EndDocPrinter Lib "winspool.drv" (ByVal hPrinter As Long) As Long Public Declare Function EndPagePrinter Lib "winspool.drv" (ByVal hPrinter As Long) As Long Public Declare Function OpenPrinter Lib "winspool.drv" Alias "OpenPrinterA" (ByVal pPrinterName As String, phPrinter As Long, ByVal pDefault As Long) As Long Public Declare Function StartDocPrinter Lib "winspool.drv" Alias "StartDocPrinterA" (ByVal hPrinter As Long, ByVal Level As Long, pDocInfo As DOC_INFO_1) As Long Public Declare Function StartPagePrinter Lib "winspool.drv" (ByVal hPrinter As Long) As Long Public Declare Function WritePrinter Lib "winspool.drv" (ByVal hPrinter As Long, pBuf As Any, ByVal cdBuf As Long, pcWritten As Long) As Long 'For Find Window and bring to top Private Declare Function AttachThreadInput Lib "user32" (ByVal idAttach As Long, ByVal idAttachTo As Long, ByVal fAttach As Long) As Long Private Declare Function GetForegroundWindow Lib "user32" () As Long Private Declare Function GetWindow Lib "user32" (ByVal hWnd As Long, ByVal wCmd As Long) As Long Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long Private Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hWnd As Long) As Long Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Long, lpdwProcessId As Long) As Long Private Declare Function IsIconic Lib "user32" (ByVal hWnd As Long) As Long Private Declare Function SetForegroundWindow Lib "user32" (ByVal hWnd As Long) As Long Private Declare Function ShowWindow Lib "user32" (ByVal hWnd As Long, ByVal nCmdShow As Long) As Long 'Used by the ShowWindowAfterDelay sub Dim strFindWindowTitle As String Dim intFindExitExcel As String Public Function CalculateCombinations(lngNumberElements As Long, lngNumberPositions) As Double 'Calculate the number of possible arrangements where the order does not matter of the number of elements into the number of positions CalculateCombinations = Application.WorksheetFunction.Combin(lngNumberElements, lngNumberPositions) End Function Public Function CalculateInterestPayment(dblInterestRate As Single, lngPaymentsPerYear As Long, lngNumPayments As Long, sglCurrentValue As Single) As Single 'Calculates the interest payments on a given principal CalculateInterestPayment = Abs(Application.WorksheetFunction.Pmt(dblInterestRate / lngPaymentsPerYear, lngNumPayments, sglCurrentValue)) End Function Public Function CalculatePermutations(lngNumberElements As Long, lngNumberPositions) As Double 'Calculate the number of possible arrangements where the order matters of the number of elements into the number of positions CalculatePermutations = Application.WorksheetFunction.Permut(lngNumberElements, lngNumberPositions) End Function Public Function EnvironmentVariable(strIn As String) As String 'Returns the environment variable set on the computer 'EnvironmentVariable("windir") 'Windows folder 'EnvironmentVariable("temp") 'Temp folder 'EnvironmentVariable("username") 'Username 'EnvironmentVariable("computername") 'Computer name EnvironmentVariable = Environ(strIn) End Function Private Function ForceForegroundWindow(ByVal hWnd As Long) As Long Dim lngThreadID1 As Long Dim lngThreadID2 As Long Dim lngResult As Long If hWnd = GetForegroundWindow() Then ForceForegroundWindow = True Else lngThreadID1 = GetWindowThreadProcessId(GetForegroundWindow, ByVal 0&) lngThreadID2 = GetWindowThreadProcessId(hWnd, ByVal 0&) If lngThreadID1 <> lngThreadID2 Then Call AttachThreadInput(lngThreadID1, lngThreadID2, True) lngResult = SetForegroundWindow(hWnd) Call AttachThreadInput(lngThreadID1, lngThreadID2, False) Else lngResult = SetForegroundWindow(hWnd) End If If IsIconic(hWnd) Then Call ShowWindow(hWnd, SW_RESTORE) Else Call ShowWindow(hWnd, SW_SHOW) End If ForceForegroundWindow = lngResult End If End Function Public Function RegistryGetEntry(strSubKeys As String, strValName As String) As String 'Reads a string value from the HKEY_CURRENT_USER section of the registry Const lngType = 1& Const lngKey = &H80000001 'HKEY_CURRENT_USER Dim lngResult As Long Dim lngHandle As Long Dim lngcbData As Long Dim strRet As String On Error Resume Next lngResult = RegOpenKeyEx(lngKey, strSubKeys, 0&, KEY_READ, lngHandle) If lngResult <> ERROR_SUCCESS Then RegistryGetEntry = "!!!KEY DOES NOT EXIST" & vbTab & Format(lngResult) Exit Function End If strRet = String(300, Chr(0)) lngcbData = Len(strRet) lngResult = RegQueryValueEx(lngHandle, strValName, 0&, lngType, ByVal strRet, lngcbData) 'See if the value to be returned is longer than the number of character positions in the passed in string If lngcbData > 300 Then 'String not long enough, try again strRet = String(lngcbData, Chr(0)) lngResult = RegQueryValueEx(lngHandle, strValName, 0&, lngType, ByVal strRet, lngcbData) End If If lngResult <> ERROR_SUCCESS Then RegistryGetEntry = "!!!VALUE DOES NOT EXIST" & vbTab & Format(lngResult) Else RegistryGetEntry = Left(strRet, lngcbData - 1) End If lngResult = RegCloseKey(lngHandle) End Function Public Function RegistrySetEntry(strSubKeys As String, strValName As String, strValue As String) As Long Const lngType = 1& Const lngKey = &H80000001 'HKEY_CURRENT_USER Dim lngResult As Long Dim lngHandle As Long Dim lngcbData As Long Dim strRet As String Dim strNewKey As String On Error Resume Next lngResult = RegCreateKey(lngKey, strSubKeys, lngHandle) lngResult = RegSetValueEx(lngHandle, strValName, 0&, lngType, ByVal strValue, Len(strValue)) If lngResult <> ERROR_SUCCESS Then RegistrySetEntry = False Else RegistrySetEntry = True End If lngResult = RegCloseKey(lngHandle) End Function Public Function ReverseString(strIn As Variant) As Variant 'Reverses a string value using a simple VBA macro Dim i As Integer Dim strTemp As String For i = Len(strIn) To 1 Step -1 strTemp = strTemp & Mid(strIn, i, 1) Next i ReverseString = strTemp End Function Public Function RoundNumber(dblIn As Double, intDigits As Integer, intPrintCommas As Integer) As String 'Switch the value of the variable so that TRUE becomes FALSE and FALSE becomes TRUE intPrintCommas = Not intPrintCommas 'Round the number to the specified number of decimal places RoundNumber = Application.WorksheetFunction.Fixed(dblIn, intDigits, intPrintCommas) End Function Public Function ShowCabinetBuilder(sglMarkup As Single) As Variant Dim i As Integer Dim intBOMNodes As Integer Dim intBOMCount As Integer Dim strMaterialList As String frmCabinetBuilder.sglPriceMarkup = sglMarkup frmCabinetBuilder.Show intBOMNodes = frmCabinetBuilder.tvBillOfMaterial.Nodes.Count 'Set the initial size of the array to be returned to the number of items in the treeview intBOMCount = 0 If (frmCabinetBuilder.intFormCancelled = False) And (intBOMNodes > 0) Then For i = 1 To intBOMNodes 'Do not return the category headings If frmCabinetBuilder.tvBillOfMaterial.Nodes(i).Children = 0 Then intBOMCount = intBOMCount + 1 strMaterialList = strMaterialList & frmCabinetBuilder.tvBillOfMaterial.Nodes(i).Text & vbTab End If Next i Else End If If strMaterialList <> "" Then strMaterialList = Left(strMaterialList, Len(strMaterialList) - 1) End If ShowCabinetBuilder = strMaterialList 'Remove the form from memory Unload frmCabinetBuilder End Function Public Sub ShowWindowWithDelay(strWindowTitle As String, Optional intDelaySeconds As Integer = 1, Optional intExitExcel As Integer = False) 'Used to display a window that is opened within a Visual macro - usually behind the Visual module DoEvents 'Set a couple module level variables that will be used in the ShowWindowAfterDelay sub strFindWindowTitle = strWindowTitle intFindExitExcel = intExitExcel 'After the specified number of seconds locate and show the window Application.OnTime DateAdd("s", intDelaySeconds, Now), "ShowWindowAfterDelay" End Sub Private Sub ShowWindowAfterDelay() Dim intLength As Integer 'Length of the window text length Dim strListItem As String 'Name of running programs Dim lngCurrWnd As Long 'Current window handle Dim lngResult As Long 'Return value from the API calls Dim intFlag As Integer On Error Resume Next lngCurrWnd = GetWindow(Application.hWnd, GW_HWNDFIRST) 'Loop through all of the top-level windows to find the one of interest Do While lngCurrWnd <> 0 intLength = GetWindowTextLength(lngCurrWnd) strListItem = Space$(intLength + 1) intLength = GetWindowText(lngCurrWnd, strListItem, intLength + 1) If intLength > 0 Then If InStr(UCase(strListItem), UCase(strFindWindowTitle)) > 0 Then lngResult = ForceForegroundWindow(lngCurrWnd) DoEvents intFlag = True Exit Do End If End If lngCurrWnd = GetWindow(lngCurrWnd, GW_HWNDNEXT) DoEvents Loop strFindWindowTitle = "" If intFindExitExcel = True Then ActiveWorkbook.Saved = True Application.Quit End If End Sub Public Function ToHexadecimal(dblIn As Double) As String 'Will not work in Excel 2003 'Changes the number passed in to hexadecimal using a built-in function, could use VB's HEX function ToHexadecimal = Application.WorksheetFunction.Dec2Hex(dblIn) End Function Public Function ToRoman(dblIn As Double) As String 'Changes the number passed in to Roman numerals using a built-in function ToRoman = Application.WorksheetFunction.Roman(dblIn, 0) End Function Public Function VisualINIGet(strSection As String, strName As String, Optional strINIFile As String = "Visual.ini") As String Dim lngResult As Long Dim lngRetSize As Long Dim strLocalVisualDirectory As String Dim strFilename As String Dim strRet As String strLocalVisualDirectory = RegistryGetEntry("Software\Infor Global Solutions\VISUAL Manufacturing\Configuration", "Local Directory") If Left(strLocalVisualDirectory, 3) = "!!!" Then 'Registry access failed, try again with Lilly Software strLocalVisualDirectory = RegistryGetEntry("Software\Lilly Software\VISUAL Manufacturing\Configuration", "Local Directory") End If If (Left(strLocalVisualDirectory, 3) <> "!!!") And (strLocalVisualDirectory <> "") Then 'Make certain that the path end in a \ If Right(strLocalVisualDirectory, 1) <> "\" Then strLocalVisualDirectory = strLocalVisualDirectory & "\" End If 'Make certain that the ini file ends with .INI If Len(strINIFile) > 4 Then If Right(UCase(strINIFile), 4) <> ".INI" Then strINIFile = strINIFile & ".ini" End If Else strINIFile = strINIFile & ".ini" End If strFilename = strLocalVisualDirectory & strINIFile lngRetSize = 255 strRet = String(lngRetSize, Chr(0)) lngResult = GetPrivateProfileString(strSection, strName, "", strRet, lngRetSize, strFilename) If lngResult <> 0 Then VisualINIGet = Left(strRet, InStr(strRet, Chr(0)) - 1) Else VisualINIGet = "!!!VALUE DOES NOT EXIST" End If Else 'Registry access failed VisualINIGet = "!!!LOCATION OF VISUAL.INI NOT KNOWN" End If End Function Public Function VisualINISet(strSection As String, strName As String, strValue As String, Optional strINIFile As String = "Visual.ini") As Long Dim lngResult As Long Dim lngRetSize As Long Dim strLocalVisualDirectory As String Dim strFilename As String Dim strRet As String strLocalVisualDirectory = RegistryGetEntry("Software\Infor Global Solutions\VISUAL Manufacturing\Configuration", "Local Directory") If Left(strLocalVisualDirectory, 3) = "!!!" Then 'Registry access failed, try again with Lilly Software strLocalVisualDirectory = RegistryGetEntry("Software\Lilly Software\VISUAL Manufacturing\Configuration", "Local Directory") End If If (Left(strLocalVisualDirectory, 3) <> "!!!") And (strLocalVisualDirectory <> "") Then 'Make certain that the path end in a \ If Right(strLocalVisualDirectory, 1) <> "\" Then strLocalVisualDirectory = strLocalVisualDirectory & "\" End If 'Make certain that the ini file ends with .INI If Len(strINIFile) > 4 Then If Right(UCase(strINIFile), 4) <> ".INI" Then strINIFile = strINIFile & ".ini" End If Else strINIFile = strINIFile & ".ini" End If strFilename = strLocalVisualDirectory & strINIFile lngResult = WritePrivateProfileString(strSection, strName, strValue, strFilename) If lngResult <> 0 Then VisualINISet = True Else VisualINISet = False End If Else VisualINISet = False End If End Function Public Sub ZebraPrintLabel(strPrinter As Variant, strRawText As Variant) 'Variables for handling printing Dim i As Integer Dim lngPrinterHandle As Long Dim lngResult As Long Dim lngWritten As Long Dim lngPrinterDocHandle As Long Dim bytRawText() As Byte Dim MyDocInfo As DOC_INFO_1 On Error Resume Next 'In VB6 to see the list of printer device names, enter the following into the Debug window 'For i = 0 to Printers.Count - 1:Debug.Print Printers(i).DeviceName:Next 'strPrinter = "\\MYCOMP\ZEBRA" 'Sample label 'strRawText = "~SD25^XA" 'Set Darkness, Label start 'strRawText = strRawText & "^SZ2" 'Enable ZPL2 'strRawText = strRawText & "^JZ" 'Reprint on error 'strRawText = strRawText & "^PR8,8,8" 'Print speed 8" per second, 8" per sec slew, 8" per sec backfeed 'strRawText = strRawText & "^LH10,26" 'Label home position in pixels 'strRawText = strRawText & "^FO2,14^A0R,20,20^FDMy Company^FS" 'rotated text in font A 'strRawText = strRawText & "^FO2,480^A0R,20,20^FDSomewhere^FS" 'strRawText = strRawText & "^FO180,135^B3R,,105,N^FD" & "ABC111" & "^FS" 'Font 3 of 9 barcode 'strRawText = strRawText & "^FO180,0^GB0,760,3^FS" 'Vertical Line 3 pixels wide 'strRawText = strRawText & "^FO335,0^GB0,1218,3^FS" 'Vertical Line 3 pixels wide 'strRawText = strRawText & "^FO550,0^GB0,1218,3^FS" 'Vertical Line 3 pixels wide 'strRawText = strRawText & "^FO260,760^GB0,452,3^FS" 'Vertical Line 3 pixels wide 'strRawText = strRawText & "^FO0,760^GB335,0,3^FS" 'Horizontal Line 3 pixels wide 'strRawText = strRawText & "^XZ" 'End of label indicator 'Convert the variant data types to strings strPrinter = CStr(strPrinter) strRawText = CStr(strRawText) 'Terminate the string with a CRLF combination (may not be needed) If Right(strRawText, 2) <> vbCrLf Then strRawText = strRawText & vbCrLf End If 'Convert the strRawText string to a byte stream ReDim bytRawText(1 To Len(strRawText)) For i = 1 To Len(strRawText) bytRawText(i) = Asc(Mid(strRawText, i, 1)) Next i 'Create a connection to the printer, returns a handle to the printer lngResult = OpenPrinter(strPrinter, lngPrinterHandle, 0) If lngPrinterHandle = 0 Then MsgBox "Could Not Open Printer" Exit Sub End If 'Fill in the document header structure MyDocInfo.pDocName = "Zebra Label from Excel" MyDocInfo.pOutputFile = vbNullString MyDocInfo.pDatatype = "RAW" lngPrinterDocHandle = StartDocPrinter(lngPrinterHandle, 1, MyDocInfo) If lngPrinterDocHandle = 0 Then MsgBox "Could Not Start the Document" lngResult = ClosePrinter(lngPrinterHandle) Exit Sub End If 'Prepare to start the first page Call StartPagePrinter(lngPrinterHandle) 'Write out the contents of the first page lngResult = WritePrinter(lngPrinterHandle, bytRawText(1), Len(strRawText), lngWritten) If lngResult = 0 Then MsgBox "Could Not Write to the Page" lngResult = ClosePrinter(lngPrinterHandle) Exit Sub End If 'End the first page lngResult = EndPagePrinter(lngPrinterHandle) 'End the document lngResult = EndDocPrinter(lngPrinterHandle) 'Close the connection to the printet lngResult = ClosePrinter(lngPrinterHandle) End Sub
OK, now that the easy part is done, we need to Create the UserForm that will be accessed from the VBS script. From the Insert menu, select UserForm. Change the name of the form to frmCabinetBuilder. Draw a Treeview type control on the userform (the treeview is hilighted in the below screen capture) and name the treeview as tvBillOfMaterial. Draw two ListBox controls on the userform and name them lstWoodType and lstCabinetType (shown below near the upper left of the userform). Create three CommandButton contols and name them cmdCreateSalesOrder, cmdCreateWorkOrder, and cmdExit (shown near the bottom of the userform). Create two TextBox controls and name them txtManufactureCost and txtSalesPrice (shown near the top right of the userform). Create Label controls on the userform above each of the other controls, and set their Caption property to the text that should appear inside the label control (Wood Type, Cabinet Type, etc.).
Next, we need to add code to the userform so that the controls know what to do. Double-click the background of the userform, delete any code shown, and add the following code:
Option Explicit 'Forces all variables to be declared Public intFormCancelled As Integer Public sglPriceMarkup As Single Dim dbDatabase As New ADODB.Connection Dim strDatabase As String Dim strUserName As String Dim strPassword As String Private Function ConnectDatabase() As Integer Dim intResult As Integer On Error Resume Next If dbDatabase.State <> 1 Then 'Connection to the database if closed, specify the database name, a username, and password strDatabase = "MyDB" strUserName = "MyUser" strPassword = "MyPassword" 'Connect to the database 'Oracle connection string dbDatabase.ConnectionString = "Provider=OraOLEDB.Oracle;Data Source=" & strDatabase & ";User ID=" & strUserName & ";Password=" & strPassword & ";ChunkSize=1000;FetchSize=100;" dbDatabase.ConnectionTimeout = 40 dbDatabase.CursorLocation = adUseClient dbDatabase.Open If (dbDatabase.State <> 1) Or (Err <> 0) Then intResult = MsgBox("Could not connect to the database. Check your user name and password." & vbCrLf & Error(Err), 16, "Excel Demo") ConnectDatabase = False Else ConnectDatabase = True End If Else ConnectDatabase = True End If End Function Private Sub Calculate() Const tvwChild = 4 Dim i As Integer Dim sglPrice As Single Dim sglBasePrice As Single Dim sglWoodPriceMultiplier As Single tvBillOfMaterial.Nodes.Clear If (lstWoodType.ListIndex >= 0) And (lstCabinetType.ListIndex >= 0) Then Select Case lstCabinetType.List(lstCabinetType.ListIndex) Case "Base Cabinet Full Depth" tvBillOfMaterial.Nodes.Add , , "Door", "Door" tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DStile1", "Stile 1" & " " & "3/4 X 2 X 30" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DStile2", "Stile 2" & " " & "3/4 X 2 X 30" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DRail1", "Rail 1" & " " & "3/4 X 2 X 21 3/4" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DRail2", "Rail 2" & " " & "3/4 X 2 X 21 3/4" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DPanel", "Panel" & " " & "5/8 X 21 3/4 X 27 3/4" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add , , "Case", "Case" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CStile1", "Stile 1" & " " & "3/4 X 2 X 31" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CStile2", "Stile 2" & " " & "3/4 X 2 X 31" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CRail1", "Rail 1" & " " & "3/4 X 2 X 24" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CRail2", "Rail 2" & " " & "3/4 X 2 X 24" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CKicker", "Kicker" & " " & "5/8 X 21 3/4 X 27 3/4" & " " & "Particle Board" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CSide1", "Side 1" & " " & "3/4 X 24 X 35" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CSide2", "Side 2" & " " & "3/4 X 24 X 35" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CBack", "Back" & " " & "3/4 X 22 1/2 X 35" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CTop", "Top" & " " & "3/4 X 22 1/2 X 23 1/4" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CBottom", "Bottom" & " " & "3/4 X 22 1/2 X 23 1/4" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" sglBasePrice = 150 Case "Base Cabinet Half Depth" tvBillOfMaterial.Nodes.Add , , "Door", "Door" tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DStile1", "Stile 1" & " " & "3/4 X 2 X 30" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DStile2", "Stile 2" & " " & "3/4 X 2 X 30" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DRail1", "Rail 1" & " " & "3/4 X 2 X 21 3/4" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DRail2", "Rail 2" & " " & "3/4 X 2 X 21 3/4" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DPanel", "Panel" & " " & "5/8 X 21 3/4 X 27 3/4" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add , , "Case", "Case" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CStile1", "Stile 1" & " " & "3/4 X 2 X 31" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CStile2", "Stile 2" & " " & "3/4 X 2 X 31" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CRail1", "Rail 1" & " " & "3/4 X 2 X 24" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CRail2", "Rail 2" & " " & "3/4 X 2 X 24" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CKicker", "Kicker" & " " & "5/8 X 21 3/4 X 27 3/4" & " " & "Particle Board" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CSide1", "Side 1" & " " & "3/4 X 12 X 35" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CSide2", "Side 2" & " " & "3/4 X 12 X 35" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CBack", "Back" & " " & "3/4 X 22 1/2 X 35" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CTop", "Top" & " " & "3/4 X 22 1/2 X 11 1/4" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CBottom", "Bottom" & " " & "3/4 X 22 1/2 X 11 1/4" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" sglBasePrice = 120 Case "Full Height Base Cabinet" tvBillOfMaterial.Nodes.Add , , "Door", "Door" tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DStile1", "Stile 1" & " " & "3/4 X 2 X 67" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DStile2", "Stile 2" & " " & "3/4 X 2 X 67" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DRail1", "Rail 1" & " " & "3/4 X 2 X 21 3/4" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DRail2", "Rail 2" & " " & "3/4 X 2 X 21 3/4" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DPanel", "Panel" & " " & "5/8 X 21 3/4 X 63 1/4" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add , , "Case", "Case" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CStile1", "Stile 1" & " " & "3/4 X 2 X 72" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CStile2", "Stile 2" & " " & "3/4 X 2 X 72" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CRail1", "Rail 1" & " " & "3/4 X 2 X 24" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CRail2", "Rail 2" & " " & "3/4 X 2 X 24" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CKicker", "Kicker" & " " & "5/8 X 21 3/4 X 27 3/4" & " " & "Particle Board" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CSide1", "Side 1" & " " & "3/4 X 24 X 72" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CSide2", "Side 2" & " " & "3/4 X 24 X 72" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CBack", "Back" & " " & "3/4 X 22 1/2 X 72" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CTop", "Top" & " " & "3/4 X 22 1/2 X 23 1/4" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CBottom", "Bottom" & " " & "3/4 X 22 1/2 X 23 1/4" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" sglBasePrice = 250 Case "Upper Cabinet" tvBillOfMaterial.Nodes.Add , , "Door", "Door" tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DStile1", "Stile 1" & " " & "3/4 X 2 X 9 1/4" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DStile2", "Stile 2" & " " & "3/4 X 2 X 9 1/4" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DRail1", "Rail 1" & " " & "3/4 X 2 X 21 3/4" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DRail2", "Rail 2" & " " & "3/4 X 2 X 21 3/4" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DPanel", "Panel" & " " & "5/8 X 21 3/4 X 9 1/4" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add , , "Case", "Case" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CStile1", "Stile 1" & " " & "3/4 X 2 X 12" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CStile2", "Stile 2" & " " & "3/4 X 2 X 12" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CRail1", "Rail 1" & " " & "3/4 X 2 X 24" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CRail2", "Rail 2" & " " & "3/4 X 2 X 24" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CSide1", "Side 1" & " " & "3/4 X 11 1/4 X 12" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CSide2", "Side 2" & " " & "3/4 X 11 1/4 X 12" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CBack", "Back" & " " & "3/4 X 22 1/2 X 12" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CTop", "Top" & " " & "3/4 X 22 1/2 X 11 1/4" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CBottom", "Bottom" & " " & "3/4 X 22 1/2 X 11 1/4" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" sglBasePrice = 100 Case "Upper Cabinet Narrow" tvBillOfMaterial.Nodes.Add , , "Door", "Door" tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DStile1", "Stile 1" & " " & "3/4 X 2 X 9 1/4" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DStile2", "Stile 2" & " " & "3/4 X 2 X 9 1/4" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DRail1", "Rail 1" & " " & "3/4 X 2 X 9 3/4" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DRail2", "Rail 2" & " " & "3/4 X 2 X 9 3/4" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Door", tvwChild, "DPanel", "Panel" & " " & "5/8 X 9 3/4 X 9 1/4" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add , , "Case", "Case" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CStile1", "Stile 1" & " " & "3/4 X 2 X 12" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CStile2", "Stile 2" & " " & "3/4 X 2 X 12" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CRail1", "Rail 1" & " " & "3/4 X 2 X 12" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CRail2", "Rail 2" & " " & "3/4 X 2 X 12" & " " & lstWoodType.List(lstWoodType.ListIndex) tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CSide1", "Side 1" & " " & "3/4 X 11 1/4 X 12" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CSide2", "Side 2" & " " & "3/4 X 11 1/4 X 12" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CBack", "Back" & " " & "3/4 X 10 1/2 X 12" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CTop", "Top" & " " & "3/4 X 10 1/2 X 11 1/4" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" tvBillOfMaterial.Nodes.Add "Case", tvwChild, "CBottom", "Bottom" & " " & "3/4 X 10 1/2 X 11 1/4" & " " & lstWoodType.List(lstWoodType.ListIndex) & " Plywood" sglBasePrice = 60 End Select Select Case lstWoodType.List(lstWoodType.ListIndex) Case "Birch" sglWoodPriceMultiplier = 3.2 Case "Cedar" sglWoodPriceMultiplier = 2.4 Case "Cherry" sglWoodPriceMultiplier = 6 Case "Douglas Fir" sglWoodPriceMultiplier = 2.8 Case "Ebony" sglWoodPriceMultiplier = 12 Case "Maple" sglWoodPriceMultiplier = 3.3 Case "Pine" sglWoodPriceMultiplier = 1.5 Case "Poplar" sglWoodPriceMultiplier = 1.3 Case "Red Oak" sglWoodPriceMultiplier = 2.5 Case "Redwood" sglWoodPriceMultiplier = 6 Case "Spruce" sglWoodPriceMultiplier = 1 Case "White Oak" sglWoodPriceMultiplier = 3 End Select If sglPriceMarkup = 0 Then sglPriceMarkup = 0.3 End If sglPrice = Round((sglBasePrice * sglWoodPriceMultiplier) * (1 + sglPriceMarkup), 2) txtSalesPrice = Format(sglPrice, "$0.00") txtManufactureCost = Format(Round((sglBasePrice * sglWoodPriceMultiplier), 2), "$0.00") End If For i = 1 To tvBillOfMaterial.Nodes.Count 'Force all of the nodes to appear expanded tvBillOfMaterial.Nodes(i).Expanded = True Next i If tvBillOfMaterial.Nodes.Count > 0 Then tvBillOfMaterial.Nodes(1).Selected = True End If End Sub Private Sub cmdExit_Click() 'Set the variable to indicate that the user did not force the window to close intFormCancelled = False 'Make the form disappear Me.Hide End Sub Private Sub lstCabinetType_Click() Calculate End Sub Private Sub lstWoodType_Click() Calculate End Sub Private Sub UserForm_Initialize() Dim lngResult As Long Dim strSQL As String Dim snpData As ADODB.Recordset On Error Resume Next Set snpData = New ADODB.Recordset lngResult = ConnectDatabase If lngResult = True Then strSQL = "SELECT" & vbCrLf strSQL = strSQL & " WOOD_TYPE" & vbCrLf strSQL = strSQL & "FROM" & vbCrLf strSQL = strSQL & " CABINET_BUILDER_WOOD" & vbCrLf strSQL = strSQL & "ORDER BY" & vbCrLf strSQL = strSQL & " WOOD_TYPE" snpData.Open strSQL, dbDatabase If snpData.State = 1 Then Do While Not snpData.EOF lstWoodType.AddItem snpData("wood_type") snpData.MoveNext Loop snpData.Close End If strSQL = "SELECT" & vbCrLf strSQL = strSQL & " CABINET_TYPE" & vbCrLf strSQL = strSQL & "FROM" & vbCrLf strSQL = strSQL & " CABINET_BUILDER_CAB_TYPE" & vbCrLf strSQL = strSQL & "ORDER BY" & vbCrLf strSQL = strSQL & " CABINET_TYPE" snpData.Open strSQL, dbDatabase If snpData.State = 1 Then Do While Not snpData.EOF lstCabinetType.AddItem snpData("cabinet_type") snpData.MoveNext Loop snpData.Close End If End If Set snpData = Nothing 'lstWoodType.List = Array("Birch", "Cedar", "Cherry", "Douglas Fir", "Ebony", "Maple", "Pine", "Poplar", "Red Oak", "Redwood", "Spruce", "White Oak") 'lstCabinetType.List = Array("Base Cabinet Full Depth", "Base Cabinet Half Depth", "Full Height Base Cabinet", "Upper Cabinet", "Upper Cabinet Narrow") End Sub Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer) 'Prevent the form from being unloaded Cancel = True 'Set the variable to indicate that the user closed the window intFormCancelled = True 'Make the form disappear Me.Hide End Sub
To keep matters simple, I elected to not pull up the costs for the various cabinets, the price premiums for different wood types, or the bill of material from the database. If all was successful, at this point you should be able to type the following into the Immediate Window to show the userform:
a = ShowCabinetBuilder( 1 )
Almost done, now we just need the VBS script that will interact with the Excel workbook. Use Notepad to create a new text file named “C:\ExcelMacroFunctionsDemo.vbs” and add the following code:
Dim i Dim objExcel Dim objShell Dim sglResult Dim strResult Dim strBOM Dim strMaterial Dim strOut Dim strOpenDate Dim strVendorID Set objExcel = CreateObject("Excel.Application") strOpenDate = cDate("January 7, 2010") strVendorID = "MyCompanyHere" strOut = "" With objExcel 'Open the Excel file containing the macro functions .Workbooks.Open "C:\ExcelMacroFunctionDemo.xls" 'Excute a standard macro located in mdlGlobal, pass in the vendor ID from Visual strResult = .Application.Run("ReverseString", strVendorID) strOut = strOut & "Execute a Custom Excel Macro:" & vbCrLf strOut = strOut & " The vendor ID spelled backward is " & strResult & vbCrLf 'Excute a macro located in mdlGlobal that calls an internal Excel function strResult = .Application.Run("ToRoman", Year(strOpenDate)) strOut = strOut & vbCrLf & "Execute a Custom Excel Macro - Calls Int. Excel Func:" & vbCrLf strOut = strOut & " The open date year in Roman numerals is " & strResult & vbCrLf 'Excute a macro located in mdlGlobal that calls an internal Excel function, function not available on Excel 2003 'strResult = .Application.Run("ToHexadecimal", Year(strOpenDate)) 'strOut = strOut & "The open date year in Hexadecimal is " & strResult & vbCrLf 'Excute a macro located in mdlGlobal that calls an internal Excel function strResult = .Application.Run("CalculateCombinations", 5, 3) strOut = strOut & " There are " & strResult & " cominations of 5 objects arranged in 3 slots" & vbCrLf strResult = .Application.Run("CalculateCombinations", 5, 4) strOut = strOut & " There are " & strResult & " cominations of 5 objects arranged in 4 slots" & vbCrLf strResult = .Application.Run("CalculatePermutations", 5, 3) strOut = strOut & " There are " & strResult & " permutations of 5 objects arranged in 3 slots" & vbCrLf strResult = .Application.Run("CalculatePermutations", 5, 4) strOut = strOut & " There are " & strResult & " permutations of 5 objects arranged in 4 slots" & vbCrLf 'Excute a macro located in mdlGlobal that calls an internal Excel function with multiple parameters sglResult = .Application.Run("CalculateInterestPayment", 0.07, 12, 4 * 12, 30000) strOut = strOut & " A monthly payment on a $30,000 loan at 7% interest is $" & FormatNumber(sglResult, 2) & " for 4 years" & vbCrLf 'Directly execute an internal Excel function, function not available on Excel 2003 'sglResult = .Application.WorksheetFunction.NetworkDays(Date, cDate("12/31/" & cStr(Year(Now)))) 'strOut = strOut & "There are " & cStr(sglResult) & " week days left in the year" & vbCrLf 'Directly execute an internal Excel function strOut = strOut & vbCrLf & "Directly Execute Internal Excel Func:" & vbCrLf sglResult = .Application.WorksheetFunction.Average(10,3,4,5,6,100) strOut = strOut & " The average of 10,3,4,5,6,100 is " & cStr(sglResult) & vbCrLf sglResult = .Application.WorksheetFunction.Product(10,3,4,5,6,100) strOut = strOut & " The product of 10,3,4,5,6,100 is " & cStr(sglResult) & vbCrLf sglResult = .Application.WorksheetFunction.StDev(10,3,4,5,6,100) strOut = strOut & " The standard deviation of 10,3,4,5,6,100 is " & cStr(sglResult) & vbCrLf MsgBox strOut, vbInformation, "Excel Macro Functions" strOut = "" 'Excute a macro located in mdlGlobal that retrieves a value from one of Visual's INI files strOut = strOut & "Read Values from Visual INIs:" & vbCrLf strResult = .Application.Run("VisualINIGet", "PlanningWindow", "ShowUnreleased", "Visual.ini") If (Left(Ucase(strResult), 1) = "Y") Or (Left(Ucase(strResult), 1) = "1") Then strOut = strOut & " The Material Planning Window will show Unreleased orders" & vbCrLf Else strOut = strOut & " The Material Planning Window will show Not Unreleased orders" & vbCrLf End IF strResult = .Application.Run("VisualINIGet", "Work Orders", "table", "vmbrowse.ini") strOut = strOut & " WO Browse tables=: " & strResult & vbCrLf strResult = .Application.Run("VisualINIGet", "Work Orders", "where", "vmbrowse.ini") strOut = strOut & " WO Browse where=: " & strResult & vbCrLf strResult = .Application.Run("VisualINIGet", "Work Orders", "decode", "vmbrowse.ini") strOut = strOut & " WO Browse columns=: " & strResult & vbCrLf MsgBox strOut, vbInformation, "Excel Macro Functions" strOut = "" 'Excute a macro located in mdlGlobal that retrieves values from the registry strOut = strOut & "Read Values from Windows Registry:" & vbCrLf strResult = .Application.Run("RegistryGetEntry", "Control Panel\Desktop", "Wallpaper") strOut = strOut & " Desktop Picture: " & strResult & vbCrlf 'Excute a macro located in mdlGlobal that retrieves environment variables strOut = strOut & vbCrLf & "Read Values from Windows Env Variables:" & vbCrLf strResult = .Application.Run("EnvironmentVariable", "username") strOut = strOut & " User: " & strResult strResult = .Application.Run("EnvironmentVariable", "computername") strOut = strOut & " is logged into the computer: " & strResult strResult = .Application.Run("EnvironmentVariable", "windir") strOut = strOut & " with the Windows folder located in: " & strResult strResult = .Application.Run("EnvironmentVariable", "temp") strOut = strOut & " and the temp folder is located in: " & strResult MsgBox strOut, vbInformation, "Excel Macro Functions" 'Show an Excel user form by calling a public macro function in the located in mdlGlobal module strResult = InputBox("Enter Markup Percent (for example 50) for Cabinet Builder", "Excel Macro Functions") If IsNumeric(strResult) Then 'with the help of custom program, set a 1 second delay, then force the window to the top Set objShell = CreateObject("WScript.Shell") objShell.Run("C:\BringToTop.exe " & Chr(34) & "Cabinet Builder" & Chr(34) & " 1") strBOM = .Application.Run("ShowCabinetBuilder", cSng(strResult) / 100) Set objShell = Nothing 'BOM is tab delimited, split apart strMaterial = Split(cStr(strBOM), vbTab) strOut = "Show an Excel user form on demand:" & vbCrLf strOut = strOut & "Bill of Material" & vbCrLf For i = 0 To UBound(strMaterial) strOut = strOut & strMaterial(i) & vbCrLf Next MsgBox strOut, vbInformation, "Excel Macro Functions" End If End With objExcel.DisplayAlerts = False objExcel.ActiveWorkbook.Saved = True objExcel.Quit Set objExcel = Nothing
Now for a test run. Save the Excel worksheet and exit Excel. In Windows Explorer, double-click the C:\ExcelMacroFunctionDemo.vbs file. The second screen capture will likely be blank if you try to run the script as it is, but my output looks like the following:
Clicking OK displayed the following on my computer, but it will probably be blank on your computer:
Clicking OK causes the VBS macro to access the Excel macro code that retrieves information from the Windows registry and environment variables:
Next, the computer will ask for a mark up percent. Specify a value, such as 50 or 100:
Next, the cabinet builder interface appears. Pick a type of wood and a cabinet type. The percentage specified earlier will be multiplied by the manufacture cost to arrive at the sales price. A fake bill of material will appear near the bottom of the window.
When the Exit button is clicked, the bill of material is returned to the VBS script, and then displayed on the screen.
While this specific example might not be very useful, it does demonstrate how to use an Excel userform as an interface for a VBS script – and the username and password used to access the database are not in clear text in the VBS script.
Shortcut: save all of the following in the root of the C:\ drive and remove the .doc extension at the end of the filenames.
BringToTop.exe (only needed on Vista and Windows 7)
Recent Comments