Views

...

Important:

Quaisquer soluções e/ou desenvolvimento de aplicações pessoais, ou da empresa, que não constem neste Blog podem ser tratados como consultoria freelance.

E-mails

Deixe seu e-mail para receber atualizações...

eBook Promo

VBA Excel - Deletando Linhas, Linhas em branco e Linhas duplicadas - Delete Rows, Blank Rows, Delete Row on Cell and Delete Duplicate Rows


Excluir as linhas em branco ou todas as que estiverem duplicadas numa base de dados pode ser facilitado, seguem três códigos: 

DeleteBlankRows

DeleteRowOnCell

DeleteDuplicateRows

O código DeleteBlankRows descrito a seguir irá apagar todas as linhas em branco na planilha especificada pelo parâmetro WorksheetName. Se este for omitido, a planilha ativa será utilizada. O procedimento apagará as linhas que estiverem totalmente em branco ou contiverem células cujo o conteúdo seja apenas um único apóstrofe (caracter que controla a formatação). O procedimento exige a função IsRowClear, mostrada após o procedimento DeleteBlankRows. 

CÓDIGO:

Sub DeleteBlankRows(Optional WorksheetName As Variant)
' This function will delete all blank rows on the worksheet
' named by WorksheetName. This will delete rows that are
' completely blank (every cell = vbNullString) or that have
' cells that contain only an apostrophe (special Text control
' character).
' The code will look at each cell that contains a formula,
' then look at the precedents of that formula, and will not
' delete rows that are a precedent to a formula. This will
' prevent deleting precedents of a formula where those
' precedents are in lower numbered rows than the formula
' (e.g., formula in A10 references A1:A5). If a formula
' references cell that are below (higher row number) the
' last used row (e.g, formula in A10 reference A20:A30 and
' last used row is A15), the refences in the formula will
' be changed due to the deletion of rows above the formula.
'

Dim RefColl As Collection
Dim RowNum As Long
Dim Prec As Range
Dim Rng As Range
Dim DeleteRange As Range
Dim LastRow As Long
Dim FormulaCells As Range
Dim Test As Long
Dim WS As Worksheet
Dim PrecCell As Range

If IsMissing(WorksheetName) = True Then
    Set WS = ActiveSheet
Else
    On Error Resume Next
    Set WS = ActiveWorkbook.Worksheets(WorksheetName)
    If Err.Number <> 0 Then
        '''''''''''''''''''''''''''''''
        ' Invalid worksheet name.
        '''''''''''''''''''''''''''''''
        Exit Sub
    End If
End If
    

If Application.WorksheetFunction.CountA(WS.UsedRange.Cells) = 0 Then
    ''''''''''''''''''''''''''''''
    ' Worksheet is blank. Get Out.
    ''''''''''''''''''''''''''''''
    Exit Sub
End If

''''''''''''''''''''''''''''''''''''''
' Find the last used cell on the
' worksheet.
''''''''''''''''''''''''''''''''''''''
Set Rng = WS.Cells.Find(what:="*", after:=WS.Cells(WS.Rows.Count, WS.Columns.Count), lookat:=xlPart, _
    searchorder:=xlByColumns, searchdirection:=xlPrevious, MatchCase:=False)

LastRow = Rng.Row

Set RefColl = New Collection

'''''''''''''''''''''''''''''''''''''
' We go from bottom to top to keep
' the references intact, preventing
' #REF errors.
'''''''''''''''''''''''''''''''''''''
For RowNum = LastRow To 1 Step -1
    Set FormulaCells = Nothing
    If Application.WorksheetFunction.CountA(WS.Rows(RowNum)) = 0 Then
        ''''''''''''''''''''''''''''''''''''
        ' There are no non-blank cells in
        ' row R. See if R is in the RefColl
        ' reference Collection. If not,
        ' add row R to the DeleteRange.
        ''''''''''''''''''''''''''''''''''''
        On Error Resume Next
        Test = RefColl(CStr(RowNum))
        If Err.Number <> 0 Then
            ''''''''''''''''''''''''''
            ' R is not in the RefColl
            ' collection. Add it to
            ' the DeleteRange variable.
            ''''''''''''''''''''''''''
            If DeleteRange Is Nothing Then
                Set DeleteRange = WS.Rows(RowNum)
            Else
                Set DeleteRange = Application.Union(DeleteRange, WS.Rows(RowNum))
            End If
        Else
            ''''''''''''''''''''''''''
            ' R is in the collection.
            ' Do nothing.
            ''''''''''''''''''''''''''
        End If
        On Error GoTo 0
        Err.Clear
    Else
        '''''''''''''''''''''''''''''''''''''
        ' CountA > 0. Find the cells
        ' containing formula, and for
        ' each cell with a formula, find
        ' its precedents. Add the row number
        ' of each precedent to the RefColl
        ' collection.
        '''''''''''''''''''''''''''''''''''''
        If IsRowClear(RowNum:=RowNum) = True Then
            '''''''''''''''''''''''''''''''''
            ' Row contains nothing but blank
            ' cells or cells with only an
            ' apostrophe. Cells that contain
            ' only an apostrophe are counted
            ' by CountA, so we use IsRowClear
            ' to test for only apostrophes.
            ' Test if this row is in the
            ' RefColl collection. If it is
            ' not in the collection, add it
            ' to the DeleteRange.
            '''''''''''''''''''''''''''''''''
            On Error Resume Next
            Test = RefColl(CStr(RowNum))
            If Err.Number = 0 Then
                ''''''''''''''''''''''''''''''''''''''
                ' Row exists in RefColl. That means
                ' a formula is referencing this row.
                ' Do not delete the row.
                ''''''''''''''''''''''''''''''''''''''
            Else
                If DeleteRange Is Nothing Then
                    Set DeleteRange = WS.Rows(RowNum)
                Else
                    Set DeleteRange = Application.Union(DeleteRange, WS.Rows(RowNum))
                End If
            End If
        Else
            On Error Resume Next
            Set FormulaCells = Nothing
            Set FormulaCells = WS.Rows(RowNum).SpecialCells(xlCellTypeFormulas)
            On Error GoTo 0
            If FormulaCells Is Nothing Then
                '''''''''''''''''''''''''
                ' No formulas found. Do
                ' nothing.
                '''''''''''''''''''''''''
            Else
                '''''''''''''''''''''''''''''''''''''''''''''''''''
                ' Formulas found. Loop through the formula
                ' cells, and for each cell, find its precedents
                ' and add the row number of each precedent cell
                ' to the RefColl collection.
                '''''''''''''''''''''''''''''''''''''''''''''''''''
                On Error Resume Next
                For Each Rng In FormulaCells.Cells
                    For Each Prec In Rng.Precedents.Cells
                        RefColl.Add Item:=Prec.Row, key:=CStr(Prec.Row)
                    Next Prec
                Next Rng
                On Error GoTo 0
            End If
        End If
        
    End If
    
    '''''''''''''''''''''''''
    ' Go to the next row,
    ' moving upwards.
    '''''''''''''''''''''''''
Next RowNum


''''''''''''''''''''''''''''''''''''''''''
' If we have rows to delete, delete them.
''''''''''''''''''''''''''''''''''''''''''

If Not DeleteRange Is Nothing Then
    DeleteRange.EntireRow.Delete shift:=xlShiftUp
End If

End Sub
Function IsRowClear(RowNum As Long) As Boolean
''''''''''''''''''''''''''''''''''''''''''''''''''
' IsRowClear
' This procedure returns True if all the cells
' in the row specified by RowNum as empty or
' contains only a "'" character. It returns False
' if the row contains only data or formulas.
''''''''''''''''''''''''''''''''''''''''''''''''''
Dim ColNdx As Long
Dim Rng As Range
ColNdx = 1
Set Rng = Cells(RowNum, ColNdx)
Do Until ColNdx = Columns.Count
    If (Rng.HasFormula = True) Or (Rng.Value <> vbNullString) Then
        IsRowClear = False
        Exit Function
    End If
    Set Rng = Cells(RowNum, ColNdx).End(xlToRight)
    ColNdx = Rng.Column
Loop

IsRowClear = True

End Function


Este código, DeleteBlankRows, excluirá uma linha, se ela estiver toda em branco. Apagará a linha inteira se uma célula na coluna especificada estiver em branco. Somente a coluna marcada, outras serão ignoradas.

CÓDIGO:
Public Sub DeleteRowOnCell() 

         On Error Resume Next 

         Selection.SpecialCells (xlCellTypeBlanks). EntireRow.Delete 

         ActiveSheet.UsedRange 

End Sub

Para usar este código, selecione um intervalo de células por colunas e, em seguida, execute o código. Se a célula na coluna estiver em branco, a linha inteira será excluída. Para processar toda a coluna, clique no cabeçalho da coluna para selecionar a coluna inteira.

Este código eliminará as linhas duplicadas em um intervalo. Para usar, selecione uma coluna como intervalo de células, que compreende o intervalo de linhas duplicadas a serem excluídas. Somente a coluna selecionada é usada para comparação. 


CÓDIGO: 
Sub DeleteDuplicateRows Pública () 
''''''''''''''''''''''''''''''''''''''''''''' '''''''''''''''''''''''''''''''' 
'DeleteDuplicateRows 
"Isto irá apagar registros duplicados, com base na coluna ativa. Ou seja, 
"se o mesmo valor é encontrado mais de uma vez na coluna activa, mas todos 
"os primeiros (linha número mais baixo) serão excluídos. 
" 
'Para executar a macro, selecione a coluna inteira que você deseja escanear 
'duplica e executar este procedimento. 
'''''''''''''''''''''''''''''''''''''''''''' '''''''''''''''''''''''''''''''''' 

R Dim As Long 
Dim N Long 
V Variant Dim 
Dim Rng Gama 

On Error GoTo EndMacro 
Application.ScreenUpdating = False 
Application.Calculation = xlCalculationManual 


Set Rng = Application.Intersect (ActiveSheet.UsedRange, _ 
ActiveSheet.Columns (ActiveCell.Column)) 

Application.StatusBar = "Processamento de Linha:" & Format (Rng.Row , "#,## 0 ") 

N = 0 
para R = Rng.Rows.Count To 2 Step -1 
Se Mod R 500 = 0 Then 
Application.StatusBar = "Linha de processamento:" & Format (R ", # # 0 ") 
End If 

= Rng.Cells (R, 1). Valor V 
'''''''''''''''''''''''''''''''' ''''''''''''''''''''''''''''''''''''''''''' 
Nota "que COUNTIF obras estranhamente com uma variante que é igual a vbNullString. 
" Ao invés de passar na variante, você precisa passar vbNullString explicitamente. 
''''''''''''''''''''''''''''''''''' '''''''''''''''''''''''''''''''''''''''' 
Se V = vbNullString Então 
Se Application.WorksheetFunction. CONT.SE (Rng.Columns (1), vbNullString)> 1 Então 
Rng.Rows (R). EntireRow.Delete 
N = N + 1 
End If 
Else 
Se Application.WorksheetFunction.CountIf (Rng.Columns (1), V)> 1 Então, 
(R). Rng.Rows EntireRow.Delete 
N = N + 1 
End If 
End If 
Next R 

EndMacro: 

Application.StatusBar = False 
Application.ScreenUpdating = True 
Application.Calculation = xlCalculationAutomatic 
MsgBox "Duplicar linhas excluídas:" & CStr (N ) 

End Sub


Reference:

Inspiration:
André Luiz Bernardes

Tags: VBA, delete, row, blank, cell, duplicate

VBA Excel - Detectar a última Célula da Planilha - Detecting a last cell


Esse código é para iniciantes faixas brancas: Como identificar a última célula e portanto a última linha da planilha.

Planilhas constantemente manipuláveis, cujos os dados não são conexões em bases de dados, mas dados colados através de CTRL + V, tendem a deixar dirty areas. Estas acabam por dificultar a detecção da última célula. O exemplo abaixo é uma técnica para teste naquelas bases de dados enormes, com grandes quantidades de dados, acima de 100.000 linhas, as quais devem dar constantes dores de cabeça àqueles que ainda não dominam as técnicas de conexão do MS Excel com o MS Access.

CÓDIGO: 
Function LCell(ws As Worksheet) As Range
  Dim LRow&, LCol%

  On Error Resume Next

  With ws
    Let LRow& = .Cells.Find(What:="*", SearchDirection:=xlPrevious, SearchOrder:=xlByRows).Row
    Let LCol%   = .Cells.Find(What:="*", SearchDirection:=xlPrevious,  SearchOrder:=xlByColumns).Column
  End With

  Set LCell = ws.Cells(LRow&, LCol%)
End Function

Usando esta função:
A função LCell demonstrada aqui não pode ser usada diretamente na planilha, mas pode ser evocada a partir de outra SUB VBA, implemente o código conforme demonstrado abaixo:

CÓDIGO: 
Sub Identifica()
   MsgBox LCell(Sheet1).Row
End Sub

Ahhh, e sempre se pode melhorar:

Function LRow (Rg as Range) As Long
    Dim ix As Long

    Let ix = rg.parent.UsedRange.Row - 1 + rg.parent.UsedRange.Rows.Count 
    Let LRow = ix 
End Function

Reference:

Bob Umlas

Inspiration:
André Luiz Bernardes

Tags: VBA, Tips, dummy, dummies, row, last, cell, célula, dirty area, detect, detectar

VBA Access - Compactando e Descompactando arquivos - Zip and Unzip Files


| Blog Office VBA | Blog Excel | Blog Access |


Em algumas ocasiões precisamos exportar arquivos como parte do fluxo de trabalho dentro da nossa aplicação MS Access, invariavelmente seria muito bom que estes pudessem sair compactados. Mas, se há um ponto sensível com o Zip é o de que não há nenhuma maneira simples de 'Zipar' ou descompactar um arquivos sem depender de um utilitários de terceiros. E, ao pensar sobre isso, considere que a capacidade de 'Zipar' está integrada ao Windows Explorer. Parece haver alguma restrição de licenciamento.

Felizmente, Ron de Bruin forneceu-nos uma solução que envolve automatizar o Windows Explorer (aka Shell32). O objeto para compactação Shell32.Folder pode ser uma pasta real ou uma pasta Zip, disponível para manipulação como se fosse um Shell32.Folder. Assim podemos usar o "Copiar aqui", método do Shell32.Folder, para mover os arquivos para dentro e para fora do arquivo Zip.

Como Ron observou, há um bug sutil quando trata-se da recuperação do Shell32.Folder através do método Shell32.Applications Namespace. 

Portanto, este código não vai funcionar como esperado:

Dim s As String
Dim f As Object 'Shell32.Folder

Let s = "C:\MyZip.zip"
Set f = CreateObject("Shell.Application").Namespace(s)

f.CopyHere "C:\MyText.txt" 'Error occurs here

De acordo com a documentação do MSDN, se o Método Namespace falhar, o valor de retorno será nada, e  poderemos ter um erro aparentemente não relacionado (Error 91 "With or object variable not set"). É por isso que Ron de Bruin usa Variant na sua amostra. 

Convertendo a string em uma variante irá funcionar também:

Dim s As String
Dim f As Object 'Shell32.Folder

Let s = "C:\MyZip.zip"
Set f = CreateObject("Shell.Application").Namespace(CVar(s))

f.CopyHere "C:\MyText.txt"

Alternativamente, pode optar por referenciar a Shell32.dll (normalmente no Windows\System32), modo early bind. A Vinculação antecipada não está sujeita a erro. No entanto, nossa preferência será a de late bind, para evitar qualquer problema com versões que possam ocorrer durante a execução de código num computador diferente, sistemas operacionais diferentes, service packs diferentes e assim por diante.

Ainda assim, o modo early bind pode ser útil para o desenvolvimento e validação do seu código antes de mudá-lo definitivamente para late bind.

Outra questão com a qual precisamos lidar é a de que, por existir apenas um método ou o outro disponível, ("Copiar aqui" ou "Mover para cá") com o objeto Shell32.Folder, temos de considerar como devemos lidar com a nomeação dos arquivos que serão compactados, especialmente quando estivermos descompactando os arquivos que potencialmente têm o mesmo nome ou devem substituir os arquivos originais no diretório de destino. 

Isso pode ser resolvido de duas maneiras diferentes: 

1) Descompacte os arquivos em um diretório temporário, renomeando-os, em seguida, movendo-os para o diretório final ou;

2) Renomeie um arquivo antes de 'zipar', assim terá um nome único quando descompactar Zip e, portanto, poderá ser renomeado. 

A opção 1 é mais segura, mas exige a criação de um diretório temporário e a sua eventual limpeza, mas quando você tem controle sobre o que o diretório de destino conterá, a opção 2 é bastante simples.

Em qualquer abordagem, podemos usar o VBA para renomear um arquivo simplesmente como:

Name strUnzippedFile As strFinalFileName

Finalmente, ao usar Shell32, estamos essencialmente automatizando o aspecto visual do Windows Explorer. Assim, quando invocarmos um "Copiar aqui" (CopyHere), será equivalente a realmente arrastar um arquivo e soltá-lo numa pasta (ou um arquivo zip). Isto significa que virá com os componentes da interface do usuário que podem impor algumas questões, especialmente quando estivermos automatizando o processo. Neste caso, é preciso esperar até que a compressão seja concluída antes de tomarmos qualquer tipo de ação. Porque será uma ação interativa, que ocorre de forma assíncrona, precisaremos escrever um código de espera. 

O monitoramento de uma compressão fora do processo pode ser complicado e por isso desenvolveremos um salvaguarda, que abrange diferentes contingências, tais como a compressão ocorrendo muito rapidamente, ou quando há um atraso entre a caixa de diálogo de compressão.

Faremos isso de 3 maneiras diferentes:

a) Um timing após 3 segundos para os arquivos pequenos, 

b) Acompanhar a contagem de itens do arquivo Zip, 

c) e Monitorização da presença de compressão de diálogo. 

A última parte nos obriga a utilizar o método WScript.Shell object's AppActivate porque ao contrário do método de acesso embutido o WScript.Shell retornará um valor booleano que pode ser usado para determinar se a ativação foi bem sucedida ou não, e, portanto, implicará na presença / ausência do "Comprimir ..." diálogo sem um gerenciamento bagunçado da API.

Exemplo de uso
O código completo está abaixo para usar:

'Cria um novo arquivo Zip e Zipa o arquivo PDF
Zip "C:\Temp\MyNewZipFile.zip", "C:\Temp\MyPdf.pdf

'Unzip o PDF e coloca-o no mesmo diretório
Unzip "C:\Temp\MyNewZipFile.zip"

'Exemplo de múltipla compactação num simples arquivo Zip.
Zip "C:\Temp\MyZipFile.zip", "C:\Temp\A1.pdf"
Zip "C:\Temp\MyZipFile.zip", "C:\Temp\A2.pdf"
Zip "C:\Temp\MyZipFile.zip", "C:\Temp\A3.pdf"

'Descompacta um arquivo Zip com mais de um arquivo
'colocando-o nu mpasta compartilhada sobreescrevendo qualquer arquivo préexistente.

Unzip "C:\Temp\MyZipFile.zip", "Z:\Shared Folder\", True

Aqui está o algoritmo completo do procedimento para Zipar e Descompactar, basta copiá-lo num novo módulo VBA e aproveitar:

Private Declare Sub Sleep Lib "kernel32" ( _
    ByVal dwMilliseconds As Long _)

Public Sub Zip( _
    ZipFile As String, _
    InputFile As String _)

On Error GoTo ErrHandler

    Dim FSO As Object 'Scripting.FileSystemObject
    Dim oApp As Object 'Shell32.Shell
    Dim oFld As Object 'Shell32.Folder
    Dim oShl As Object 'WScript.Shell
    Dim i As Long
    Dim l As Long

    Set FSO = CreateObject("Scripting.FileSystemObject")

    If Not FSO.FileExists(ZipFile) Then
        'Create empty ZIP file
        FSO.CreateTextFile(ZipFile, True).Write _
            "PK" & Chr(5) & Chr(6) & String(18, vbNullChar)
    End If

    Set oApp = CreateObject("Shell.Application")
    Set oFld = oApp.NameSpace(CVar(ZipFile))

    Let i = oFld.Items.Count

    oFld.CopyHere (InputFile)

    Set oShl = CreateObject("WScript.Shell")

    'Search for a Compressing dialog
    Do While oShl.AppActivate("Compressing...") = False
        If oFld.Items.Count > i Then
            'There's a file in the zip file now, but
            'compressing may not be done just yet
            Exit Do
        End If
        If l > 30 Then
            '3 seconds has elapsed and no Compressing dialog
            'The zip may have completed too quickly so exiting
            Exit Do
        End If

        DoEvents

        Sleep 100

        Let l = l + 1
    Loop

    ' Wait for compression to complete before exiting
    Do While oShl.AppActivate("Compressing...") = True
        DoEvents

        Sleep 100
    Loop

ExitProc:
    On Error Resume Next
        Set FSO = Nothing
        Set oFld = Nothing
        Set oApp = Nothing
        Set oShl = Nothing
    Exit Sub
ErrHandler:
    Select Case Err.Number
        Case Else
            MsgBox "Error " & Err.Number & _
                   ": " & Err.Description, _
                   vbCritical, "Unexpected error"
    End Select

    Resume ExitProc

    Resume
End Sub

Public Sub UnZip( _
    ZipFile As String, _
    Optional TargetFolderPath As String = vbNullString, _
    Optional OverwriteFile As Boolean = False _)

On Error GoTo ErrHandler
    Dim oApp As Object
    Dim FSO As Object
    Dim fil As Object
    Dim DefPath As String
    Dim strDate As String

    Set FSO = CreateObject("Scripting.FileSystemObject")
 
   If Len(TargetFolderPath) = 0 Then
        Let DefPath = CurrentProject.Path & "\"
    Else
        If FSO.folderexists(TargetFolderPath) Then
            Let DefPath = TargetFolderPath & "\"
        Else
            Err.Raise 53, , "Folder not found"
        End If
    End If

    If FSO.FileExists(ZipFile) = False Then
        MsgBox "System could not find " & ZipFile _
            & " upgrade cancelled.", _
            vbInformation, "Error Unziping File"
        Exit Sub
    Else
        'Extract the files into the newly created folder
        Set oApp = CreateObject("Shell.Application")

        With oApp.NameSpace(ZipFile & "\")
            If OverwriteFile Then
                For Each fil In .Items
                    If FSO.FileExists(DefPath & fil.Name) Then
                        Kill DefPath & fil.Name
                    End If
                Next
            End If
            oApp.NameSpace(CVar(DefPath)).CopyHere .Items
        End With

        On Error Resume Next
        Kill Environ("Temp") & "\Temporary Directory*"

        'Kill zip file
        Kill ZipFile
    End If

ExitProc:
    On Error Resume Next
    Set oApp = Nothing
    Exit Sub
ErrHandler:
    Select Case Err.Number
        Case Else
            MsgBox "Error " & Err.Number & ": " & Err.Description, vbCritical, "Unexpected error"
    End Select
    Resume ExitProc
    Resume
End Sub



Reference: Ron de Bruin

Tags: VBA, Access, Zip, Unzip, compact, compactar, Shell32, Shell32.Folder, Shell32.Applications, Namespace, Dll, Shell32.dll, API, 

VBA Tips - Compatibilidade entre as API 32-Bits (x86) e 64-Bits (x64) no VBA

Inline image 1
| Blog Office VBA | Blog Excel | Blog Access |

Utilizar APIs tem se tornado uma complicação para alguns e isso deve-se ao fato de ser necessário compatibilizar as versões do MS Office e seus respectivos VBEs (ambientes de desenvolvimento).

A versão MS Office 2010, introduziu a plataforma 64 bits e a nova versão 7.0 do VBA.

Isso requer que todos aqueles que desenvolvem no ambiente MS Office atentem-se para manter a compatibilidade dos seus códigos em todas as plataformas, seja 32 Bits ou 64 Bits ou mesmo em versões anteriores. Prá quem já desenvolve a muito tempo isso não é novidade nenhuma, mas para os neofitos essa dica é bem relevante.

Lembrem-se desses ínfimos detalhes:

A versão do MS Office 2010 é a 14.0
A versão do MS Office 2007 é o 12.0
A versão do MS Office 2003 (XP) é o 11.0

O VBA tem a sua própria versão contida nos diferentes pacotes:

A versão VBA do MS Office 2007 é a 6.0
A versão VBA do MS Office 2010 é a 7.0

Divertido não?

Não fiquem demasiadamente preocupados com isso. O VBA 6.0 oferece uma ótima compatibilidade com as edições anteriores. Mas o VBA 7.0, do MS Office 2010 não.

Em tempo, faz-se necessário comentar que o cenário pode ser um pouco mais complexo quando nos referimos as versões 64 bits do MS Office, as quais foram introduzidas a partir do MS Office 2010. Estas não estão totalmente compatíveis com as versões 32 Bits do MS Office 2010.

Resumindo para os menos atentos:

MS Office 2010 64 bits
- VBA 7.0 - 64 bits

MS Office 2010 32 bits
- VBA 7.0 - 32 bits

MS Office 2007
ou anterior - VBA 6.0 ou anterior - 32 bits

E agora, quem poderá nos proteger?

Condicionais de Compilação
Os Condicionais de Compilação são instruções compiladas somente se o critério atender a condição. Possuem em seu prefixo o caractere #.

#If VBA7
Then
    ' Compilado somente no VBA do Office 2010, tanto nas versão 32 Bits, quanto na versão 64 Bits
     
     #If Win64 Then
        ' Compilado somente no VBA do Office 2010, na versão 64 Bits
     
     #Else
        ' Compilado somente no VBA do Office 2010, na versão 32 Bits
     #End If

#Else
    ' Compilado somente no VBA do Office 2007 ou inferior

#End If

Obs: Apesar do termo compilado as Condicionais podem ser inseridas dentro dos Procedimentos e podem ser depuradas.

Você que é desenvolvedor VBA, foque bem a sua atenção:

As suas declarações API devem ser cautelosas, veja o exemplo prático abaixo:

#If VBA7 Then
   Declare PtrSafe Function GetActiveWindow Lib "user32" () As LongPtr

   #If Win64 Then
      Declare PtrSafe Function GetTickCount64 Lib "kernel32" () As LongLong

   #Else
      Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long

   #End If
#Else

   Declare Function GetActiveWindow Lib "user32" () As Long
   Declare Function GetTickCount Lib "kernel32" () As Long
#End If

Nesse exemplo, estamos usando as APIs GetActiveWindow e GetTickCount. Da forma como foram declaradas acima, são executadas em qualquer versão do MS Office. Perceba que deve efetuar as seguintes considerações caso a versão do VBA seja 7.0:

Veja detalhes aqui:

Adicionar o parâmetro PtrSafe entre Declare e Function (ou Sub)

Substituir Long por LongPtr

Observe que ao contrário da GetActiveWindow, a API GetTickCount deve ser declarada separadamente para as versões de 32 e 64 bits do Office 2010 (observe o sufixo 64 em GetTickCount64). Para APIs que estejam dentro do bloco Win64, a troca de variáveis tipo Long deve ser por LongLong, que é um novo tipo de inteiro de 64 bits que trabalha em ambiente 32 bits.

A parte difícil vem agora: nem todos os tipos Long devem ser convertidos. Numa explicação curta, podemos dizer que as APIs são definidas em linguagem C++, e nesta plataforma existem dois tipos numéricos não presentes no VBA: ponteiros e handles. Apenas ponteiros e handles devem ser transformados de Long para LongPtr. Como saber quais Long são handles e ponteiros? Teoricamente, o usuário deverá entrar na documentação da função no site da MSDN da Microsoft e ver quais são os parâmetros da função API desejada. Isso é um custo muito alto para o programador, e devido à reclamações da comunidade, a Microsoft lançou uma lista com as novas API para a plataforma 32 e 64 bits já corrigidas, que pode ser acessada através deste link.

Outro exemplo para o seu deleite:

Inline image 3

' A user-defined type to store the window dimensions.

Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type

' Test which version of VBA you are using.

#If VBA7 Then
   ' API function to locate a window.
   Declare PtrSafe Function FindWindow Lib "user32" _
      Alias "FindWindowA" ( _
      ByVal lpClassName As String, _
      ByVal lpWindowName As String) As LongPtr
    
   ' API function to retrieve a window's dimensions.
   Declare PtrSafe Function GetWindowRect Lib "user32" ( _
      ByVal hwnd As LongPtr, _
      lpRect As RECT) As Long

#Else
   ' API function to locate a window.
   Declare Function FindWindow Lib "user32" _
      Alias "FindWindowA" ( _
      ByVal lpClassName As String, _
      ByVal lpWindowName As String) As Long
    
   ' API function to retrieve a window's dimensions.
   Declare Function GetWindowRect Lib "user32" ( _
      ByVal hwnd As Long, _
      lpRect As RECT) As Long
#End If

Sub DisplayExcelWindowSize()
   Dim hwnd As Long, uRect As RECT
   
   ' Get the handle identifier of the main Excel window.
   hwnd = FindWindow("XLMAIN", Application.Caption)
   
   ' Get the window's dimensions into the RECT UDT.
   GetWindowRect hwnd, uRect
   
   ' Display the result.
   MsgBox "The Excel window has these dimensions:" & _
      vbCrLf & " Left: " & uRect.Left & _
      vbCrLf & " Right: " & uRect.Right & _
      vbCrLf & " Top: " & uRect.Top & _
      vbCrLf & " Bottom: " & uRect.Bottom & _
      vbCrLf & " Width: " & (uRect.Right - uRect.Left) & _
      vbCrLf & " Height: " & (uRect.Bottom - uRect.Top)
   
End Sub

Reference:


André Luiz Bernardes


Tags: VBA, Office, API, 32 Bits, 64 Bits, compatibilidade, VBA 6.0, VBA 7.0, MS Office 2003, MS Office 2007, MS Office 2010, MS Office 2010 32 Bits, MS Office 2010 64 Bits,  32-bit (x86), 64-bit (x64)

eBooks VBA na AMAZOM.com.br

LinkWithinBrazilVBAExcelSpecialist

Related Posts Plugin for WordPress, Blogger...

Vitrine