Este artigo explora como utilizar as capacidades de Reconhecimento Óptico de Caracteres (OCR) nativas do WeChat em conjunto com Python. A abordagem se baseia em engenharia reversa de um arquivo DLL (mmmojo.dll) que gerencia a comunicação com o executável do OCR do WeChat (WeChatOCR.exe). Ao carregar esta DLL, é possível iniciar e interagir com o serviço de OCR sem a necessidade de ter o WeChat aberto.
Detalhes de Implementação e Dicas
Gerenciamento de Callbacks com Ponteiros de Contexto
O sistema de OCR utiliza funções de callback para notificar o aplicativo chamador sobre o status e os resultados do reconhecimento. Uma função de callback notável é OCRRemoteOnConnect, cujo protótipo inclui um parâmetro void* user_data. Este parâmetro permite passar um ponteiro de contexto, que pode ser configurado através da função exportada SetMMMojoEnvironmentCallbacks.
Em implementações C++, este ponteiro user_data é frequentemente usado para passar o ponteiro this de uma instância de classe. Isso permite que o código de callback modifique o estado interno do objeto, como uma variável de conexão (m_connect_con_var), garantindo que operações subsequentes, como o início de uma tarefa de OCR (DoOCRTask), só ocorram após o estabelecimento bem-sucedido da conexão.
Em Python, essa técnica pode ser replicada utilizando o módulo ctypes. O objeto Python pode ser encapsulado em um ctypes.py_object para ser passado como um ponteiro para a camada C. Dentro da função de callback em Python, este ponteiro pode ser convertido de volta para o objeto Python original usando ctypes.cast e acessando o atributo .value.
# Converte a instância 'self' para um ponteiro C e o define como user_data
SetMMMojoEnvironmentCallbacks(self.m_mmmojo_env_ptr, 0, py_object(self))
# Exemplo de uso do user_data no callback
def OCRRemoteOnConnect(is_connected: c_bool, user_data: py_object):
print(f"Callback OCRRemoteOnConnect chamado. Conectado: {is_connected}")
if user_data:
# Recupera o objeto Python do ponteiro de contexto
manager_obj = cast(user_data, py_object).value
manager_obj.SetConnectState(True)
Armadilhas Comuns (Pitfalls)
Ao interagir com DLLs via ctypes, é crucial gerenciar corretamente o ciclo de vida dos objetos passados como argumentos. Por exemplo, ao passar um ponteiro de caractere largo (c_wchar_p) para funções da DLL, ele deve ser atribuído a uma variável intermediária. Se passado diretamente, o coletor de lixo do Python pode desalocar a memória antes que a DLL a utilize, levando a falhas silenciosas e terminações abruptas do programa.
# Abordagem incorreta (pode levar à coleta de lixo prematura)
SetMMMojoEnvironmentInitParams(self.m_mmmojo_env_ptr, 2, c_wchar_p(self.m_exe_path))
# Abordagem correta (a variável intermediária garante a persistência)
c_exe_path = c_wchar_p(self.m_exe_path)
SetMMMojoEnvironmentInitParams(self.m_mmmojo_env_ptr, 2, c_exe_path)
Para garantir que os objetos persistam durante toda a interação com a DLL, considere declará-los como variáveis globais ou atribuí-los a atributos da instância da classe (self).
Instruções de Uso
Instalação
O pacote está disponível no PyPI. Instale-o usando pip:
pip install wechat-ocr
Se estiver usando um mirror de pacotes que pode não ter a versão mais recente, tente especificar o índice oficial:
pip install wechat-ocr -i https://pypi.org/simple
Exemplo de Código
O exemplo a seguir demonstra como inicializar o serviço de OCR, definir um callback para receber os resultados e processar imagens.
import os
import json
import time
from wechat_ocr.ocr_manager import OcrManager, OCR_MAX_TASK_ID
# Caminho para o executável do OCR do WeChat
WECHAT_OCR_EXECUTABLE = r"C:\Users\Administrator\AppData\Roaming\Tencent\WeChat\XPlugin\Plugins\WeChatOCR\7057\extracted\WeChatOCR.exe"
# Caminho para a instalação do WeChat
WECHAT_INSTALL_DIR = r"D:\GreenSoftware\WeChat\3.9.6.32"
def ocr_completion_handler(image_path: str, recognition_results: dict):
"""Callback para processar os resultados do OCR."""
output_filename = os.path.basename(image_path) + ".json"
print(f"Reconhecimento concluído para: {image_path}. Salvando em: {output_filename}")
with open(output_filename, 'w', encoding='utf-8') as f:
json.dump(recognition_results, f, ensure_ascii=False, indent=2)
def main_ocr_process():
"""Função principal para orquestrar o processo de OCR."""
ocr_service = OcrManager(WECHAT_INSTALL_DIR)
# Configura o caminho do executável do OCR
ocr_service.SetExePath(WECHAT_OCR_EXECUTABLE)
# Configura o diretório da instalação do WeChat
ocr_service.SetUsrLibDir(WECHAT_INSTALL_DIR)
# Define a função de callback para resultados
ocr_service.SetOcrResultCallback(ocr_completion_handler)
# Inicia o serviço de OCR
ocr_service.StartWeChatOCR()
# Submete tarefas de OCR para diferentes imagens
ocr_service.DoOCRTask(r"T:\Code\WeChat\OCR\Python\img\image1.png")
ocr_service.DoOCRTask(r"T:\Code\WeChat\OCR\Python\img\image2.png")
ocr_service.DoOCRTask(r"T:\Code\WeChat\OCR\Python\img\image3.png")
# Aguarda a conclusão das tarefas
# Nota: Um loop de espera pode ser mais robusto dependendo do cenário
time.sleep(1)
while ocr_service.m_task_id.qsize() != OCR_MAX_TASK_ID:
time.sleep(0.1) # Pequena pausa para evitar uso excessivo de CPU
# Finaliza o serviço de OCR
ocr_service.KillWeChatOCR()
if __name__ == "__main__":
main_ocr_process()
Repositório de Código Fonte
O código-fonte completo está disponível no GitHub:
https://github.com/kanadeblisst00/wechat_ocr
Versão Aardio
Uma implementação semelhante também foi desenovlvida em Aardio: