PIC

Cuatro años de obras y 20 de explotación. Las actuaciones del PIC (el denominado Plan Junker) estaban pensadas para cuatro años de intensas obras y 30 años de explotación en manos privadas. Estos son los 27 proyectos que lo integran, con la premisa de que estén incluídos en la red transeuropea de transportes.
  • Eje Este-Oeste de la A-7. Alicante-Murcia.
  • Autovía a-30 Murcia. ( Eje Norte-Sur).
  • Autovía A-73. Burgos-Aguilar de Campoo.
  • Prolongación de la A-7.VilanovaD’Alcolea y Traiguera (Castellón).
  • A-32. Linares-Albacete (en Albacete)
  • Variante a la N-120. Porriño-Vigo.
  • Duplicación By Pass A-7 Valencia.
  • Mejora de la capacidad de la A-62. Tramos Dueñas-Cigales-Simancas-Tordesillas.
  • Autovía A-8. Laredo (Cantabria)-Límite provincial de Vizcaya.
  • Variante Autovía A-1 en Madrid.
  • Aumento de capacidad en la N-II. Girona-La Jonquera.
  • Tercer carril A-8. Gijón- Avilés.
  • A-32. Linares-Albacete (Jaén)
  • Autovía A-11. Zamora-Frontera portuguesa.
  • Autovía A-15. Soria-Límite provincial Navarra.
  • A-1. Madrid-El Molar.
  • A-1. El Molar-Sto. Tomé del Puerto.
  • A-3. Madrid (M-30)-Lím. Prov. Cuenca.
  • A-4. Km. 67,5 (R4)-Puerto Lápice.
  • A-4. Límite provincial Jaén/Ciudad Real – Lim. prov. Córdoba/Jaén.
  • A-4. Lím. prov. Córdoba/Jaén – Lím. prov. Sevilla/Córdoba
  • A-4. Lím. prov. Sevilla/Córdoba – Sevilla
  • A-5. Madrid – Toledo Km 4 – 168
  • A-2. Igualada – Martorell
  • A-6. Adanero – Benavente
  • Autovía SE-40. Túneles Sur del Guadalquivir – Embocadura Oeste. 48-SE-4520ª
  • Autovía SE-40. Tramo A-66 / A-49.

Microsoft Office XP Developer (MOD)

[adapted from https://www.itprotoday.com/office-365/microsoft-office-2000-developer ] Starting from 2000 version, Microsoft introduced the Developer edition (MOD). MOD provides all the tools you need: COM AddInDesigners, data-bound controls, the Package and Deployment Wizard, Visual SourceSafe (VSS) integration with Visual Basic for Applications (VBA), and the Code Librarian. In MOD, Microsoft expands Office applications’ object models. More important, MOD documents these object models well. To optimally use MOD, you must know applications’ object models. With this knowledge, you can choose whether to use an Office facility or write code to gain functionality. For example, you can write a routine that counts the words in a Microsoft Word document or you can use Word’s inherent Word Count feature. You can find MOD’s object model documentation here. Beyond the developer tools contained in Office XP Developer, the product integrates a number of key productivity enhancements, including:
  • Smart Tags, which can help users easily access information by automatically linking them to rich, up-to-date corporate and Web information directly from within Office documents.
  • XML Designer, which lets developers create and manipulate XML data easily, and provides a Source View for working with XML and XSD (XML schema definition) files.
  • COM Add-In Designer, which developers can use to create stand-alone COM add-ins (DLLs) for use in any or all of the Office applications.
  • The Code Librarian, which offers a drag-and-drop database for storing and retrieving code modules, functions and code snippets, allows developers to retrieve the tools they need while they are creating their applications. Code Librarian is a database that contains example code and scripts for all the things you’d want to work on in Office XP.
  • A VBA Code Commenter, which makes it easier to add comments into code using customizable templates, and VBA Error Handler helps developers create more professional applications that are easier to debug and support.
  • A collection of documentation resources, including the Microsoft Office XP Developer’s Guide , to help developers learn the product quickly, as well as prewritten code for standard routines for VBA and the Visual Studio development environment, workflow samples and step-by-step white papers that help developers learn to build workflow solutions.
  • Developer-only versions of Exchange 2000 Server and SQL Server 2000.
  • Developers can create or edit XML code by typing directly in the Source View editor window. The designer color-codes the XML source code as it is input and automatically completes tags, offering a pop-up list of available properties as a tag is inserted.
  • Native XML support in Excel and Access enables Office developers to create data-driven solutions that share data with a virtually unlimited number of sources, and interoperate with external applications that are within the corporation or over the Web.
  • For example, Medicity, a provider of the only secure, vendor-neutral Internet platform for physicians, created an Office XP solution built on smart tags and XML technology. The solution automatically triggers a smart tag option and generates a letter that is embedded within patient information. This automation allows patients to be notified more quickly and accurately than before. Because physicians and staff can create accurate letters in seconds, they can spend more time focusing on patient care.
Many of MOD’s tools are part of the standard Office package and other Microsoft products. For example, Microsoft has integrated Access’s database diagramming tools into MOD. You can use the Access database diagramming tool by selecting a database’s Database Diagrams option in MOD. This option provides a default Relationships diagram that shows the database’s entity-relationship model. MOD lets you share this diagram with Microsoft SQL Server 7.0 and Visual Studio (VS) 6.0 so that you can create tables. If you lack the proper rights to create tables, you can pass diagrams to a DBA who can create tables. MOD also includes Microsoft Database Engine (MSDE), which is an alternative database engine for Access. Think of MSDE as a runtime version of SQL Server. Because MSDE is SQL Server-compatible, you can develop Access databases that port to SQL Server with little or no modification. The Jet 4.0 engine is the default Access engine, so developers can still distribute an Access runtime program as well as distribute MSDE with their applications. Unlike conventional SQL Server, MSDE runs on Windows 9x. An Access Project file, which has an .adp extension, represents Access databases stored in SQL Server or MSDE. This file contains the Access databases’ code, forms, reports, macros, modules, and HTML-based database objects, and information that tells the system to use the OLE DB connector to connect to Access through SQL Server. MSDE looks like SQL Server to users and other programs, but it lacks many of SQL Server’s high-end management and development tools, such as Enterprise Manager. MSDE includes the Service Manager, which lets you stop and restart the service; network configuration tools, which ensure that the MSDE server uses the proper network protocols; and the Data Transformation Services (DTS) wizard, which shows you how to import and export data. MSDE limits database size to 2GB and performs adequately with as many as five concurrent users.

COM AddIns

One of MOD’s unique capabilities is that it lets you use COM AddIns to write software that works across the entire Office suite. COM AddIns are special DLLs that all Office applications can invoke. You can write these AddIns in any compiled language that can handle COM and create DLLs. COM AddIns can access the Office object model, so you can write code that is generically applicable to Office applications. Through the Office object model, COM AddIns can also gain access to SQL Server or MSDE databases and perform sophisticated operations on the data. To help you get started with COM AddIns, MOD provides AddInDesigners, which are dialog boxes that contain options that create a component shell. In an AddInDesigner dialog box, you can specify identification information, the application you’re targeting, and specific application versions (e.g., Word 9.0). You can customize load behavior by selecting from the following options: None, Load on demand, Startup, and Load at next startup only. You can specify the AddIn’s DLL name and location. In addition, you can specify a Registry key from which the AddIn can retrieve external data. By default, MOD writes AddIns in the VBA environment, but Microsoft provides templates for you to write AddIns in Visual C++ (VC++), Visual Basic (VB), and Visual J++ (VJ++). These templates aren’t installed by default, so you must go to the \odetools\v9\samples\unsupported\mkaddin directory to install them.

Data-Bound Controls

MOD comes with several new data-bound controls, such as bound lists and bound data grids. To bind the controls to data, you use MOD’s Data Environment Designer (DED). The DED lets you use ADO and OLE DB to define queries and save them in a form that you can pass to other users for use in their queries. To create a connection in a VBA environment, select Data Environment from the Insert menu. After VBA inserts a connection object into your project, right-click the object, select Properties, and fill out the Connection dialog box to connect the object to an ADO, OLE DB, or other database source. Next, choose a driver (e.g., ODBC, OLAP, Oracle, SQL Server) and enter the server and database you’ll be working with. To test the connection in the same dialog box you created it in, click Test Connection. You then use ADO to command the database. To change the database’s properties, select Add Command. As you change the database’s properties, the DED progressively queries the database, simplifying the building process. Data-bound controls have been around for some time, and the DED first shipped in VB 6.0. But Microsoft’s inclusion of the DED in MOD represents a qualitative leap in ease-of-use for building data-aware applications. Building read/write database applications has never been this easy.

Package and Deployment Wizard

Any software deployment can be difficult, but deploying Office solutions is especially arduous. Office application deployment is complex and involves ActiveX controls and templates for numerous application types, graphics, and add-ons. MOD’s Package and Deployment Wizard simplifies this process by letting you build a deployment package on 3.5″ disks, CD-ROMs, a LAN drive, or an intranet location. To create a deployment package, select Package and Deployment Wizard from the Add-Ins menu. The system presents you with three options: Package, Deploy, and Manage Scripts. The Package option lets you bundle different files into one dependency file (i.e., a file that defines the rules for installing the program from any location). The Deploy option sends the dependency file to a server, file share, or intranet location from which users can install it. The Manage Scripts option is a simple interface from which you can delete, rename, and duplicate deployment packages. When you invoke the Package option, the system asks for the DLL that contains the component you created (i.e., the DLL you specified for the COM AddIn), then analyzes the DLL and creates a list of dependencies (i.e., a list of files it wants to include in the package). This list probably includes the contents of the DLL, OLE custom control (OCX) files you might have used to build the DLL, Help files and HTML you created, and setup.exe and other overhead files. In addition, if you used MSDE, you’ll find it in the dependency list. At this point, you can opt to include the traditional Access runtime. You then decide whether you want the wizard to create one compressed cabinet format (CAB) file or to split the file into 1.44MB 3.5″ disk-sized chunks. You define the installation’s title and, optionally, a postinstallation program (e.g., display a README file, launch an application). You define where the application links go in the Start menu. Next, you use generic environment names, such as $(WinSysPath) for \windows\system, to specify the deployment package files’ location on the client system. (Change the default locations at your own risk.) You can mark a file (e.g., the COM AddIn you created) as a shared file so that the system establishes a counter in the Registry for the number of applications that use that file. The system won’t uninstall that file until the counter hits 0. Click Finish to complete the package. The system produces a report that lists any problems that occurred during the package-creation process. To install the package, users run setup.exe from where you placed it in the wizard.

VSS Integration with VBA 

VSS is a source-code revision-control tool—a necessary facility in team-development situations. When multiple developers work on a project, two or more of them might work on the same source code file with undefined and undesirable results. VSS is a special database for source-code files. Developers must check out code from the database to use VSS. When they’re finished, they check the code back in. While a user or tool (e.g., a compiler) has code checked out, no other users or tools can access the code. First, go to MOD’s AddIn Manager and instruct MOD to load the VBA Source Code Control. Next, add the project to VSS by selecting VBA Source Code Control from the Add-Ins menu, assigning a project name, and specifying which files to control. When you right-click a file in your VBA project window, you’ll see the check-in and check-out options. You can also look in the Source Code Control Status window to see who has which project files open.

Code Librarian 

The Code Librarian is a database of code snippets—hundreds of code bits that perform isolated actions. When you’re programming, you often write routines for the same common tasks and you usually have to look up each task’s details. The Code Librarian, lets you browse or search for the task you need (e.g., OLE automating an Excel object) and drag the code into your project. The Code Librarian comes preloaded with snippets of Office code, mostly VBA, but you can extend it to any language. The libraries are Access (i.e., .mdb) databases, so they’re easy to distribute. The Code Librarian stores snippets in a hierarchical list with attached keywords. You can search the keywords or the database’s contents. You can add your snippets to the database, or you can create a database of your snippets. The Code Commenter is also a useful tool. You usually want to comment your code in a consistent way (e.g., a particular header-comment structure for a module or function). The Code Commenter enforces these rules. After you invoke the Code Commenter from the Add-Ins menu, you specify that the comments apply to a specific procedure, an entire module, or an entire project. You specify your name and initials, and a template file. When you click OK, the Code Commenter uses the template and applies the comments to the code in whatever scope you specified. The Code Commenter works only in VBA.

Access Workflow Designer 

MOD’s Access Workflow Designer for SQL Server is a separate tool for creating Access applications that implement processes with rules. Access Workflow Designer lets you implement the stages of a project in a flowchart-like design. You assign code at different steps to implement the work at that point, and you assign code on the connectors to implement the decision-making process. You can find the Access Workflow Designer at http://msdn.microsoft.com/officedev/awd.

Printers

Again, from My Engineering World, I stomped on this post, dealing with printers. Is not that I do a lot of printing, but sometimes come handy when you need to send to PDF. I have several other procedures coded in the past that perform these tasks, but decided to try to work on them again, and retailor to better fit as general procedures/functions.
'----------------------------------------------------------------------------------
' Functions to deal with printers from VBA:
'
' • PrinterExists:           Checks if there is a printer installed with the given name.
' • IsDefaultPrinter:        Checks if the given printer corresponds to the default windows printer.
' • SetDefaultPrinter:       Makes the given printer to be the default one.
'
' • SelectPrinter:           Shows the list of installed printers and retrieves one.
'
' • GetInstalledPrinters:    Loops through all the installed printers and outputs to an array.
'                            Moreover, it checks if each printer is the default one.
'
' • SetAsTheDefaultPrinter:  The user selects a printer to becomes the default one.
'----------------------------------------------------------------------------------

Private computer            As String
Private wmiService          As Object
Private installedPrinters   As Object 'Variant
Private printer             As Object

Public Function PrinterExists(ByVal printerName As String) As Boolean
    On Error Resume Next

    'Check if the printer name is empty.
    If printerName = vbNullString Then Exit Function

    'Set the computer (Dot means the computer running the code).
    computer = "."

    'Get the WMI object
    Set wmiService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & computer & "\root\cimv2")

    'Retrieve information about the installed printers (by running a query).
    Set installedPrinters = wmiService.ExecQuery("Select * from Win32_Printer")

    'If an error occurs in the previous step, the function should exit and return False.
    If Err.Number  0 Then GoTo ExitProc

    'Loop through all the installed printers.
    'If the given name matches to any of the installed printers, exit the loop and return True.
    For Each printer In installedPrinters
        If UCase(printer.Name) = UCase(printerName) Then
            PrinterExists = True
            GoTo ExitProc
        End If
    Next printer

ExitProc:
    On Error GoTo 0
End Function

Public Function IsDefaultPrinter(ByVal printerName As String) As Boolean
    On Error Resume Next

    'Check if the printer name is empty.
    If printerName = vbNullString Then Exit Function

    'Set the computer (Dot means the computer running the code).
    computer = "."

    'Get the WMI object
    Set wmiService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & computer & "\root\cimv2")

    'Retrieve information about the installed printers (by running a query).
    Set installedPrinters = wmiService.ExecQuery("Select * from Win32_Printer")

    'If an error occurs in the previous step, the function should exit and return False.
    If Err.Number  0 Then GoTo ExitProc

    'Loop through all the installed printers. If the given name matches to any of the installed printers
    'and the Default property is set to True, exit the loop and return True.
    For Each printer In installedPrinters
        If UCase(printer.Name) = UCase(printerName) And printer.Default = True Then
            IsDefaultPrinter = True
            Exit Function
        End If
    Next printer

ExitProc:
    On Error GoTo 0
End Function

Public Function SelectPrinter() As String
    Dim aPrinter() As String
    Dim lgPrinter As Long
    Dim strMsg As String

    On Error Resume Next

    aPrinter() = VBA.Split(GetInstalledPrinters, vbCrLf)
    ReDim Preserve aPrinter(LBound(aPrinter) To UBound(aPrinter) - 1)
    strMsg = "Default        PrinterName" & vbCrLf
    For lgPrinter = LBound(aPrinter) + 1 To UBound(aPrinter)
        strMsg = strMsg & lgPrinter & ": " & aPrinter(lgPrinter) & vbCrLf
    Next lgPrinter

    lgPrinter = VBA.CLng(VBA.InputBox("Select printer (0 for default):" & vbCrLf & vbCrLf & strMsg, _
                                      "Select printer", 1))
    If lgPrinter = 0 Then
        GoTo ExitProc
    ElseIf lgPrinter  UBound(aPrinter) Then
        GoTo ExitProc
    Else
        SelectPrinter = VBA.Trim$(VBA.Mid$(VBA.Trim$(aPrinter(lgPrinter)), 2))
    End If

ExitProc:
    On Error GoTo 0
End Function

Public Function SetDefaultPrinter(Optional ByVal printerName As String = vbNullString) As Boolean
    Dim wscNetwork As Object

    On Error Resume Next

    'Check if the printer name is empty.
    'If printerName = vbNullString Then GoTo ExitProc
    If printerName = vbNullString Then
        'Select printer
        printerName = SelectPrinter
    End If

    If Not PrinterExists(printerName) Then
        MsgBox "Printer [" & printerName & "] does not exist. Won't set printer", vbExclamation, "W A R N I N G"
        GoTo ExitProc
    End If

    'Test if the printer is already the default one. If yes, return True.
    If IsDefaultPrinter(printerName) = True Then
        SetDefaultPrinter = True
        GoTo ExitProc
    End If

    'The printer is not the default one. Create the WScript.Network object.
    Set wscNetwork = CreateObject("WScript.Network")

    'If the WScript.Network object was not created, exit.
    If wscNetwork Is Nothing Then GoTo ExitProc

    'Set the given printer to be the default one.
    wscNetwork.SetDefaultPrinter printerName

    'Release the WScript.Network object.
    Set wscNetwork = Nothing

    'Check (again) if after the change, the given printer is indeed the default one.
    SetDefaultPrinter = IsDefaultPrinter(printerName)

ExitProc:
    On Error GoTo 0
End Function

Public Function GetInstalledPrinters() As String
    Dim computer            As String
    Dim wmiService          As Object
    Dim installedPrinters   As Object 'Variant
    Dim printer             As Object
    Dim strPrinters         As String

    On Error Resume Next

    'Set the computer. Dot means the computer running the code.
    computer = "."

    'Get the WMI object
    Set wmiService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & computer & "\root\cimv2")

    'Retrieve information about the installed printers (by running a query).
    Set installedPrinters = wmiService.ExecQuery("Select * from Win32_Printer")

    'If an error occurs in the previous step, inform the user.
    If Err.Number  0 Then
        MsgBox "Could not retrieve the printer information from WMI object!", vbCritical, "WMI Object Error"
        GoTo ExitProc
    End If

    'Loop through all the installed printers and get their name.
    'Check if one of them is the default one.
    strPrinters = "Default   PrinterName" & vbCrLf
    For Each printer In installedPrinters
        strPrinters = strPrinters & _
                      VBA.Space(5) & VBA.IIf(printer.Default, "•", "º") & VBA.Space(9) & _
                      printer.Name & vbCrLf
    Next printer

    GetInstalledPrinters = strPrinters

ExitProc:
    On Error GoTo 0
End Function

Public Sub SetAsTheDefaultPrinter()
    Dim printerName As String

    On Error Resume Next

    printerName = SelectPrinter
    Call SetDefaultPrinter(printerName)

ExitProc:
    On Error GoTo 0
End Sub


Public Function CheckPrinterStatus(ByVal strPrinterName As String) As String
' Returns a string with the printer status.

    Dim strComputer As String
    Dim objWMIService As Object
    Dim colInstalledPrinters As Variant
    
    On Error Resume Next
    
    'Set the WMI object and the check the install printers.
    strComputer = "."
    Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
    Set colInstalledPrinters = objWMIService.ExecQuery("Select * from Win32_Printer")
        
    'If an error occurs in the previous step, the function will return error.
    If Err.Number  0 Then
        CheckPrinterStatus = "Error"
        GoTo ExitProc
    End If
    
    'The function loops through all installed printers and for the selected printer,
    'checks it status.
    For Each Printer In colInstalledPrinters
        If Printer.Name = strPrinterName Then
            Select Case Printer.PrinterStatus
                Case 1: CheckPrinterStatus = "Other"
                Case 2: CheckPrinterStatus = "Unknown"
                Case 3: CheckPrinterStatus = "Idle"
                Case 4: CheckPrinterStatus = "Printing"
                Case 5: CheckPrinterStatus = "Warmup"
                Case 6: CheckPrinterStatus = "Stopped printing"
                Case 7: CheckPrinterStatus = "Offline"
                Case Else: CheckPrinterStatus = "Error"
            End Select
        End If
    Next Printer
    
    'If there is a blank status the function returns error.
    If CheckPrinterStatus = "" Then CheckPrinterStatus = "Error"
    
ExitProc:
    On Error GoTo 0
End Function
 

Access windows (in WindowsOS)

I was looking to this post of My Engineering World, about opening PDF with VBA, and it seems to be calling for reuse in other purposes different from the PDF task. It’s somewhat related to this other one, so I think I must try to dissasemble both and try to do a mashup of them. The point is that they rely in a little Microsoft app called Spy++, that is shiped with Visual Studio, but can be downloaded from here (at your own risk). Other tool that comes very handy is APIViewer, to get how the API calls are correctly defined.
What follows is a full copy paste of the posts from My Engineering World, just to do the cut/copy/paste process near me:
For the example we will manage (print a web in PDF, and open the file), there are several API functions that are beeing called
  • API functions (FindWindow, SetForegroundWindow andFindWindowEx) in order to “find” the print window of Internet Explorer and its “child” windows.
  • API functions (SendMessage and keybd_event) for changing the PDF file path.
  • API functions (FindWindow and PostMessage) for finding the opened PDF document and closing it.The above list describes more or less the sequence of actions that I followed in order to fulfil this task
Also there’s the need to do some WMI queries:
  • A custom WMI (Windows Management Instrumentation) function in order to determine if the printer has finished printing.
For the printing task, you should copy the procedures from this post. And finally, you’ll need the following tools to accomplish this project:
  • Spy++ is a utility that gives you a graphical view of the system’s processes, threads, windows, and window messages.
  • API Viewer is a utility that helps you write the API declarations, by providing the correct syntax of each function.

VBA code

' By Christos Samaras
' http://www.myengineeringworld.net

'API functions (FindWindow, SetForegroundWindow andFindWindowEx) in order
'to “find” the print window of Internet Explorer and its “child” windows.
'API functions (SendMessage and keybd_event) for changing the PDF file path.
'A custom WMI (Windows Management Instrumentation) function in order to determine if the printer has finished printing.
'API functions (FindWindow and PostMessage) for finding the opened PDF document and closing it.The above list describes more or less the sequence of actions that I followed in order to fulfil this task. However, I should mention that without the following tools it would be impossible to finish this project:

'Spy++ is a utility that gives you a graphical view of the system’s processes, threads, windows, and window messages.
'API Viewer (href="http://www.activevb.de/rubriken/apiviewer/index-apiviewereng.html") is a utility that helps you write the API declarations, by providing the correct syntax of each function.

'Shows how to use API functions in order to specify a control (i.e. combo box, listbox,...) in a specific window.
'
'It requires the following steps
' • Check the file path and if is valid, use the FollowHyperlink method in order to open the PDF file.
' • With the FindWindow API function find the window of Adobe Reader or Adobe Professional that contains the opened PDF file and bring it to foreground using the SetForegroundWindow API function.
' • Find the subsequent child windows with the FindWindowEx API function.
' • Use the SendMessage and PostMessage API functions in order to send the desired page number and window zoom value to the corresponding textboxes.

'API calls:
'Retrieves a handle to the top-level window whose class name and window name match the specified strings.
'This function does not search child windows. This function does not perform a case-sensitive search.
Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long

'Retrieves a handle to a window whose class name and window name match the specified strings.
'The function searches child windows, beginning with the one following the specified child window.
'This function does not perform a case-sensitive search.
Public Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long

'Sets the specified window's show state.
Public Declare Function ShowWindow Lib "user32.dll" (ByVal hwnd As Long, ByVal nCmdShow As Long) As Long

'Brings the thread that created the specified window into the foreground and activates the window.
'Keyboard input is directed to the window, and various visual cues are changed for the user.
'The system assigns a slightly higher priority to the thread that created the foreground
'window than it does to other threads.
Public Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As Long) As Long

'Suspends the execution of the current thread until the time-out interval elapses.
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

'Sends the specified message to a window or windows. The SendMessage function calls the window procedure
'for the specified window and does not return until the window procedure has processed the message.
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long

'Places (posts) a message in the message queue associated with the thread that created the specified
'window and returns without waiting for the thread to process the message.
Public Declare Function PostMessage Lib "user32.dll" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

'Synthesizes a keystroke. The system can use such a synthesized keystroke to generate a WM_KEYUP or
'WM_KEYDOWN message. The keyboard driver's interrupt handler calls the keybd_event function.
Public Declare Sub keybd_event Lib "user32.dll" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)

'Constants used in API functions.
Public Const SW_MAXIMIZE = 3
Public Const WM_SETTEXT = &HC
Public Const VK_DELETE = &H2E
Public Const KEYEVENTF_KEYUP = &H2
Public Const BM_CLICK = &HF5&
Public Const WM_CLOSE As Long = &H10

Public Const ¼ As Double = 0.25
Public Const ½ As Double = 0.5
Public Const ¾ As Double = 0.75
Public Const ¹÷³ As Double = 0.333
Public Const ²÷³ As Double = 0.667

Private Sub TestPDF()
    OpenPDF ThisWorkbook.Path & "\" & "Sample File.pdf", 6, 143
    URLToPDF ThisWorkbook.Path & "\" & "Sample File.pdf"
End Sub

Private Sub URLToPDF(pageURL As String, PDFFullPath As String)
' main procedure.
' Loops through all the URLs at column C and print the web pages as PDF using Adobe Professional.
' First, it checks the folder’s path that was selected in the previous step.
' If the folder’s path exists and is not blank it tries to find if there are any
' illegal characters in the PDF files’ path;
' if it finds anyone it replaces it with a “-“.
' Afterwards, the code calls the WebpageToPDF sub using as parameters the URL
' address and the corresponding PDF file name that is provided in the main sheet (by the user).
    Dim arrSpecialChr() As String
    Dim dblSpCharFound  As Double
    Dim PDFFullPath     As String
    Dim i               As Long
    Dim j               As Integer

    'An array with special characters that cannot be used for naming a file.
    arrSpecialChr() = Split("\ / : * ? " & Chr$(34) & "  |", " ")

    'Set the default printer to PDFCreator (or whatever you like).
    SetDefaultPrinter "PDFCreator"

    'Convert the URLs to PDFs.
    On Error Resume Next

    'Check if the PDF name contains a special/illegal character.
    For j = LBound(arrSpecialChr) To UBound(arrSpecialChr)
        dblSpCharFound = VBA.InStr(1, PDFFullPath, arrSpecialChr(j))
        If dblSpCharFound > 0 Then
            PDFFullPath = VBA.Replace(PDFFullPath, arrSpecialChr(j), "-")
        End If
    Next j
    'PDFFullPath = PDFFolder & PDFPath

    On Error GoTo 0

    'Save the PDF files to the selected folder.
    Call WebpageToPDF(pageURL, PDFFullPath & ".pdf")

    'Inform the user that macro finished.
    MsgBox "Web page successfully saved as PDF!", vbInformation, "Done"

End Sub

Private Sub WebpageToPDF(pageURL As String, PDFPath As String)
'Creates a new web browser object, opens a selected URL and then prints to selected printer
'The API functions FindWindow and SetForegroundWindow are used in order to find the IE window
'and bring it to the foreground (above other windows).

   'The macro needs a reference to Windows Script Host Object Model Library, as well
   'as to the Microsoft Internet Controls Library in order to work.
   'From VBA editor go to Tools -> References -> add the two references.
   'Or you can find them at C:\Windows\system32\wshom.ocx and C:\Windows\system32\ieframe.dll.

    Dim WebBrowser   As InternetExplorer
    Dim StartTime    As Date
    Dim intRet       As Long

    'Create new web browser object, make it visible,
    'maximize the window and navigate to the desired url.
    Set WebBrowser = New InternetExplorer
    WebBrowser.Visible = True
    ShowWindow WebBrowser.hwnd, SW_MAXIMIZE
    WebBrowser.Navigate (pageURL)

    'Wait until the web page is fully loaded.
    Do
        DoEvents
    Loop Until WebBrowser.ReadyState = READYSTATE_COMPLETE

    'Check if the internet explorer window exists.
    StartTime = Now()
    Do Until Now() > StartTime + TimeValue("00:00:05")
        intRet = 0
        DoEvents
        'IEFrame is the class name for internet explorer.
        intRet = FindWindow("IEFrame", vbNullString)
        If intRet  0 Then Exit Do
    Loop

    'If the IE window exists, print the web page as PDF.
    If intRet  0 Then
        Call SetForegroundWindow(intRet)
        WebBrowser.ExecWB OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER
        Call PDFPrint(PDFPath)
        SetForegroundWindow (intRet)
    End If

    'Release the web browser object.
    WebBrowser.Quit
    Set WebBrowser = Nothing

End Sub

Private Sub PDFPrint(ByVal strPDFPath As String)
'!!!!!!!!!!!!!!!!!!!!!!
'In order to change the default name (and path) of the save as dialog,
'I used Spy++ in order to specify and edit the combo box
'that contains the file name.
'The sequence goes like this: Save PDF File As (main window) ? DUIViewWndClassName (first child) ? DirectUIHWND (second child) ? FloatNotifySink (third child) ? ComboBox ? Edit.
'Having found the Edit property of the combo box the SendMessage API is used to send the PDF file path.
'
'Well, here there is a tricky part: for some unknown reason if you pass the PDF path using
'directly the SendMessage function and then press the Save button (again, using SendMessage)
'the file is not saved with the desired name and at the desired path!
'The file is named by the URL (for example vba-macro-to-convert-) and is saved at the last folder
'you selected within IE window! Quite strange….

'I overcome this obstacle by doing a small trick: when I pass the PDF path in the combo box
'I use a space before the path. So in the combo box, the SendMessage function passes a string like
'" " & "C:\Users\???st??\Desktop\New folder\" & "Daily Schedule Charts.pdf" (notice the blank space before C).
'Then, I delete this space using the keybd_event API function.
'This function simulates a key press (here the delete button) and a key release.
'Why I did this? Well, because when I was experimenting (without using code)
'I saw that the PDF path changed only if there was a keyboard change.
'So, I tried to simulate this observation using VBA code.

'Having passed the PDF path successfully and pressed the Save button,
'then the macro checks if the printer has finished printing (i.e. creating the PDF file)
'by using the CheckPrinterStatus function.
'If the function returns "Idle" it means that the printing finished.

'Finally, since the Adobe Professional opens the file after finishing the printing,
'a combination of FindWindow and PostMessage API functions are used in order to find the PDF window and close it.
'!!!!!!!!!!!!!!!!!!!!!!

    'Prints a web page as PDF file using Adobe Professional.
    'API functions are used to specify the necessary windows while
    'a WMI function is used to check printer's status.

    Dim Ret                 As Long
    Dim ChildRet            As Long
    Dim ChildRet2           As Long
    Dim ChildRet3           As Long
    Dim comboRet            As Long
    Dim editRet             As Long
    Dim ChildSaveButton     As Long
    Dim PDFRet              As Long
    Dim PDFName             As String
    Dim StartTime           As Date

    'Find the main print window.
    StartTime = Now()
    Do Until Now() > StartTime + TimeValue("00:00:05")
        Ret = 0
        DoEvents
        Ret = FindWindow(vbNullString, "Save PDF File As")
        If Ret  0 Then Exit Do
    Loop

    If Ret  0 Then
        SetForegroundWindow (Ret)
        'Find the first child window.
        StartTime = Now()
        Do Until Now() > StartTime + TimeValue("00:00:05")
            ChildRet = 0
            DoEvents
            ChildRet = FindWindowEx(Ret, ByVal 0&, "DUIViewWndClassName", vbNullString)
            If ChildRet  0 Then Exit Do
        Loop

        If ChildRet  0 Then
            'Find the second child window.
            StartTime = Now()
            Do Until Now() > StartTime + TimeValue("00:00:05")
                ChildRet2 = 0
                DoEvents
                ChildRet2 = FindWindowEx(ChildRet, ByVal 0&, "DirectUIHWND", vbNullString)
                If ChildRet2  0 Then Exit Do
            Loop

            If ChildRet2  0 Then
                'Find the third child window.
                StartTime = Now()
                Do Until Now() > StartTime + TimeValue("00:00:05")
                    ChildRet3 = 0
                    DoEvents
                    ChildRet3 = FindWindowEx(ChildRet2, ByVal 0&, "FloatNotifySink", vbNullString)
                    If ChildRet3  0 Then Exit Do
                Loop

                If ChildRet3  0 Then
                    'Find the combobox that will be edited.
                    StartTime = Now()
                    Do Until Now() > StartTime + TimeValue("00:00:05")
                        comboRet = 0
                        DoEvents
                        comboRet = FindWindowEx(ChildRet3, ByVal 0&, "ComboBox", vbNullString)
                        If comboRet  0 Then Exit Do
                    Loop

                    If comboRet  0 Then
                        'Finally, find the "edit property" of the combobox.
                        StartTime = Now()
                        Do Until Now() > StartTime + TimeValue("00:00:05")
                            editRet = 0
                            DoEvents
                            editRet = FindWindowEx(comboRet, ByVal 0&, "Edit", vbNullString)
                            If editRet  0 Then Exit Do
                        Loop

                        'Add the PDF path to the file name combobox of the print window.
                        If editRet  0 Then
                            SendMessage editRet, WM_SETTEXT, 0&, ByVal " " & strPDFPath
                            keybd_event VK_DELETE, 0, 0, 0 'press delete
                            keybd_event VK_DELETE, 0, KEYEVENTF_KEYUP, 0 ' release delete

                            'Get the PDF file name from the full path.
                            On Error Resume Next
                            PDFName = Mid(strPDFPath, WorksheetFunction.Find("*", WorksheetFunction.Substitute(strPDFPath, "\", "*", Len(strPDFPath) _
                            - Len(WorksheetFunction.Substitute(strPDFPath, "\", "")))) + 1, Len(strPDFPath))
                            On Error GoTo 0

                            'Save/print the web page by pressing the save button of the print window.
                            Sleep 1000
                            ChildSaveButton = FindWindowEx(Ret, ByVal 0&, "Button", "&Save")
                            SendMessage ChildSaveButton, BM_CLICK, 0, 0

                            'Sometimes the printing delays, especially in large colorful web pages.
                            'Here the code checks printer status and if is idle it means that the
                            'printing has finished.
                            Do Until CheckPrinterStatus("Adobe PDF") = "Idle"
                                DoEvents
                                If CheckPrinterStatus("Adobe PDF") = "Error" Then Exit Do
                            Loop

                            'Since the Adobe Professional opens after finishing the printing, find
                            'the open PDF document and close it (using a post message).
                            StartTime = Now()
                            Do Until StartTime > StartTime + TimeValue("00:00:05")
                                PDFRet = 0
                                DoEvents
                                PDFRet = FindWindow(vbNullString, PDFName & " - Adobe Acrobat Pro")
                                If PDFRet  0 Then Exit Do
                            Loop
                            If PDFRet  0 Then
                                PostMessage PDFRet, WM_CLOSE, 0&, 0&
                            End If
                        End If
                    End If
                End If
            End If
        End If
   End If
End Sub

Private Sub OpenPDF(ByVal strPDFPath As String, ByVal strPageNumber As String, ByVal strZoomValue As String)
' Opens a PDF file to a specific page and with a specific zoom.
' API functions are used to specify the necessary windows
' and send the page and zoom info to the PDF reader window.

    Dim strPDFName                  As String
    Dim lParent                     As Long
    Dim lFirstChildWindow           As Long
    Dim lSecondChildFirstWindow     As Long
    Dim lSecondChildSecondWindow    As Long
    Dim dtStartTime               As Date

    'Check if the PDF path is correct.
    If FileExists(strPDFPath) = False Then
        MsgBox "The PDF path is incorect!", vbCritical, "Wrong path"
        Exit Sub
    End If

    'Get the PDF file name from the full path.
    On Error Resume Next
    strPDFName = Mid(strPDFPath, InStrRev(strPDFPath, "\") + 1, Len(strPDFPath))
    On Error GoTo 0

    'The following line depends on the apllication you are using.
    'For Word:
    'ThisDocument.FollowHyperlink strPDFPath, NewWindow:=True
    'For Power Point:
    'ActivePresentation.FollowHyperlink strPDFPath, NewWindow:=True
    'Note that both Word & Power Point pop up a security window asking
    'for access to the specified PDf file.
    'For Access:
    'Application.FollowHyperlink strPDFPath, NewWindow:=True
    'For Excel:
    ThisWorkbook.FollowHyperlink strPDFPath, NewWindow:=True
    'Find the handle of the main/parent window.
    dtStartTime = Now()
    Do Until Now() > dtStartTime + TimeValue("00:00:05")
        lParent = 0
        DoEvents
        'For Adobe Reader.
        'lParent = FindWindow("AcrobatSDIWindow", strPDFName & " - Adobe Reader")
        'For Adobe Professional.
        lParent = FindWindow("AcrobatSDIWindow", strPDFName & " - Adobe Acrobat Pro")
        If lParent  0 Then Exit Do
    Loop

    If lParent  0 Then

        'Bring parent window to the foreground (above other windows).
        SetForegroundWindow (lParent)

        'Find the handle of the first child window.
        dtStartTime = Now()
        Do Until Now() > dtStartTime + TimeValue("00:00:05")
            lFirstChildWindow = 0
            DoEvents
            lFirstChildWindow = FindWindowEx(lParent, ByVal 0&, vbNullString, "AVUICommandWidget")
            If lFirstChildWindow  0 Then Exit Do
        Loop

        'Find the handles of the two subsequent windows.
        If lFirstChildWindow  0 Then
            dtStartTime = Now()
            Do Until Now() > dtStartTime + TimeValue("00:00:05")
                lSecondChildFirstWindow = 0
                DoEvents
                lSecondChildFirstWindow = FindWindowEx(lFirstChildWindow, ByVal 0&, "Edit", vbNullString)
                If lSecondChildFirstWindow  0 Then Exit Do
            Loop

            If lSecondChildFirstWindow  0 Then

                'Send the zoom value to the corresponding window.
                SendMessage lSecondChildFirstWindow, WM_SETTEXT, 0&, ByVal strZoomValue
                PostMessage lSecondChildFirstWindow, WM_KEYDOWN, VK_RETURN, 0

                dtStartTime = Now()
                Do Until Now() > dtStartTime + TimeValue("00:00:05")
                    lSecondChildSecondWindow = 0
                    DoEvents
                    'Notice the difference in syntax between lSecondChildSecondWindow and lSecondChildFirstWindow.
                    'lSecondChildSecondWindow is the handle of the next child window after lSecondChildFirstWindow,
                    'while both windows have as parent window the lFirstChildWindow.
                    lSecondChildSecondWindow = FindWindowEx(lFirstChildWindow, lSecondChildFirstWindow, "Edit", vbNullString)
                    If lSecondChildSecondWindow  0 Then Exit Do
                Loop

                If lSecondChildSecondWindow  0 Then
                    'Send the page number to the corresponding window.
                    SendMessage lSecondChildSecondWindow, WM_SETTEXT, 0&, ByVal strPageNumber
                    PostMessage lSecondChildSecondWindow, WM_KEYDOWN, VK_RETURN, 0
                End If
            End If
        End If
    End If

End Sub

Private Function FileExists(strFilePath As String) As Boolean
'Checks if a file exists.

    On Error Resume Next
    If Not Dir(strFilePath, vbArchive) = vbNullString Then FileExists = True
    On Error GoTo 0

End Function

Private Function FolderExists(strFolderPath As String) As Boolean
'Checks if a folder exists.

   On Error Resume Next
   If Not Dir(strFolderPath, vbDirectory) = vbNullString Then FolderExists = True
   On Error GoTo 0

End Function

Private Function FolderSelection() As String
' Shows the folder picker dialog in order the user to select
' the folder in which the downloaded files will be saved.

    Dim FoldersPath     As String

    'Show the folder picker dialog.
    With Application.FileDialog(msoFileDialogFolderPicker)
        .Title = "Select a folder to save your files..."
        .Show
        If .SelectedItems.Count = 0 Then
            MsgBox "You did't select a folder!", vbExclamation, "Canceled"
            Exit Function
        Else
            FoldersPath = .SelectedItems(1)
        End If
    End With

    'Pass the folder's path to the cell.
    FolderSelection = FoldersPath & "\"
End Function
API Viewer Window
Figure 2: The API Viewer window.
Folder Picker Dialog
Figure 3: The PDFFolderSelection sub results (folder picker dialog).
3. The URLToPDF constitutes the main procedure.  First, it checks the folder’s path that was selected in the previous step. If the folder’s path exists and is not blank it tries to find if there are any illegal characters in the PDF files’ path; if it finds anyone it replaces it with a “-“. Afterwards, the code calls the WebpageToPDF sub using as parameters the URL address and the corresponding PDF file name that is provided in the main sheet (by the user). 5. The WebpageToPDF creates a new web browser object, makes it visible, maximizes the browser window and navigates to the desired URL. If the IE window is visible it popups the print window. The API functions FindWindow and SetForegroundWindow are used in order to find the IE window and bring it to the foreground (above other windows). Then the code calls the PDFPrint procedure.
Webpage To PDF
Figure 4: WebpageToPDF sub results.
6. In order to change the default name (and path) of the save as dialog, I used Spy++ in order to specify and edit the combo box that contains the file name. The sequence goes like this: Save PDF File As (main window) → DUIViewWndClassName (first child) → DirectUIHWND (second child) → FloatNotifySink (third child) → ComboBox → Edit. Having found the Edit property of the combo box the SendMessage API is used to send the PDF file path.
Spyxx Window
Figure 5: Showing the hierarchy of Save As PDF window in Spy++.
Well, here there is a tricky part: for some unknown reason if you pass the PDF path using directly the SendMessage function and then press the Save button (again, using SendMessage) the file is not saved with the desired name and at the desired path! The file is named by the URL (for example vba-macro-to-convert-) and is saved at the last folder you selected within IE window! Quite strange…. I overcome this obstacle by doing a small trick: when I pass the PDF path in the combo box I use a space before the path. So in the combo box, the SendMessage function passes a string like “ C:\Users\Χρήστος\Desktop\New folder\ Daily Schedule Charts.pdf” (notice the blank space before C). Then, I delete this space using the keybd_event API function. This function simulates a key press (here the delete button) and a key release. Why I did this? Well, because when I was experimenting (without using code) I saw that the PDF path changed only if there was a keyboard change. So, I tried to simulate this observation using VBA code. Having passed the PDF path successfully and pressed the Save button, then the macro checks if the printer has finished printing (i.e. creating the PDF file) by using the CheckPrinterStatus function. If the function returns “Idle” it means that the printing finished. Finally, since the Adobe Professional opens the file after finishing the printing, a combination of FindWindow and PostMessage API functions are used in order to find the PDF window and close it.
    • Open PDF File With VBA
In comparison with my previous attempt the VBA code below doesn’t use the Adobe object system or the sendkeys method. Instead, a combination of various API functions is used in order to find and manipulate the page number and page zoom textboxes. The general idea behind this code can be divided in four steps:
 
  1. Check the file path and if is valid, use the FollowHyperlink method in order to open the PDF file.
  2. With the FindWindow API function find the window of Adobe Reader or Adobe Professional that contains the opened PDF file and bring it to foreground using the SetForegroundWindow API function.
  3. Find the subsequent child windows with the FindWindowEx API function.
  4. Use the SendMessage and PostMessage API functions in order to send the desired page number and window zoom value to the corresponding textboxes.
  • Windows Hierarchy In Adobe Reader-Professional
  • Similarly to my previous post, the Spy++ software was used in order to specify the windows hierarchy in Adobe Reader/Professional. The picture above shows the window tree of a sample PDF document.The VBA code in this post can be used with almost all the office programs. I have tested it with Access, Excel, Word and Power Point (both 2010 and 2003 versions) and works like charm. Since it doesn’t require any reference to Adobe library and no sendkeys are used is probably much easier to use and more reliable than my previous codes.
  VBA code The short video below demonstrates how the above VBA code can be used with  Access, Word, Power Point and Excel 2010.
[youtube https://www.youtube.com/watch?v=xadHPW2BYS0]
 

VBA RegExp (Regular Expressions)

Regular expressions are used for Pattern Matching. Maybe one of the best answers I’ve seen in StackOverflow is this about Regular expressions, from user Portland Runner, but the topic can get as tricky as this answer show, epic!. Following are a series of procedures to perform Regular Expressions operations. You can work with “Early binding” (set a reference to Microsoft VBScript Regular Expressions 5.5) or with “Late binding” through objects.

Early binding

  • Press ALT+F11 to access to the VBE.
  • Select “Tools” from the top menu.
  • Select “References”, and  check the box of “Microsoft VBScript Regular Expressions 5.5” to include in your workbook.
  • Click “OK”

Patterns

Basic definitions: - Range.
  • E.g. a-z matches an lower case letters from a to z
  • E.g. 0-5 matches any number from 0 to 5
[] Match exactly one of the objects inside these brackets.
  • E.g. [a] matches the letter a
  • E.g. [abc] matches a single letter which can be a, b or c
  • E.g. [a-z] matches any single lower case letter of the alphabet.
() Groups different matches for return purposes. See examples below. {} Multiplier for repeated copies of pattern defined before it.
  • E.g. [a]{2} matches two consecutive lower case letter a: aa
  • E.g. [a]{1,3} matches at least one and up to three lower case letter a, aa, aaa
+ Match at least one, or more, of the pattern defined before it.
  • E.g. a+ will match consecutive a’s a, aa, aaa, and so on
? Match zero or one of the pattern defined before it.
  • E.g. Pattern may or may not be present but can only be matched one time.
  • E.g. [a-z]? matches empty string or any single lower case letter.
* Match zero or more of the pattern defined before it. – E.g. Wildcard for pattern that may or may not be present. – E.g. [a-z]* matches empty string or string of lower case letters. . Matches any character except newline \n
  • E.g. a. Matches a two character string starting with a and ending with anything except \n
| OR operator
  • E.g. a|b means either a or b can be matched.
  • E.g. red|white|orange matches exactly one of the colors.
^ NOT operator
  • E.g. [^0-9] character can not contain a number
  • E.g. [^aA] character can not be lower case a or upper case A
\ Escapes special character that follows (overrides above behavior)
  • E.g. \., \\, \(, \?, \$, \^

Anchoring Patterns: ^ Match must occur at start of string
  • E.g. ^a First character must be lower case letter a
  • E.g. ^[0-9] First character must be a number.
$ Match must occur at end of string
  • E.g. a$ Last character must be lower case letter a

Precedence table:
Order  Name                Representation
1      Parentheses         ( )
2      Multipliers         ? + * {m,n} {m, n}?
3      Sequence & Anchors  abc ^ $
4      Alternation         |

Predefined Character Abbreviations:
abr    same as       meaning
\d     [0-9]         Any single digit
\D     [^0-9]        Any single character that's not a digit
\w     [a-zA-Z0-9_]  Any word character
\W     [^a-zA-Z0-9_] Any non-word character
\s     [ \r\t\n\f]   Any space character
\S     [^ \r\t\n\f]  Any non-space character
\n     [\n]          New line

Example 1: Run as macro The following example macro looks at the value in cell A1 to see if the first 1 or 2 characters are digits. If so, they are removed and the rest of the string is displayed. If not, then a box appears telling you that no match is found. Cell A1 values of 12abc will return abc, value of 1abc will return abc, value of abc123 will return “Not Matched” because the digits were not at the start of the string.
Private Sub simpleRegex()
    Dim strPattern As String: strPattern = "^[0-9]{1,2}"
    Dim strReplace As String: strReplace = ""
    'Dim regEx As New RegExp
    Dim regEx As Object: Set regEx = CreateObject("VBScript.RegExp")
    Dim strInput As String
    Dim Myrange As Excel.Range

    Set Myrange = ActiveSheet.Range("A1")

    If strPattern  "" Then
        strInput = Myrange.Value

        With regEx
            .Global = True
            .MultiLine = True
            .IgnoreCase = False
            .Pattern = strPattern
        End With

        If regEx.Test(strInput) Then
            MsgBox (regEx.Replace(strInput, strReplace))
        Else
            MsgBox ("Not matched")
        End If
    End If
    Set regEx = Nothing
End Sub

Example 2: Run as an in-cell function This example is the same as example 1 but is setup to run as an in-cell function. To use, change the code to this:
Function simpleCellRegex(Myrange As Excel.Range) As String
    'Dim regEx As New RegExp
    Dim regEx As Object: Set regEx = CreateObject("VBScript.RegExp")
    Dim strPattern As String
    Dim strInput As String
    Dim strReplace As String
    Dim strOutput As String


    strPattern = "^[0-9]{1,3}"

    If strPattern  "" Then
        strInput = Myrange.Value
        strReplace = ""

        With regEx
            .Global = True
            .MultiLine = True
            .IgnoreCase = False
            .Pattern = strPattern
        End With

        If regEx.test(strInput) Then
            simpleCellRegex = regEx.Replace(strInput, strReplace)
        Else
            simpleCellRegex = "Not matched"
        End If
    End If
    Set regEx = Nothing
End Function
Place your strings (“12abc”) in cell A1. Enter this formula =simpleCellRegex(A1) in cell B1 and the result will be “abc”. q3RRC
Example 3: Loop Through Range This example is the same as example 1 but loops through a range of cells.
Private Sub simpleRegex()
    Dim strPattern As String: strPattern = "^[0-9]{1,2}"
    Dim strReplace As String: strReplace = ""
    'Dim regEx As New RegExp
    Dim regEx As Object: Set regEx = CreateObject("VBScript.RegExp")
    Dim strInput As String
    Dim Myrange As Excel.Range

    Set Myrange = ActiveSheet.Range("A1:A5")

    For Each cell In Myrange
        If strPattern  "" Then
            strInput = cell.Value

            With regEx
                .Global = True
                .MultiLine = True
                .IgnoreCase = False
                .Pattern = strPattern
            End With

            If regEx.Test(strInput) Then
                MsgBox (regEx.Replace(strInput, strReplace))
            Else
                MsgBox ("Not matched")
            End If
        End If
    Next
    Set regEx = Nothing
End Sub

Example 4: Splitting apart different patterns This example loops through a range (A1, A2 & A3) and looks for a string starting with three digits followed by a single alpha character and then 4 numeric digits. The output splits apart the pattern matches into adjacent cells by using the (). $1 represents the first pattern matched within the first set of ().
Private Sub splitUpRegexPattern()
    'Dim regEx As New RegExp
    Dim regEx As Object: Set regEx = CreateObject("VBScript.RegExp")
    Dim strPattern As String
    Dim strInput As String
    Dim Myrange As Excel.Range

    Set Myrange = ActiveSheet.Range("A1:A3")

    For Each C In Myrange
        strPattern = "(^[0-9]{3})([a-zA-Z])([0-9]{4})"

        If strPattern  "" Then
            strInput = C.Value

            With regEx
                .Global = True
                .MultiLine = True
                .IgnoreCase = False
                .Pattern = strPattern
            End With

            If regEx.test(strInput) Then
                C.Offset(0, 1) = regEx.Replace(strInput, "$1")
                C.Offset(0, 2) = regEx.Replace(strInput, "$2")
                C.Offset(0, 3) = regEx.Replace(strInput, "$3")
            Else
                C.Offset(0, 1) = "(Not matched)"
            End If
        End If
    Next
    Set regEx = Nothing
End Sub
Results: 9eCZ5
Additional Pattern Examples
String   Regex Pattern                  Explanation
a1aaa    [a-zA-Z][0-9][a-zA-Z]{3}       Single alpha, single digit, three alpha characters
a1aaa    [a-zA-Z]?[0-9][a-zA-Z]{3}      May or may not have preceeding alpha character
a1aaa    [a-zA-Z][0-9][a-zA-Z]{0,3}     Single alpha, single digit, 0 to 3 alpha characters
a1aaa    [a-zA-Z][0-9][a-zA-Z]*         Single alpha, single digit, followed by any number of alpha characters

</i8>    \<\/[a-zA-Z][0-9]\>            Exact non-word character except any single alpha followed by any single digit
Finally, there is this version of an UDF to use Regular Expressions, on the same post answer as the one above:
Function regex(strInput As String, matchPattern As String, Optional ByVal outputPattern As String = "$0") As Variant
    Dim inputRegexObj As New VBScript_RegExp_55.RegExp, outputRegexObj As New VBScript_RegExp_55.RegExp, outReplaceRegexObj As New VBScript_RegExp_55.RegExp
    Dim inputMatches As Object, replaceMatches As Object, replaceMatch As Object
    Dim replaceNumber As Integer

    With inputRegexObj
        .Global = True
        .MultiLine = True
        .IgnoreCase = False
        .Pattern = matchPattern
    End With
    With outputRegexObj
        .Global = True
        .MultiLine = True
        .IgnoreCase = False
        .Pattern = "\$(\d+)"
    End With
    With outReplaceRegexObj
        .Global = True
        .MultiLine = True
        .IgnoreCase = False
    End With

    Set inputMatches = inputRegexObj.Execute(strInput)
    If inputMatches.Count = 0 Then
        regex = False
    Else
        Set replaceMatches = outputRegexObj.Execute(outputPattern)
        For Each replaceMatch In replaceMatches
            replaceNumber = replaceMatch.SubMatches(0)
            outReplaceRegexObj.Pattern = "\$" & replaceNumber

            If replaceNumber = 0 Then
                outputPattern = outReplaceRegexObj.Replace(outputPattern, inputMatches(0).Value)
            Else
                If replaceNumber > inputMatches(0).SubMatches.Count Then
                    'regex = "A to high $ tag found. Largest allowed is $" & inputMatches(0).SubMatches.Count & "."
                    regex = CVErr(xlErrValue)
                    Exit Function
                Else
                    outputPattern = outReplaceRegexObj.Replace(outputPattern, inputMatches(0).SubMatches(replaceNumber - 1))
                End If
            End If
        Next
        regex = outputPattern
    End If
End Function
[/sourcecode]	

Geocoding + Reverse geocoding

To get the Longitude and Latitude on the globe from an address or a placemark, is what is called “geocoding”; the inverse, to get full address from Latitude and Longitude data is calling “reverse geocoding”. Long time ago I developed a procedure to do both actions, using either GoogleMaps or BingMaps (VirtualEarth was its name). And it did the matter really well. But it has rained a lot since then. I don’t remember to use Google API keys (or at least, none I owned), and now, to perform this task, it should be necessary to have one. Searching on the net came this clarifying post, from My Engineering World, on how to get one that shows how to do the job. , and these other two (1 and 2) interesting posts are related to the subject. As mentioned in post 2, it should be noted that using GAS (Google Apps) has the noticeable advantage of have not to deal with the API key, on the other hand, it forces to be bounded to Google Apps. Still no way to do heavy load work on the cloud. No more on this subject until the mix of the posts…

VBA Bitwise operators

In VBA there are no bitwise operators, those similar to “” operators in C++ and Visual Basic. Do this mean we can’t do bit shift operations?. Following this post from Excely, we can replicate bitwise shift operators with multiplying or dividing by the appropriate power of 2. The original post code was far from optimized and did not behave well in several possible situations we can deal with. The code presented here have some amedments, beeing the main to have a buffer with the shifters to optimize the operations, and some checkings in the code just to get not trapped in overflow errors.
Option Explicit

Private aShifter() As Long ' Buffer for the shift operations

Private Function fBitwiser() As Boolean
' Compute the shifters and store in buffer
Dim lgShift As Long

    ReDim aShifter(0 To 30) ' overflow for 2^31 and 2^32
    aShifter(0) = 1 '2^0
    For lgShift = 1 To 30
        aShifter(lgShift) = aShifter(lgShift - 1) * 2 '2^lgShift
    Next lgShift
End Function

Public Sub sBitwiseTest()
    Dim Value As Long
    Dim strHex1 As String
    Dim strHex2 As String

    Value = &H90000000
    strHex1 = "&H" & Hex(shr(Value, 1))
    strHex2 = "&H" & Hex(shl(strHex1, 1))
End Sub

Public Function shr(ByVal Value As Long, _
                    ByVal shift As Byte) As Long
' Right shifting is equal to dividing by 2^shift
' Bitwise Right Shift Function
    Dim bSgn As Boolean

    If Not (Not aShifter()) Then Else Call fBitwiser

    If shift > 30 Then GoTo ErrControl

    If Value  0) Then Value = Value - 1
            shr = Not Value
        Else
            shr = Value
        End If
    Else
        Value = 0
    End If

    Exit Function
ErrControl:
    Dim lgRetVal As Long
    lgRetVal = VBA.MsgBox("Can not operate on 32 bits", vbExclamation + vbOKOnly)
End Function

Public Function shl(ByVal Value As Long, _
                    ByVal shift As Byte) As Long
' Left shifting is equal to multiplying by 2^Shift. 
' Bitwise Left Shift Function
    If Not (Not aShifter()) Then Else Call fBitwiser

    shl = Value

    If shift > 0 Then
        Dim i As Long
        Dim m As Long
        If Value < aShifter(30 - shift) Then
            Value = Value * aShifter(shift)
        Else
            ' To avoid an overflow error we'd use small trick:
            For i = 1 To Shift
                m = Value And &H40000000                    ' save 30th bit
                Value = Value And &H3FFFFFFF                ' clear 30th and 31st bits
                Value = Value * 2                           ' multiply by 2
                If m  0 Then Value = Value Or &H80000000  ' set 31st bit
            Next i
        End If
        shl = Value
    End If
End Function

Public Function DecToBin(ByVal lngDec As Long) As String
' Decimal-to-Binary function that converts a Long Integer value
' (max value range -2^31 to 2^31 or -2147483648 to 2147483647)
' to binary number represented by a string:

' Sample Results:
'-----------------------------
' Print DecToBin(32768)
' 00000000000000001000000000000000
' Print DecToBin(32769)
' 00000000000000001000000000000001
' Print DecToBin(2 ^ 31 - 1)
' 01111111111111111111111111111111
' Print DecToBin(2147483647)
' 01111111111111111111111111111111
' Print DecToBin(-2 ^ 31)
' 10000000000000000000000000000000
' Print DecToBin(-2147483648#)
' 10000000000000000000000000000000
' Print DecToBin(2 ^ 31)
' Overflow error

    Const MAXLEN = 30
    Dim strBin As String
    Dim n As Long

    If Not (Not aShifter()) Then Else Call fBitwiser

    If lngDec < 0 Then strBin = "1" Else strBin = "0"

    For n = MAXLEN To 0 Step -1
        If (lngDec And aShifter(n)) Then
            strBin = strBin & "1"
        Else
            strBin = strBin & "0"
        End If
    Next

    DecToBin = strBin

End Function
[/sourcecode]