Boas práticas de desempenho em macros para produção
Aprenda a otimizar macros VBA no CorelDRAW para ambientes de produção: desative eventos, use arrays, evite loops desnecessários e reduza o tempo de execução …
A macro roda perfeito no arquivo de teste com 3 páginas. No dia da entrega, o cliente manda o arquivo real: 142 páginas, 3.800 objetos por página, curvas complexas com efeitos de lente e transparência. O que levava 4 segundos agora trava o CorelDRAW por 47 minutos — ou derruba a aplicação sem mensagem de erro. Esse cenário não é exceção; é a regra quando o código é escrito só para “funcionar” sem considerar o ambiente de produção.
A diferença entre um script de automação e uma ferramenta de produção está na previsibilidade sob carga. O CorelDRAW expõe um modelo de objetos COM rico, mas cada chamada cruza a barreira entre VBA e o núcleo C++ do aplicativo. Em volume, o custo dessas travessias domina o tempo total. Boas práticas de desempenho não são otimização prematura; são engenharia de confiabilidade para que a automação escale junto com a demanda.
Este artigo reúne padrões validados em rotinas que processam milhares de páginas por noite em park de pré-impressão. Cada técnica tem contrapartida mensurável: menos chamadas COM, menos alocações de memória, menos sincronização com a interface. O objetivo é transformar macros frágeis em ativos de produção que o operador executa sem supervisão.
Desative a interface antes de processar
A primeira linha de uma macro de produção deve desligar tudo o que gera overhead visual e de eventos:
With Application
.ScreenUpdating = False
.EventsEnabled = False
.Optimization = True
End With
ScreenUpdating = False impede redesenhos de tela a cada mudança de propriedade. EventsEnabled = False silencia disparos de DocumentChange, SelectionChange, ShapeChange — cada um deles força o VBA a marshalar parâmetros e invocar handlers mesmo que vazios. Optimization = True diz ao motor de layout para postergar recálculos de paginação e quebra de texto.
Restaure no bloco Finally (via On Error GoTo Cleanup):
Cleanup:
On Error Resume Next
With Application
.ScreenUpdating = True
.EventsEnabled = True
.Optimization = False
End With
Exit Sub
Sem esse guard, um erro no meio do processamento deixa o CorelDRAW “congelado” (eventos desligados, tela não atualiza) até reinício. Em produção, isso vira chamado de suporte urgente.
Prefira loops indexados a For Each
For Each shp In Layer.Shapes parece limpo, mas cria um enumerador COM (IEnumVARIANT) a cada iteração. Em 50.000 shapes, são 50.000 alocações/liberações de enumerador + 50.000 chamadas Next. O loop indexado evita isso:
Dim i As Long, cnt As Long
cnt = ActiveLayer.Shapes.Count
For i = 1 To cnt
Dim shp As Shape
Set shp = ActiveLayer.Shapes(i)
' processa shp
Next i
Ganhos adicionais: você controla a direção (processar de trás para frente evita reindexação ao deletar), pula itens com Step 2, e evita o bug do enumerador que invalida se a coleção mudar durante o loop.
Para ShapeRange, a mesma lógica: sr.Item(i) em vez de For Each s In sr.
Leia e grave Excel em blocos, não célula a célula
A integração com Excel é o segundo maior gargalo (o primeiro é a própria UI do CorelDRAW). Cada Cells(r, c).Value cruza a barreira COM. A leitura em bloco traz tudo em um Variant(1 To nRows, 1 To nCols):
Dim dados As Variant
dados = ws.Range("A2:Z" & ultimaLinha).Value
' dados(1,1) = A2, dados(2,3) = C3, etc.
Escrita inversa:
ws.Range("A2").Resize(UBound(dados, 1), UBound(dados, 2)).Value = dados
Uma única chamada COM substitui milhares. Em testes com 10.000 linhas x 20 colunas, o tempo caiu de 84 segundos para 1,2 segundos. Regra prática: se o range tem mais de 50 células, use array.
Tipagem forte e variáveis locais em loops quentes
Dim x As Variant dentro de loop força o VBA a fazer coerção de tipo a cada iteração. Declare fora do loop com tipo concreto:
Dim i As Long, cnt As Long
Dim shp As Shape
Dim nome As String
Dim largura As Double
cnt = ActiveLayer.Shapes.Count
For i = 1 To cnt
Set shp = ActiveLayer.Shapes(i)
nome = shp.Name
largura = shp.SizeWidth
' ...
Next i
Long para contadores/índices (32-bit signed), Double para coordenadas/dimensões (o CorelDRAW usa double internamente), String para nomes. Evite Currency, Date, Object em caminhos críticos.
Variáveis de objeto (Set shp = ...) devem ser reutilizadas — não redeclare Dim shp As Shape dentro do loop.
Agrupe operações no modelo de undo e no ShapeRange
Cada alteração de propriedade cria uma entrada no histórico de undo. 5.000 shapes × 3 propriedades = 15.000 entradas — estoura o buffer de undo e torna o arquivo instável. Use BeginCommandGroup / EndCommandGroup:
Application.BeginCommandGroup "Ajuste produção"
For i = 1 To cnt
Set shp = ActiveLayer.Shapes(i)
shp.Fill.UniformColor.CMYKAssign 0, 100, 100, 0
shp.Outline.Width = 0.25
shp.Outline.Color.CMYKAssign 0, 0, 0, 100
Next i
Application.EndCommandGroup
Uma única entrada de undo para toda a operação. O usuário desfaz tudo com Ctrl+Z uma vez.
ShapeRange aplica a mesma propriedade a centenas de shapes em uma chamada COM:
Dim sr As ShapeRange
Set sr = ActiveLayer.CreateShapeRangeFromArray(Array(shp1, shp2, shp3))
sr.Fill.UniformColor.CMYKAssign 100, 0, 0, 0
Útil quando a mesma mudança atinge muitos objetos.
Libere referências COM explicitamente
VBA usa contagem de referências. Variáveis de objeto mantêm o shape vivo mesmo após o loop. Em macros longas, isso acumula centenas de wrappers RCW (Runtime Callable Wrapper) no lado VBA, pressionando o coletor de lixo e o pool de handles do CorelDRAW.
For i = 1 To cnt
Set shp = ActiveLayer.Shapes(i)
' processa
Set shp = Nothing ' libera já
Next i
No final de escopos maiores (funções, subs), Set obj = Nothing em todas as variáveis de objeto. Evite variáveis globais de objeto — elas nunca morrem até o CorelDRAW fechar.
Evite seleção programática; use referências diretas
ActiveSelection.Add shp / ActiveSelection.Remove shp / ActiveShape forçam o CorelDRAW a atualizar a UI de seleção, recalcular handles, disparar eventos. Em lote, é veneno.
Trabalhe direto na variável shp:
' Lento
ActiveSelection.Add shp
ActiveShape.Rotate 45
ActiveSelection.Remove shp
' Rápido
shp.Rotate 45
Se precisar operar em conjunto, construa ShapeRange a partir das referências já obtidas — sem tocar em ActiveSelection.
Strings: funções nativas VBA batem RegExp/Like
Parsing de nomes de camadas, nomes de arquivos, dados de CSV: InStr, InStrRev, Mid$, Left$, Right$, Replace$ (com $ retorna String, não Variant) são 10-50x mais rápidos que VBScript.RegExp ou operador Like.
' Extrair prefixo antes do primeiro "_"
Dim pos As Long
pos = InStr(nome, "_")
If pos > 0 Then prefixo = Left$(nome, pos - 1) Else prefixo = nome
Reserve RegExp para validação complexa (e-mail, CNPJ, padrões irregulares) fora de loops.
Medição caseira: Timer + média truncada
Sem profiler, use Timer (resolução ~1 ms no Windows). Execute o bloco 10 vezes, descarte o maior e o menor (aquecimento de cache, GC, agendador), faça média dos 8 restantes:
Dim t As Double, tempos(1 To 10) As Double
For run = 1 To 10
t = Timer
Call ProcessaLote
tempos(run) = Timer - t
Next run
' ordena tempos, descarta 1 e 10, média 2..9
Teste sempre com dataset real de produção: arquivo com 100+ páginas, 500+ objetos/página, fontes embarcadas, bitmaps linked, efeitos de lente. Arquivo de teste pequeno mascara O(n²) que só aparece em escala.
Checklist de homologação antes de ir para produção
Antes de entregar a macro para a equipe de pré-impressão, valide cada item:
- Executa 3 vezes seguidas sem reiniciar o CorelDRAW — memória estável?
- Processa o maior arquivo do último mês sem erro de “Automation error” ou travamento?
- Tempo de execução ≤ 20% do janela de manutenção noturna?
- Undo único funciona (Ctrl+Z volta tudo)?
- Log de erros gravado em arquivo (não MsgBox) com timestamp, shape.ID, página, descrição?
- Variáveis de objeto todas liberadas (
Set = Nothing) em todos os caminhos de saída? - Documentação inline: parâmetros esperados, side effects, versão, autor?
Publicação e versionamento
Uma macro de produção vive no Gerenciador de Extensões com versão semântica (MAJOR.MINOR.PATCH), changelog e instalador .ccx. Isso garante rollback imediato se uma atualização regressar desempenho. O time de produção nunca edita .gms direto no editor — sempre via pacote versionado.
Para fluxos que cruzam CorelDRAW e planilhas de especificação, o padrão de integração com Excel via automação VBA usando arrays em bloco é obrigatório. E quando a automação envolve separação de cores para serigrafia, o pipeline de separação automatizada já incorpora essas práticas de desempenho no core.
Desempenho em produção não é sobre truques; é sobre disciplina sistemática: menos chamadas COM, menos alocações, menos sincronização com UI, medição real com dados reais. Aplicados consistentemente, esses padrões transformam macros que “funcionam no meu arquivo” em ferramentas que rodam noite adentro processando milhares de páginas sem supervisão — o que, no fim, é a única métrica que importa.
Checklist prático
Perguntas frequentes
Por que desativar Application.ScreenUpdating e Events acelera a macro?
Cada alteração no documento dispara eventos e redesenhos de tela que consomem CPU. Desativar essas propriedades impede atualizações visuais desnecessárias durante o processamento em lote, devolvendo o controle ao final.
Qual a diferença real entre iterar Shapes com For Each e acessar por índice?
For Each cria um enumerador COM a cada iteração, adicionando overhead. Acessar via Layer.Shapes(i) em loop For tradicional evita essa alocação repetida e permite controle preciso da ordem de processamento.
Quando vale a pena ler ranges de células do Excel em array único?
Sempre que houver mais de 50 linhas. Uma única chamada Range.Value traz todos os dados para memória VBA (Variant 2D), eliminando centenas de chamadas COM individuais que são o gargalo real.
Como identificar vazamento de objetos COM em macros longas?
Monitore o uso de memória do processo CorelDRW.exe no Gerenciador de Tarefas durante execução. Crescimento contínuo indica objetos não liberados; use Set obj = Nothing e evite variáveis globais desnecessárias.
O que é mais rápido: duplicar shapes via Duplicate ou criar novos via CreateRectangle?
Duplicate mantém propriedades complexas (estilos, efeitos, dados anexados) com uma única chamada COM. Criar do zero exige múltiplas chamadas para replicar cada atributo, sendo mais lento para objetos ricos.
Como testar desempenho sem ferramentas profissionais de profiling?
Use Timer antes e depois do bloco crítico (resolução ~1ms). Execute 10 vezes, descarte o maior e o menor valor, faça a média dos 8 restantes. Compare versões antes/depois com o mesmo dataset de produção.