viernes, 29 de septiembre de 2017

VBA Access. Funciones para guardar imagen a Bdd, leer de Bdd y volcar a fichero, leer de Bdd y mostrar en un control Imagen del formulario.

' SQL Server, Tabla Imagenes
' [Id] [int] NOT NULL,
' [Descripcion] [nvarchar](250) NULL,
' [Imagen] [varbinary](max) NULL,

Function AddFileImageToBdd(ByVal Id As Long, ByVal sDescripcion As String, ByVal sFName As String) As Boolean
On Error GoTo error
    Dim rs As ADODB.Recordset
    Dim objStream As New ADODB.Stream
 
    'create the stream
    objStream.Type = adTypeBinary
    objStream.Open
    objStream.LoadFromFile sFName
 
    Set rs = GetRecordset("SELECT TOP 0 * FROM Imagenes")
    With rs
        .AddNew
        !Id = Id
        !Descripcion = sDescripcion
        !Imagen = objStream.Read
        .Update
        .Close
    End With
    Set rs = Nothing
    objStream.Close
    Set objStream = Nothing
 
    AddFileImageToBdd = True
 
Exit Function
error:
    AddFileImageToBdd = False
    Debug.Print Err.Description
End Function

Function ReadFileImageBdd(ByVal Id As Long, ByVal sDescripcion As String, ByVal sFName As String) As Boolean
On Error GoTo error
    Dim rs As ADODB.Recordset
    Dim objStream As New ADODB.Stream
 
    'create the stream
    objStream.Type = adTypeBinary
    objStream.Open
 
    Set rs = GetRecordset("SELECT Imagen FROM Imagenes WHERE Id = " & Id)
    If Not rs.EOF Then
        objStream.Write rs!Imagen
        objStream.SaveToFile sFName, adSaveCreateOverWrite
    End If
    rs.Close
    Set rs = Nothing
    objStream.Close
    Set objStream = Nothing
 
    ReadFileImageBdd = True
 
Exit Function
error:
    ReadFileImageBdd = False
    Debug.Print Err.Description
End Function

Function ReadFileImageBddToPicture(ByVal Id As Long, ByRef ImgCtl As image) As Boolean
On Error GoTo error
    Dim rs As ADODB.Recordset
    Dim objStream As New ADODB.Stream
 
    'create the stream
    objStream.Type = adTypeBinary
    objStream.Open
 
    Set rs = GetRecordset("SELECT Imagen FROM Imagenes WHERE Id = " & Id)
    If Not rs.EOF Then
        objStream.Write rs!Imagen
        objStream.Position = 0
        ImgCtl.PictureData = objStream.Read  ' load bytes into Image control on form
    End If
    rs.Close
    Set rs = Nothing
    objStream.Close
    Set objStream = Nothing
 
    ReadFileImageBddToPicture = True
 
Exit Function
Resume
error:
    ReadFileImageBddToPicture = False
    Debug.Print Err.Description
End Function

VBA Access. Módulo de clase clsWindowExists para comprobar si una ventana de aplicación Windows existe por su nombre parcial. Window Exists.

Option Compare Database
Option Explicit

'http://www.vbforums.com/showthread.php?316924-Find-Window-handle-by-Partial-Caption

'Windows API Function Declarations
#If Win64 = 1 Then
    Private Declare PtrSafe Function FindWindow Lib "User32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
    Private Declare PtrSafe Function GetWindowText Lib "User32" Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
    Private Declare PtrSafe Function GetWindowTextLength Lib "User32" Alias "GetWindowTextLengthA" (ByVal hWnd As Long) As Long
    Private Declare PtrSafe Function GetWindow Lib "User32" (ByVal hWnd As Long, ByVal wCmd As Long) As Long
    Private Declare PtrSafe Function IsWindowVisible Lib "User32" (ByVal hWnd As Long) As Boolean
#Else
    Private Declare Function FindWindow Lib "User32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) 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 GetWindow Lib "User32" (ByVal hWnd As Long, ByVal wCmd As Long) As Long
    Private Declare Function IsWindowVisible Lib "User32" (ByVal hWnd As Long) As Boolean
#End If

#If Win64 = 1 Then
    Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr) 'For 64 Bit Systems
#Else
    Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) 'For 32 Bit Systems
#End If

Private Const GW_HWNDNEXT = 2

Public Function WindowExists(ByVal PartialCaption As String) As Boolean
On Error GoTo error
    Dim lhWndP As Long
    If GetHandleFromPartialCaption(lhWndP, PartialCaption) = True Then
        If IsWindowVisible(lhWndP) = True Then
          Debug.Print "Found VISIBLE Window Handle: " & lhWndP, vbOKOnly + vbInformation
        Else
          Debug.Print "Found INVISIBLE Window Handle: " & lhWndP, vbOKOnly + vbInformation
        End If
     
        WindowExists = True
    Else
        Debug.Print "Window '" & PartialCaption & "' not found!", vbOKOnly + vbExclamation
        WindowExists = False
    End If

Exit Function
error:
    WindowExists = False
    Debug.Print Err.Number & ": " & Err.Description
End Function

Public Sub LoopWhileWindowsExists(ByVal PartialCaption As String, Optional ByVal CheckTimeMilliSeconds As Long = 3000)
On Error GoTo error
    Do
        Sleep (CheckTimeMilliSeconds)
    Loop Until Not WindowExists(PartialCaption)

Exit Sub
error:
    Debug.Print Err.Number & ": " & Err.Description
End Sub

Private Function GetHandleFromPartialCaption(ByRef lWnd As Long, ByVal sCaption As String) As Boolean
    Dim lhWndP As Long
    Dim sStr As String
    GetHandleFromPartialCaption = False
    lhWndP = FindWindow(vbNullString, vbNullString) 'PARENT WINDOW
    Do While lhWndP <> 0
        sStr = String(GetWindowTextLength(lhWndP) + 1, Chr$(0))
        GetWindowText lhWndP, sStr, Len(sStr)
        sStr = Left$(sStr, Len(sStr) - 1)
        If InStr(1, sStr, sCaption) > 0 Then
            GetHandleFromPartialCaption = True
            lWnd = lhWndP
            Exit Do
        End If
        lhWndP = GetWindow(lhWndP, GW_HWNDNEXT)
    Loop

End Function

VBA Access. Seleccionar fichero, carpeta, nombre de fichero guardar como, nombre de fichero a abrir

Option Compare Database
Option Explicit

'*****************************
'    'Ejemplo de uso
'    'selecciona 1 fichero de Excel
'    Dim RutaDoc() As String
'    RutaDoc = SelectItem(msoFileDialogFilePicker, "*.xls,*.xlsx", False, "Selección de fichero Excel")
'    
'    'comprueba si se ha seleccionado un fichero
'    If Len(Join(RutaDoc)) > 0 Then
'        Debug.Print "Fichero seleccionado: " & RutaDoc(0) 
'    End If
'*****************************/

Public Enum MsoFileDialogType
    msoFileDialogOpen = 1           'Permite al usuario abrir un archivo.
    msoFileDialogSaveAs = 2         'Permite al usuario guardar un archivo.
    msoFileDialogFilePicker = 3     'Permite al usuario seleccionar un archivo.
    msoFileDialogFolderPicker = 4   'Permite al usuario seleccionar una carpeta.
End Enum

Public Function SelectItem(ByVal tipo As MsoFileDialogType, Optional ByVal filtro As String, Optional ByVal bMultiSelect As Boolean = False, Optional TituloVentana As String = "") As String()
On Error GoTo error
    Dim fDialog As Object
    Dim intResult As Integer
    Dim i As Long
    Dim items() As String

    Set fDialog = Application.FileDialog(tipo)

    'Optional: FileDialog properties
    'fDialog.AllowMultiSelect = False
    'fDialog.title = "título de ventana"
    'fDialog.InitialFileName = "C:\"
   
    'Optional: Add filters
    'fDialog.Filters.Clear
    'fDialog.Filters.Add "File images", "*.jpeg"
    'fDialog.Filters.Add "All files", "*.*"

    'Optional
    'Application.FileDialog(tipo).ButtonName = "nombre del botón personalizado El nombre del botón : 'Abrir' por defecto"

    If TituloVentana <> "" Then fDialog.Title = TituloVentana

    fDialog.AllowMultiSelect = bMultiSelect

    If tipo = msoFileDialogFilePicker Then
        'limpiamos historial de filtros
        fDialog.Filters.Clear
        'aplicamos filtro
        If Nz(filtro, "") <> "" Then
            fDialog.Filters.Add filtro, filtro
        End If
    End If

    intResult = Application.FileDialog(tipo).Show
    If intResult <> 0 Then
        For i = 0 To Application.FileDialog(tipo).SelectedItems.Count - 1
            ReDim Preserve items(i)
            items(i) = Application.FileDialog(tipo).SelectedItems(i + 1)
        Next
    End If
   
    Set fDialog = Nothing
    SelectItem = items

Exit Function
Resume
error:
    Set fDialog = Nothing
    MsgBox Err.Description
End Function

lunes, 25 de septiembre de 2017

VBA Access. Módulo de clase clsFileList. Obtiene listado de ficheros, carpetas y el número total de ficheros y carpetas encontrados. La búsqueda puede ser recursiva o no.

Option Compare Database
Option Explicit

'#########################################################################################################################################
'GetFileList(Folder,Optional bRecursive=true)
'Obtiene la lista de ficheros y carpetas recursivamente de una carpeta / La búsqueda recursiva es opcional
'Si GetFileList = False, han habido errores y los resultados obtenidos no son fiables (por ejemplo un error de permiso de acceso denegado)
'
'Ejemplo de uso:
'Dim res As Boolean
'Dim oFileList As New clsFileList
'res = oFileList.GetFileList("C:\Temp")
'Debug.Print "#Folders : " & UBound(oFileList.Folders)
'Debug.Print "FoldersCount: " & oFileList.FoldersCount
'Debug.Print "#Files : " & UBound(oFileList.Files)
'Debug.Print "FilesCount: " & oFileList.FilesCount
'Set oFileList = Nothing
'
'Tip: Comprobar si un array está vacío
'(not arrayName) = -1
'#########################################################################################################################################

Private m_Folder As String

Private m_Files() As String
Private m_FilesCount As Long
Private m_FoldersCount As Long
Private m_Folders() As String

Private Sub Class_Initialize()
    'initialize values
    FilesCount = 0
    FoldersCount = 0
End Sub

Private Sub Class_Terminate()
    'deallocate if needed
End Sub

Public Sub InitiateProperties(ByRef ArrayFolders() As String, ByVal FoldersCount As Long, ByRef ArrayFiles() As String, ByVal FilesCount As Long)
On Error GoTo error
    ReDim Preserve m_Files(UBound(ArrayFiles))
    m_Files = ArrayFiles
    m_FilesCount = FilesCount

    ReDim Preserve m_Folders(UBound(ArrayFolders))
    m_Folders = ArrayFolders
    m_FoldersCount = FoldersCount
Exit Sub
error:
    Debug.Print Err.Description
End Sub

Public Property Get Files()
    Files = m_Files
End Property

Public Property Get FilesCount() As Long
    FilesCount = m_FilesCount
End Property

Public Property Let FilesCount(ByVal Value As Long)
    m_FilesCount = Value
End Property

Public Property Get Folders()
    Folders = m_Folders
End Property

Public Property Get FoldersCount() As Long
    FoldersCount = m_FoldersCount
End Property

Public Property Let FoldersCount(ByVal Value As Long)
    m_FoldersCount = Value
End Property

Public Property Get Folder() As String
    Folder = m_Folder
End Property

Public Property Let Folder(ByVal Value As String)
    m_Folder = Value
End Property

Public Function GetFileList(ByVal Folder As String, Optional ByVal bRecursivo As Boolean = True) As Boolean
On Error GoTo error
    Dim objFSO As Object
    Dim objFolder As Object
    Dim objSubFolder As Object
    Dim objFile As Object

    Dim res As Boolean
    Dim resR As Boolean
    res = True
    resR = True
   
    Debug.Print Folder & vbNewLine

    If Dir(Folder, vbDirectory) <> "" Then
        'root folder not included
        If (Not m_Folders) <> -1 Then
            'Add folder to array
            m_Folders(UBound(m_Folders)) = Folder
        End If
    End If
   
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    Set objFolder = objFSO.GetFolder(Folder)

    'Add files to array and count them
    For Each objFile In objFolder.Files
        If (Not m_Files) = -1 Then
            ReDim Preserve m_Files(1)
        Else
            ReDim Preserve m_Files(UBound(m_Files) + 1)
        End If

        'Count files
        FilesCount = UBound(m_Files)

        'Add file to array
        m_Files(FilesCount) = objFile.path
        Debug.Print vbTab & objFile.path & vbNewLine

    Next objFile

    'loops through each folder in the directory
    For Each objSubFolder In objFolder.SubFolders
        'Count folders
        FoldersCount = FoldersCount + 1

        'recursive search
        If bRecursivo Then
            Dim SubFolders() As String
            Dim oFileList As New clsFileList
           
            If (Not m_Folders) = -1 Then
                '1st item
                ReDim Preserve m_Folders(1)
            Else
                ReDim Preserve m_Folders(UBound(m_Folders) + 1)
            End If
           
            oFileList.InitiateProperties m_Folders, UBound(m_Folders), m_Files, m_FilesCount
            resR = oFileList.GetFileList(objSubFolder.path, bRecursivo)
           
            m_Files = oFileList.Files
            FilesCount = oFileList.FilesCount
            m_Folders = oFileList.Folders
            FoldersCount = oFileList.FoldersCount
           
            Set oFileList = Nothing
        End If
       
        res = res And resR

        DoEvents
    Next objSubFolder

    Set objFolder = Nothing
    Set objSubFolder = Nothing
    Set objFile = Nothing
    Set objFSO = Nothing

    GetFileList = res

Exit Function
Resume
error:
    GetFileList = False
    Debug.Print vbTab & ">>>> " & "Error " & Err.Number & ", " & Err.Description & " <<<<"
End Function

viernes, 15 de septiembre de 2017

VBA Access. Módulo de clase clsImageResize usando WIA. Redimensionar / cambiar de resolución una imagen.

'---------------------------------------------------------------------------------------
' Procedure : WIA_ResizeImage
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Resize an image based on Max width and Max height using WIA
' Copyright : The following may be altered and reused as you wish so long as the
'             copyright notice is left unchanged (including Author, Website and
'             Copyright).  It may not be sold/resold or reposted on other sites (links
'             back to this site are allowed).
' Req'd Refs: Uses Late Binding, so none required
'
' Windows Image Acquisition (WIA)
'             https://msdn.microsoft.com/en-us/library/windows/desktop/ms630368(v=vs.85).aspx
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sInitialImage : Fully qualified path and filename of the original image to resize
' sResizedImage : Fully qualified path and filename of where to save the resized image
' lMaximumWidth : Maximum allowable image width
' lMaximumHeight: Maximum allowable image height
'
' Usage:
' ~~~~~~
' Call WIA_ResizeImage("C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg", _
'                      "C:\Users\MyUser\Desktop\Chrysanthemum_small.jpg", _
'                      800, 600)
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2017-01-18              Initial Release
'---------------------------------------------------------------------------------------
Public Function WIA_ResizeImage(sInitialImage As String, sResizedImage As String, _
                                           lMaximumWidth As Long, lMaximumHeight As Long) As Boolean
    On Error GoTo Error_Handler
    Dim oWIA                  As Object 'WIA.ImageFile
    Dim oIP                   As Object 'ImageProcess

    Set oWIA = CreateObject("WIA.ImageFile")
    Set oIP = CreateObject("WIA.ImageProcess")

    oIP.Filters.Add oIP.FilterInfos("Scale").FilterID
    oIP.Filters(1).Properties("MaximumWidth") = lMaximumWidth
    oIP.Filters(1).Properties("MaximumHeight") = lMaximumHeight

    oWIA.LoadFile sInitialImage
    Set oWIA = oIP.Apply(oWIA)
    oWIA.SaveFile sResizedImage
    WIA_ResizeImage = True

Error_Handler_Exit:
    On Error Resume Next
    If Not oIP Is Nothing Then Set oIP = Nothing
    If Not oWIA Is Nothing Then Set oWIA = Nothing
    Exit Function

Error_Handler:
    MsgBox "The following error has occured" & vbCrLf & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Source: WIA_ResizeImage" & vbCrLf & _
           "Error Description: " & Err.Description & _
           Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
           , vbOKOnly + vbCritical, "An Error has Occured!"
    Resume Error_Handler_Exit
End Function



VBA Access. Módulo de clase clsImageConvert usando WIA. Convertir formatos de imagen (BMP.JPEG,PNG,TIFF).

Public Enum wiaFormat
    BMP = 0
    GIF = 1
    JPEG = 2
    PNG = 3
    TIFF = 4
End Enum

'---------------------------------------------------------------------------------------
' Procedure : WIA_ConvertImage
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Convert an image's format using WIA
' Copyright : The following may be altered and reused as you wish so long as the
'             copyright notice is left unchanged (including Author, Website and
'             Copyright).  It may not be sold/resold or reposted on other sites (links
'             back to this site are allowed).
' Req'd Refs: Uses Late Binding, so none required
'
' Windows Image Acquisition (WIA)
'             https://msdn.microsoft.com/en-us/library/windows/desktop/ms630368(v=vs.85).aspx
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sInitialImage : Fully qualified path and filename of the original image to resize
' sOutputImage  : Fully qualified path and filename of where to save the new image
' lFormat       : Format to convert the image into
' lQuality      : Quality level to be used for the conversion process (1-100)
'
' Usage:
' ~~~~~~
' Call WIA_ConvertImage("C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg", _
'                       "C:\Users\MyUser\Desktop\Chrysanthemum_2.jpg", _
'                       JPEG)
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2017-01-18              Initial Release
'---------------------------------------------------------------------------------------
Public Function WIA_ConvertImage(sInitialImage As String, _
                                 sOutputImage As String, _
                                 lFormat As wiaFormat, _
                                 Optional lQuality As Long = 85) As Boolean
    On Error GoTo Error_Handler
    Dim oWIA                  As Object    'WIA.ImageFile
    Dim oIP                   As Object    'ImageProcess
    Dim sFormatID             As String
    Dim sExt                  As String

    'Convert our Enum over to the proper value used by WIA
    Select Case lFormat
        Case 0
            sFormatID = "{B96B3CAB-0728-11D3-9D7B-0000F81EF32E}"
            sExt = "BMP"
        Case 1
            sFormatID = "{B96B3CB0-0728-11D3-9D7B-0000F81EF32E}"
            sExt = "GIF"
        Case 2
            sFormatID = "{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}"
            sExt = "JPEG"
        Case 3
            sFormatID = "{B96B3CAF-0728-11D3-9D7B-0000F81EF32E}"
            sExt = "PNG"
        Case 4
            sFormatID = "{B96B3CB1-0728-11D3-9D7B-0000F81EF32E}"
            sExt = "TIFF"
    End Select

    If lQuality > 100 Then lQuality = 100

    'Should check if the output file already exists and if so,
    'prompt the user to overwrite it or not

    Set oWIA = CreateObject("WIA.ImageFile")
    Set oIP = CreateObject("WIA.ImageProcess")

    oIP.Filters.Add oIP.FilterInfos("Convert").FilterID
    oIP.Filters(1).Properties("FormatID") = sFormatID
    oIP.Filters(1).Properties("Quality") = lQuality

    oWIA.LoadFile sInitialImage
    Set oWIA = oIP.Apply(oWIA)
    'Overide the specified ext with the appropriate one for the choosen format
    oWIA.SaveFile Left(sOutputImage, InStrRev(sOutputImage, ".")) & LCase(sExt)
    WIA_ConvertImage = True

Error_Handler_Exit:
    On Error Resume Next
    If Not oIP Is Nothing Then Set oIP = Nothing
    If Not oWIA Is Nothing Then Set oWIA = Nothing
    Exit Function

Error_Handler:
    MsgBox "The following error has occured" & vbCrLf & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Source: WIA_ConvertImage" & vbCrLf & _
           "Error Description: " & Err.Description & _
           Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
           , vbOKOnly + vbCritical, "An Error has Occured!"
    Resume Error_Handler_Exit
End Function

jueves, 14 de septiembre de 2017

VBA Access. Módulo de clase clsPrintScreen. Captura de pantalla en BMP. Screenshot, PrintScreen.

'***********************************************************************************************
'   * Please leave any Trademarks or Credits in place.
'   *
'   * ACKNOWLEDGEMENT TO CONTRIBUTORS :
'   *       STEPHEN BULLEN, 15 November 1998 - Original PastPicture code
'   *       G HUDSON, 5 April 2010 - Pause Function
'   *       LUTZ GENTKOW, 23 July 2011 - Alt + PrtScrn
'   *       PAUL FRANCIS, 11 April 2013 - Putting all pieces together, bridging the 32 bit and 64 bit version.
'   *       CHRIS O, 12 April 2013 - Code suggestion to work on older versions of Access.
'   *
'   * DESCRIPTION: Creates a standard Picture object from whatever is on the clipboard.
'   *              This object is then saved to a location on the disc. Please note, this
'   *              can also be assigned to (for example) and Image control on a userform.
'   *
'   * The code requires a reference to the "OLE Automation" type library.
'   *
'   * The code in this module has been derived from a number of sources
'   * discovered on MSDN, Access World Forum, VBForums.
'   *
'   * To use it, just copy this module into your project, then you can use:
'   * SaveClip2Bit("C:\Pics\Sample.bmp")
'   * to save this to a location on the Disc.
'   * (Or)
'   * Set ImageControl.Image = PastePicture
'   * to paste a picture of whatever is on the clipboard into a standard image control.
'   *
'   * PROCEDURES:
'   *   PastePicture  :   The entry point for 'Setting' the Image
'   *   CreatePicture :   Private function to convert a bitmap or metafile handle to an OLE reference
'   *   fnOLEError    :   Get the error text for an OLE error code
'   *   SaveClip2Bit  :   The entry point for 'Saving' the Image, calls for PastePicture
'   *   AltPrintScreen:   Performs the automation of Alt + PrtScrn, for getting the Active Window.
'   *   Pause         :   Makes the program wait, to make sure proper screen capture takes place.
'**************************************************************************************************

Option Explicit
Option Compare Text

'Declare a UDT to store a GUID for the IPicture OLE Interface
Private Type Guid
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(0 To 7) As Byte
End Type

'Declare a UDT to store the bitmap information
Private Type uPicDesc
    Size As Long
    Type As Long
    hPic As Long
    hPal As Long
End Type

'Windows API Function Declarations
#If Win64 = 1 Then
   
    'Does the clipboard contain a bitmap/metafile?
    Private Declare PtrSafe Function IsClipboardFormatAvailable Lib "user32" (ByVal wFormat As Integer) As Long
   
    'Open the clipboard to read
    Private Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hWnd As Long) As Long
   
    'Get a pointer to the bitmap/metafile
    Private Declare PtrSafe Function GetClipboardData Lib "user32" (ByVal wFormat As Integer) As Long
   
    'Close the clipboard
    Private Declare PtrSafe Function CloseClipboard Lib "user32" () As Long
   
    'Convert the handle into an OLE IPicture interface.
    Private Declare PtrSafe Function OleCreatePictureIndirect Lib "oleaut32.dll" (PicDesc As uPicDesc, RefIID As Guid, ByVal fPictureOwnsHandle As Long, IPic As IPicture) As Long
   
    'Create our own copy of the metafile, so it doesn't get wiped out by subsequent clipboard updates.
    Private Declare PtrSafe Function CopyEnhMetaFile Lib "gdi32" Alias "CopyEnhMetaFileA" (ByVal hemfSrc As Long, ByVal lpszFile As String) As Long
   
    'Create our own copy of the bitmap, so it doesn't get wiped out by subsequent clipboard updates.
    Private Declare PtrSafe Function CopyImage Lib "user32" (ByVal Handle As Long, ByVal un1 As Long, ByVal n1 As Long, ByVal n2 As Long, ByVal un2 As Long) As Long
   
    'Uses the Keyboard simulation
    Private Declare PtrSafe Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)

#Else

    'Does the clipboard contain a bitmap/metafile?
    Private Declare Function IsClipboardFormatAvailable Lib "user32" (ByVal wFormat As Integer) As Long
   
    'Open the clipboard to read
    Private Declare Function OpenClipboard Lib "user32" (ByVal hWnd As Long) As Long
   
    'Get a pointer to the bitmap/metafile
    Private Declare Function GetClipboardData Lib "user32" (ByVal wFormat As Integer) As Long
   
    'Close the clipboard
    Private Declare Function CloseClipboard Lib "user32" () As Long
   
    'Convert the handle into an OLE IPicture interface.
    Private Declare Function OleCreatePictureIndirect Lib "oleaut32.dll" (PicDesc As uPicDesc, RefIID As Guid, ByVal fPictureOwnsHandle As Long, IPic As IPicture) As Long
   
    'Create our own copy of the metafile, so it doesn't get wiped out by subsequent clipboard updates.
    Private Declare Function CopyEnhMetaFile Lib "gdi32" Alias "CopyEnhMetaFileA" (ByVal hemfSrc As Long, ByVal lpszFile As String) As Long
   
    'Create our own copy of the bitmap, so it doesn't get wiped out by subsequent clipboard updates.
    Private Declare Function CopyImage Lib "user32" (ByVal Handle As Long, ByVal un1 As Long, ByVal n1 As Long, ByVal n2 As Long, ByVal un2 As Long) As Long
   
    'Uses the Keyboard simulation
    Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)

#End If
 
'The API format types we're interested in
Const CF_BITMAP = 2
Const CF_PALETTE = 9
Const CF_ENHMETAFILE = 14
Const IMAGE_BITMAP = 0
Const LR_COPYRETURNORG = &H4

Private Const KEYEVENTF_KEYUP = &H2
Private Const VK_SNAPSHOT = &H2C
Private Const VK_MENU = &H12

' Subroutine    : AltPrintScreen
' Purpose       : Capture the Active window, and places on the Clipboard.

Sub AltPrintScreen()
    keybd_event VK_MENU, 0, 0, 0
    keybd_event VK_SNAPSHOT, 0, 0, 0
    keybd_event VK_SNAPSHOT, 0, KEYEVENTF_KEYUP, 0
    keybd_event VK_MENU, 0, KEYEVENTF_KEYUP, 0
End Sub

' Subroutine    : PastePicture
' Purpose       : Get a Picture object showing whatever's on the clipboard.

Function PastePicture() As IPicture
    'Some pointers
    Dim h As Long, hPtr As Long, hPal As Long, lPicType As Long, hCopy As Long

    'Check if the clipboard contains the required format
    If IsClipboardFormatAvailable(CF_BITMAP) Then
        'Get access to the clipboard
        h = OpenClipboard(0&)
        If h > 0 Then
            'Get a handle to the image data
            hPtr = GetClipboardData(CF_BITMAP)

            hCopy = CopyImage(hPtr, IMAGE_BITMAP, 0, 0, LR_COPYRETURNORG)

            'Release the clipboard to other programs
            h = CloseClipboard
            'If we got a handle to the image, convert it into a Picture object and return it
            If hPtr <> 0 Then Set PastePicture = CreatePicture(hCopy, 0, CF_BITMAP)
        End If
    End If
End Function


' Subroutine    : CreatePicture
' Purpose       : Converts a image (and palette) handle into a Picture object.
' NOTE          : Requires a reference to the "OLE Automation" type library

Private Function CreatePicture(ByVal hPic As Long, ByVal hPal As Long, ByVal lPicType) As IPicture
    ' IPicture requires a reference to "OLE Automation"
    Dim r As Long, uPicInfo As uPicDesc, IID_IDispatch As Guid, IPic As IPicture
    'OLE Picture types
    Const PICTYPE_BITMAP = 1
    Const PICTYPE_ENHMETAFILE = 4
    ' Create the Interface GUID (for the IPicture interface)
    With IID_IDispatch
        .Data1 = &H7BF80980
        .Data2 = &HBF32
        .Data3 = &H101A
        .Data4(0) = &H8B
        .Data4(1) = &HBB
        .Data4(2) = &H0
        .Data4(3) = &HAA
        .Data4(4) = &H0
        .Data4(5) = &H30
        .Data4(6) = &HC
        .Data4(7) = &HAB
    End With
    ' Fill uPicInfo with necessary parts.

    With uPicInfo
        .Size = Len(uPicInfo) ' Length of structure.
        .Type = PICTYPE_BITMAP ' Type of Picture
        .hPic = hPic ' Handle to image.
        .hPal = hPal ' Handle to palette (if bitmap).
    End With

    ' Create the Picture object.
    r = OleCreatePictureIndirect(uPicInfo, IID_IDispatch, True, IPic)

    ' If an error occurred, show the description
    If r <> 0 Then Debug.Print "Create Picture: " & fnOLEError(r)

    ' Return the new Picture object.
    Set CreatePicture = IPic
End Function


' Subroutine    : fnOLEError
' Purpose       : Gets the message text for standard OLE errors

Private Function fnOLEError(lErrNum As Long) As String
    'OLECreatePictureIndirect return values
    Const E_ABORT = &H80004004
    Const E_ACCESSDENIED = &H80070005
    Const E_FAIL = &H80004005
    Const E_HANDLE = &H80070006
    Const E_INVALIDARG = &H80070057
    Const E_NOINTERFACE = &H80004002
    Const E_NOTIMPL = &H80004001
    Const E_OUTOFMEMORY = &H8007000E
    Const E_POINTER = &H80004003
    Const E_UNEXPECTED = &H8000FFFF
    Const S_OK = &H0

    Select Case lErrNum
        Case E_ABORT
            fnOLEError = " Aborted"
        Case E_ACCESSDENIED
            fnOLEError = " Access Denied"
        Case E_FAIL
            fnOLEError = " General Failure"
        Case E_HANDLE
            fnOLEError = " Bad/Missing Handle"
        Case E_INVALIDARG
            fnOLEError = " Invalid Argument"
        Case E_NOINTERFACE
            fnOLEError = " No Interface"
        Case E_NOTIMPL
            fnOLEError = " Not Implemented"
        Case E_OUTOFMEMORY
            fnOLEError = " Out of Memory"
        Case E_POINTER
            fnOLEError = " Invalid Pointer"
        Case E_UNEXPECTED
            fnOLEError = " Unknown Error"
        Case S_OK
            fnOLEError = " Success!"
    End Select
End Function

' Routine   : SaveClip2Bit
' Purpose   : Saves Picture object to desired location.
' Arguments : Path to save the file

Public Sub SaveClip2Bit(savePath As String)
On Error GoTo ErrHandler:
    AltPrintScreen
    Pause (3)
    SavePicture PastePicture, savePath
errExit:
        Exit Sub
ErrHandler:
    Debug.Print "Save Picture: (" & Err.Number & ") - " & Err.Description
    Resume errExit
End Sub

' Routine   : Pause
' Purpose   : Gives a short interval for proper image capture.
' Arguments : Seconds to wait.

Public Function Pause(NumberOfSeconds As Variant)
On Error GoTo Err_Pause
    Dim PauseTime As Variant, start As Variant
    PauseTime = NumberOfSeconds
    start = Timer
    Do While Timer < start + PauseTime
        DoEvents
    Loop
Exit_Pause:
    Exit Function
Err_Pause:
    MsgBox Err.Number & " - " & Err.Description, vbCritical, "Pause()"
    Resume Exit_Pause
End Function

VBA Access. Crear ruta completa de carpetas. MakeDirFullPath.

Public Function MakeDirFullPath(ByVal sPath As String) As Boolean
On Error GoTo error
    'crear todo el path de un directorio
    If Right(sPath, 1) = "\" Then
        sPath = Left(sPath, Len(sPath) - 1)
    End If
    Dim SplitPath() As String
    SplitPath = Split(sPath, "\")
    Dim Value As Integer
    Dim Merge As String
    For Value = 0 To UBound(SplitPath)
        If Value <> 0 Then
            Merge = Merge & "\"
        End If
        Merge = Merge & SplitPath(Value)
        If Dir(Merge, vbDirectory) = "" Then
            MkDir Merge
        End If
    Next
    SetAttr sPath, vbNormal
    MakeDirFullPath = True
   
Exit Function
error:
    MakeDirFullPath = False
    Debug.Print Err.Description
End Function

VBA Access. Obtener nombre de usuario y máquina. GetWindowsUser, GetComputerName.

Public Function GetWindowsUser() As String
On Error GoTo error
    Dim sUsername As String
    Dim objNetwork As Object
   
    sUsername = Environ$("username")
   
    If sUsername = "" Then
        Set objNetwork = CreateObject("WScript.Network")
        sUsername = objNetwork.username
        Set objNetwork = Nothing
    End If

    GetWindowsUser = sUsername
   
Exit Function
error:
    Debug.Print Err.Description
    GetWindowsUser = ""
End Function

Public Function GetComputerName() As String
On Error GoTo error
    Dim sComputerName As String
    Dim objNetwork As Object
   
    sComputerName = Environ$("ComputerName")
       
    If sComputerName = "" Then
        Set objNetwork = CreateObject("WScript.Network")
        sComputerName = objNetwork.ComputerName
        Set objNetwork = Nothing
    End If
       
    GetComputerName = sComputerName

Exit Function
error:
    Debug.Print Err.Description
    GetComputerName = ""
End Function

VBA Access. Módulo de clase clsResizeForm. Redimensionar formulario. Resize form.

Option Compare Database
Option Explicit

'CREDITS:
'This modResizeForm module was created by Jamie Czernik 31st March 2000 (jsczernik@hotmail.com)
'The module was updated by Dr. Martin Dumskyj 30th January 2001 (mdumskyj@sghms.ac.UK)
'Module Declarations (here, set the original resolution width was made the form)

'* changes to use as a class and now can pass the resolutionX as an argument
'* To use, put on event onload form passing form and DesignResolutionX as argument  : create object and then ... objResizeForm.ResizeForm Me, 1024
'* Added control for 64 bits Windows API declaration
'* Tip: DesignResolutionX nomally must be the resolutionX that was designed the form to adjust to the current resolution

Const WM_HORZRES = 8
Const WM_VERTRES = 10

Dim m_DesignResolutionX As Integer

Dim Width As Integer
Dim Factor As Single 'Used as multiplier for current size properties'

'Windows API Function Declarations
#If Win64 = 1 Then
    Private Declare PtrSafe Function WM_apiGetDeviceCaps Lib "gdi32" Alias "GetDeviceCaps" (ByVal hDC As Long, ByVal nIndex As Long) As Long
    Private Declare PtrSafe Function WM_apiGetDesktopWindow Lib "user32" Alias "GetDesktopWindow" () As Long
    Private Declare PtrSafe Function WM_apiGetDC Lib "user32" Alias "GetDC" (ByVal hWnd As Long) As Long
    Private Declare PtrSafe Function WM_apiReleaseDC Lib "user32" Alias "ReleaseDC" (ByVal hWnd As Long, ByVal hDC As Long) As Long
    Private Declare PtrSafe Function WM_apiGetSystemMetrics Lib "user32" Alias "GetSystemMetrics" (ByVal nIndex As Long) As Long
#Else
    Private Declare Function WM_apiGetDeviceCaps Lib "gdi32" Alias "GetDeviceCaps" (ByVal hDC As Long, ByVal nIndex As Long) As Long
    Private Declare Function WM_apiGetDesktopWindow Lib "user32" Alias "GetDesktopWindow" () As Long
    Private Declare Function WM_apiGetDC Lib "user32" Alias "GetDC" (ByVal hWnd As Long) As Long
    Private Declare Function WM_apiReleaseDC Lib "user32" Alias "ReleaseDC" (ByVal hWnd As Long, ByVal hDC As Long) As Long
    Private Declare Function WM_apiGetSystemMetrics Lib "user32" Alias "GetSystemMetrics" (ByVal nIndex As Long) As Long
#End If

Function GetScreenResolution() As String
    'returns the height and width
    Dim DisplayHeight As Integer
    Dim DisplayWidth As Integer
    Dim hDesktopWnd As Long
    Dim hDCcaps As Long
    Dim iRtn As Integer
    'API call get current resolution
    hDesktopWnd = WM_apiGetDesktopWindow() 'get handle to desktop
    hDCcaps = WM_apiGetDC(hDesktopWnd) 'get display context for desktop
    DisplayHeight = WM_apiGetDeviceCaps(hDCcaps, WM_VERTRES)
    DisplayWidth = WM_apiGetDeviceCaps(hDCcaps, WM_HORZRES)
    iRtn = WM_apiReleaseDC(hDesktopWnd, hDCcaps) 'release display context
    GetScreenResolution = DisplayWidth & "x" & DisplayHeight
    Width = DisplayWidth
End Function

Public Sub ReSizeForm(frm As Form, Optional ByVal DesignResolutionX As Integer = 1024)
On Error Resume Next
    Dim ctl As Control
 
    m_DesignResolutionX = DesignResolutionX
    SetFactor 'Call to procedure SetFactor
 
    With frm
        .Width = frm.Width * Factor
    End With
 
    For Each ctl In frm.Controls
        With ctl
            .Height = ctl.Height * Factor
            .Left = ctl.Left * Factor
            .Top = ctl.Top * Factor
            .Width = ctl.Width * Factor
            .FontSize = .FontSize * Factor
        End With
    Next ctl
End Sub

Sub SetFactor()
    GetScreenResolution 'Call to function GetScreenResolution
    Factor = Width / m_DesignResolutionX
End Sub

VBA Access. Módulo de clase clsCrypt. Encriptar y Desencriptar texto con RC4. Crypt, Decrypt.

Option Compare Database
Option Explicit

'https://stackoverflow.com/questions/7025644/vb6-encrypt-text-using-password

Public Function Crypt(ByVal sValue As String, sKey As String) As String
On Error GoTo error
    Crypt = ToHexDump(CryptRC4(sValue, sKey))

Exit Function
error:
    Crypt = sValue
    Debug.Print Err.Description
End Function

Public Function DeCrypt(ByVal sValue As String, sKey As String) As String
On Error GoTo error
    DeCrypt = CryptRC4(FromHexDump(sValue), sKey)

Exit Function
error:
    DeCrypt = sValue
    Debug.Print Err.Description
End Function

Private Function CryptRC4(sText As String, sKey As String) As String
    Dim baS(0 To 255) As Byte
    Dim baK(0 To 255) As Byte
    Dim bytSwap     As Byte
    Dim lI          As Long
    Dim lJ          As Long
    Dim lIdx        As Long

    For lIdx = 0 To 255
        baS(lIdx) = lIdx
        baK(lIdx) = Asc(Mid$(sKey, 1 + (lIdx Mod Len(sKey)), 1))
    Next
    For lI = 0 To 255
        lJ = (lJ + baS(lI) + baK(lI)) Mod 256
        bytSwap = baS(lI)
        baS(lI) = baS(lJ)
        baS(lJ) = bytSwap
    Next
    lI = 0
    lJ = 0
    For lIdx = 1 To Len(sText)
        lI = (lI + 1) Mod 256
        lJ = (lJ + baS(lI)) Mod 256
        bytSwap = baS(lI)
        baS(lI) = baS(lJ)
        baS(lJ) = bytSwap
        CryptRC4 = CryptRC4 & Chr$((pvCryptXor(baS((CLng(baS(lI)) + baS(lJ)) Mod 256), Asc(Mid$(sText, lIdx, 1)))))
    Next
End Function

Private Function pvCryptXor(ByVal lI As Long, ByVal lJ As Long) As Long
    If lI = lJ Then
        pvCryptXor = lJ
    Else
        pvCryptXor = lI Xor lJ
    End If
End Function

Private Function ToHexDump(sText As String) As String
    Dim lIdx            As Long

    For lIdx = 1 To Len(sText)
        ToHexDump = ToHexDump & Right$("0" & Hex(Asc(Mid(sText, lIdx, 1))), 2)
    Next
End Function

Private Function FromHexDump(sText As String) As String
    Dim lIdx            As Long

    For lIdx = 1 To Len(sText) Step 2
        FromHexDump = FromHexDump & Chr$(CLng("&H" & Mid(sText, lIdx, 2)))
    Next
End Function

VBA Access. Módulo de clase clsZip. Comprimir y descomprimir ficheros o carpetas: Zip, Unzip.

Option Compare Database
Option Explicit

'Zip / UnZip file or folder
'http://www.codekabinett.com/rdumps.php?Lang=2&targetDoc=create-zip-archive-vba-shell32

Public Function Zip(ByVal zipArchivePath As String, ByVal addPath As String) As String
On Error GoTo error
    Dim sh As Object
    Dim fSource As Object
    Dim fTarget As Object
    Dim iSource As Object
    Dim sourceItem As Object
    Dim i As Long
 
    Set sh = CreateObject("Shell.Application")

    Set fTarget = sh.NameSpace((zipArchivePath))
    If fTarget Is Nothing Then
        createZipFile zipArchivePath
        Set fTarget = sh.NameSpace((zipArchivePath))
    End If
 
    Dim containingFolder As String
    Dim itemToZip As String
    containingFolder = Left(addPath, InStrRev(addPath, "\"))
    itemToZip = Mid(addPath, InStrRev(addPath, "\") + 1)

    Set fSource = sh.NameSpace((containingFolder))
    For i = 0 To fSource.Items.Count - 1
        If fSource.Items.Item((i)).Name = itemToZip Then
            Set sourceItem = fSource.Items.Item((i))
            Exit For
        End If
    Next i
 
    fTarget.CopyHere sourceItem
 
    Zip = ""
     
Exit Function
error:
    Zip = Err.Number & ": " & Err.Description
    Debug.Print Err.Number & ": " & Err.Description, , "Zip"
End Function

Public Function UnZip(ByVal zipArchivePath As String, ByVal extractToFolder As String) As String
On Error GoTo error
    Dim sh As Object
    Dim fSource As Object
    Dim fTarget As Object
 
    Set sh = CreateObject("Shell.Application")

    Set fSource = sh.NameSpace((zipArchivePath))
    Set fTarget = sh.NameSpace((extractToFolder))
 
    fTarget.CopyHere fSource.Items
 
    UnZip = ""
 
Exit Function
error:
    UnZip = Err.Number & ": " & Err.Description
    Debug.Print Err.Number & ": " & Err.Description, , "UnZip"
End Function

Public Sub DeleteFileWithInvokeVerb(ByVal zipArchivePath As String, ByVal deleteFileName As String)
On Error GoTo error
    Dim sh As Object
    Dim fTarget As Object
    Dim iSource As Object
    Dim targetItem As Object
    Dim i As Long
 
    Set sh = CreateObject("Shell.Application")
    Set fTarget = sh.NameSpace((zipArchivePath))
 
    For i = 0 To fTarget.Items.Count - 1
        If fTarget.Items.Item((i)).Name = deleteFileName Then
            Set targetItem = fTarget.Items.Item((i))
            Exit For
        End If
    Next i

    If Not targetItem Is Nothing Then
        targetItem.InvokeVerb "Delete"
    End If

Exit Sub
error:
    MsgBox Err.Number & ": " & Err.Description
End Sub

Private Function createZipFile(ByVal fileName As String) As Boolean
On Error GoTo error
    Dim fileNo As Integer
    Dim ZIPFileEOCD(22) As Byte
 
    'Signature of the EOCD:  &H06054b50
    ZIPFileEOCD(0) = Val("&H50")
    ZIPFileEOCD(1) = Val("&H4b")
    ZIPFileEOCD(2) = Val("&H05")
    ZIPFileEOCD(3) = Val("&H06")
 
    fileNo = FreeFile
    Open fileName For Binary Access Write As #fileNo
    Put #fileNo, , ZIPFileEOCD
    Close #fileNo
 
    createZipFile = True
 
Exit Function
error:
    createZipFile = False
    Debug.Print Err.Number & ": " & Err.Description
End Function

VBA Access. Módulo de clase clsMail para enviar correo usando CDO.

Option Compare Database
Option Explicit

Const CONNECTION_TIMEOUT = 60

Private m_ServidorSMTP As String
Private m_PuertoSMTP As Integer
Private m_MetodoEnvio As Integer
Private m_UsarAutenticacion As Integer
Private m_UsarSSL As Boolean
Private m_Usuario As String
Private m_Contrasena As String
Private m_From As String
Private m_ToAddress As String
Private m_CC As String
Private m_BCC As String

Public Enum formatBody
    Html = 1
    Text = 2
End Enum

Public Enum cdoSendMethod
    cdoSendUsingPickup = 1
    cdoSendUsingPort = 2
End Enum

Public Enum cdoUseAuthentication
    cdoAnonymous = 1
    cdoBasic = 2
    cdoNTLM = 3
End Enum

Private Sub Class_Initialize()
    'initialize values
    m_ServidorSMTP = ""
    m_PuertoSMTP = 25
   
    '1=cdoSendUsingPickup / 2=cdoSendUsingPort
    m_MetodoEnvio = cdoSendUsingPort
   
    '0=cdoAnonymous / 1=cdoBasic / 2=cdoNTLM
    m_UsarAutenticacion = cdoBasic
   
    m_UsarSSL = False
    m_Usuario = ""
    m_Contrasena = ""
    m_From = ""
    m_ToAddress = ""
    m_CC = ""
    m_BCC = ""

End Sub

Private Sub Class_Terminate()
    'deallocate if needed
End Sub

Public Property Get ServidorSMTP() As String
    ServidorSMTP = m_ServidorSMTP
End Property

Public Property Let ServidorSMTP(ByVal Value As String)
    m_ServidorSMTP = Value
End Property

Public Property Get PuertoSMTP() As Integer
    PuertoSMTP = m_PuertoSMTP
End Property

Public Property Let PuertoSMTP(ByVal Value As Integer)
    m_PuertoSMTP = Value
End Property

Public Property Get MetodoEnvio() As cdoSendMethod
    MetodoEnvio = m_MetodoEnvio
End Property

Public Property Let MetodoEnvio(ByVal Value As cdoSendMethod)
    m_MetodoEnvio = Value
End Property

Public Property Get UsarAutenticacion() As cdoUseAuthentication
    UsarAutenticacion = m_UsarAutenticacion
End Property

Public Property Let UsarAutenticacion(ByVal Value As cdoUseAuthentication)
    m_UsarAutenticacion = Value
End Property

Public Property Get UsarSSL() As Boolean
    UsarSSL = m_UsarSSL
End Property

Public Property Let UsarSSL(ByVal Value As Boolean)
    m_UsarSSL = Value
End Property

Public Property Get Usuario() As String
    Usuario = m_Usuario
End Property

Public Property Let Usuario(ByVal Value As String)
    m_Usuario = Value
End Property

Public Property Get Contrasena() As String
    Contrasena = m_Contrasena
End Property

Public Property Let Contrasena(ByVal Value As String)
    m_Contrasena = Value
End Property

Public Property Get From() As String
    From = m_From
End Property

Public Property Let From(ByVal Value As String)
    m_From = Value
End Property

Public Property Get ToAddress() As String
    ToAddress = m_ToAddress
End Property

Public Property Let ToAddress(ByVal Value As String)
    m_ToAddress = Value
End Property

Public Property Get CC() As String
    CC = m_CC
End Property

Public Property Let CC(ByVal Value As String)
    m_CC = Value
End Property

Public Property Get BCC() As String
    BCC = m_BCC
End Property

Public Property Let BCC(ByVal Value As String)
    m_BCC = Value
End Property

Public Function SendMail(ByVal sSubject As String, ByVal sBody As String, Optional fBody As formatBody = Text, Optional ByRef sAttachmentsArray As Variant) As String
On Error GoTo error
    Dim iMsg As Object
    Dim iConf As Object
    Dim Flds As Variant

    Set iMsg = CreateObject("CDO.Message")
    Set iConf = CreateObject("CDO.Configuration")

    'cdo  valores por defecto
    iConf.Load -1
   
    Set Flds = iConf.Fields
    With Flds
        .item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = m_ServidorSMTP
        .item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = m_PuertoSMTP

        '1=cdoSendUsingPickup / 2=cdoSendUsingPort
        .item("http://schemas.microsoft.com/cdo/configuration/sendusing") = m_MetodoEnvio
       
        '0=cdoAnonymous / 1=cdoBasic /2=cdoNTLM
        .item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = m_UsarAutenticacion
       
        'True/False
        .item("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = m_UsarSSL

        .item("http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout") = CONNECTION_TIMEOUT

        .item("http://schemas.microsoft.com/cdo/configuration/sendusername") = m_Usuario
        .item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = m_Contrasena

        .Update
    End With

    With iMsg
        Set .Configuration = iConf
        .To = m_ToAddress
        .CC = m_CC
        .BCC = m_BCC
        .From = m_From
        .Subject = sSubject
       
        If fBody = Text Then
            .TextBody = sBody
        Else
            .HtmlBody = sBody
        End If

        If Not IsMissing(sAttachmentsArray) Then
            If IsArray(sAttachmentsArray) Then
                Dim i As Integer
                For i = LBound(sAttachmentsArray) To UBound(sAttachmentsArray)
                    .AddAttachment sAttachmentsArray(i)
                Next i
            End If
        End If
        .Send
    End With

    Set iMsg = Nothing
    Set iConf = Nothing
    Set Flds = Nothing
   
    If Err.Number = 0 Then
        Debug.Print "Message sent successfully!"
        SendMail = ""
    End If

Exit Function
Resume
error:
    Debug.Print Err.Number & " " & Err.Description
    SendMail = Err.Number & " " & Err.Description
End Function

martes, 12 de septiembre de 2017

VBA Access. Módulo de clase clsLog. Escribir en un fichero. Útil para crear ficheros tipo Log o escribir cualquier valor a un fichero.

Option Compare Database
Option Explicit

Private Sub Class_Initialize()
    'initialize values
End Sub

Private Sub Class_Terminate()
    'deallocate if needed
End Sub

Public Function Log(ByVal sPathFileName As String, ByVal sValueToLog As String, Optional ByVal bAppend As Boolean = True) As String
On Error GoTo error
    Dim sValue As String    'valor a escribir en el log
    Dim SFName As String    'ruta y nombre completo del fichero de texto
    Dim iFNumber As Integer

    sValue = sValueToLog
    SFName = sPathFileName

    'crear la ruta completa si no existe
    MakeDirFullPath (Left(SFName, InStrRev(SFName, "\") - 1))

    'obtener numero de fichero
    iFNumber = FreeFile

    'añadir o sobreescribir el fichero
    If bAppend Then
        Open SFName For Append As #iFNumber
    Else
        Open SFName For Output As #iFNumber
    End If

    Print #iFNumber, sValue
    Close #iFNumber

    Log = SFName

Exit Function
error:
    Log = ""
    Debug.Print Err.Description
End Function

Private Function MakeDirFullPath(ByVal sPath As String) As Boolean
On Error GoTo error
    'crear todo el path de un directorio
    If Right(sPath, 1) = "\" Then
        sPath = Left(sPath, Len(sPath) - 1)
    End If
    Dim SplitPath() As String
    SplitPath = Split(sPath, "\")
    Dim Value As Integer
    Dim Merge As String
    For Value = 0 To UBound(SplitPath)
        If Value <> 0 Then
            Merge = Merge & "\"
        End If
        Merge = Merge & SplitPath(Value)
        If Dir(Merge, vbDirectory) = "" Then
            MkDir Merge
        End If
    Next
    SetAttr sPath, vbNormal
    MakeDirFullPath = True
   
Exit Function
error:
    MakeDirFullPath = False
    Debug.Print Err.Description
End Function

viernes, 8 de septiembre de 2017

VBA Access. Obtener más datos de ayuda para depurar errores en una aplicación Access en producción.

'VBA Access no ofrece de forma nativa una manera de obtener datos como el nombre del procedimiento que ha causado el error y la línea.
'Si tenemos problemas para depurar los errores de nuestra aplicación Access, podemos añadir un poco de código para ayudar a esta tarea, sobretodo en un entorno de producción.
'NOTA: Numerar las líneas, nos permitirá con la función ERL obtener la línea que ha causado el error

Function ProcName(Arg1 As String, Arg2 As String)
On Error GoTo error

Const METHODNAME = "ProcName"

10  línea de código
30  línea de código
40  línea de código
...

Exit Function
error:
    MsgBox "ModName: " & Application.VBE.ActiveCodePane.CodeModule.Name & vbCrLf & _
            "ProcCall: " & METHODNAME & vbCrLf & _
            "ErrorLine: " & Erl & vbCrLf & _
            "ErrorNum: " & Err.Number & vbCrLf & _
            "ErrorDesc: " & Err.Description & vbCrLf & _
            "SourceCode: " & Err.Source, , "AppName"
End Function

jueves, 7 de septiembre de 2017

VBA Access. Crear un recordset en memoria sin vincular a ninguna tabla y poder modificar sus valores.

'De utilidad si necesitamos añadir un campo adicional que una tabla no tiene.
'En este caso el campo sería: Seleccionadas, que representa el numero de unidades seleccionadas de un determinado lote
'Al hacerlo de esta manera, al asignar al formulario el Recordset construido en memoria, podemos modificar los valores sin ningún problema

...
    'Creamos el Recordset en memoria
    Dim rsvM As ADODB.Recordset
    Set rsM = New ADODB.Recordset
    With rsM
        .Fields.Append "ReferenciaArtículo", adVarChar, 20, adFldKeyColumn
        .Fields.Append "Descripción", adVarChar, 250, adFldMayBeNull
        .Fields.Append "Lote", adVarChar, 20, adFldMayBeNull
        .Fields.Append "FechaCaducidad", adDate, , adFldMayBeNull
        .Fields.Append "Unidades", adDecimal, , adFldMayBeNull
        .Fields.Append "Seleccionadas", adDecimal, , adFldMayBeNull
 
        .CursorType = adOpenKeyset
        .CursorLocation = adUseClient
        .LockType = adLockPessimistic
        .Open
    End With

    'Leemos registros de la tabla Lotes en modo solo lectura
    Dim rs New As ADODB.RecordSet
    rs.Open "SELECT * FROM Lotes Where Referencia = 'XXXXXX'", cadenaconexion, adOpenStatic, adLockReadOnly
    Do While Not rs.EOF
        'Añadimos registros al recordset en memoria
        'NOTA: La columna: Seleccionadas, no existe en la tabla Lotes
        With rsM
            .AddNew
            .Fields("ReferenciaArtículo") = rs.Fields("ReferenciaArtículo")
            .Fields("Descripción") = rs.Fields("Descripción")
            .Fields("Lote") = rs.Fields("Lote")
            .Fields("FechaCaducidad") = rs.Fields("FechaCaducidad")
            .Fields("Unidades") = rs.Fields("Unidades")
            .Fields("Seleccionadas") = 0
            .Update
        End With
     
        rs.MoveNext
    Loop

    'Cerramos y liberamos recursos del recordset de la lectura de la tabla
    rs.Close
    Set rs = Nothing

   'Asignamos el recordset en memoria al formulario para poder modificar el valor del campo: Seleccionadas. En el detalle del formulario deberemos tener en diversos textbox los campos del recordset.
  Set Me.Recordset = rsM

...

viernes, 1 de septiembre de 2017

Batch Script cerrar sesiones activas y desconectadas en un Windows Terminal Server para realizar un mantenimiento

rem Ejemplo bacth script para Terminal Server que avisa y cierra sesiones Activas y Desconectadas, exceptuando 1 Sesion y ademas para e inicia un servicio. Funciona correctamente en Windows 2008 Server R2
rem enviar mensajes a las sesiones Activas aviso menos a la sesion de Usuario
for /F "tokens=1,2,3,4,5" %%A in ('"query session | find "Activo""') DO (if NOT %%B==Usuario (msg %%B "Por mantenimiento, en 5 minutos su sesion va a ser cerrada. Conecte de nuevo en unos minutos." 2> nul))
rem espera 5 minutos antes de proceder
timeout /t 300 /nobreak
rem paramos un servicio
net stop nombre_servicio 2> nul
rem Esperar 30 segundos
timeout 30
rem cerramos sesiones Activas y Desconectadas menos la sesion de Usuario
for /F "tokens=1,2,3,4,5" %%A in ('"query session | find "Activo""') DO (if NOT %%B==Usuario (logoff %%C))
for /F "tokens=1,2,3,4,5" %%A in ('"query session | find "Desc""') DO (if NOT %%A==Usuario (logoff %%B))
rem Esperar 30 segundos
timeout 30
rem iniciar servicio
net start nombre_servicio 2> nul
rem Esperar 5 segundos
timeout 5

VBA Access. Redondeo de números decimales con el método medio redondeo. Alternativa a la función Round (bankers round)

 Private Function Redondeo(ByVal Numero As Variant, ByVal Decimales As Integer) As Double     'Aplica método medio redondeo (half round ...