Visual Basic, VB .NET, ASP, Active X, Access, SQL Server

Criando Propriedades de Formulários Com as APIs SetProp, GetProp e RemoveProp

Observação inicial: para ler este artigo você precisa saber o que é e como funciona a importante técnica de subclassificação de janela. Para um artigo que explica o assunto em detalhes, clique aqui.

Propriedades podem ser adicionadas a formulários mediante a criação de procedimentos de propriedades comuns como se faz em classes. Mas estas propriedades só podem ser usadas quando temos uma referência para o objeto Form ao qual elas pertencem. Nos casos em que precisamos subclassificar um formulário e chamar algum procedimento deste formulário dentro da subclassificação, não temos uma referência para o objeto Form, mas apenas nos é passado o hwnd do formulário. Como então obter uma referência ao formulário tendo apenas o seu hwnd? 

A API SetProp combinada com a função não documentada ObjPtr pode ser usada para armazenar, no prórprio formulário, o ponteiro para ele como uma propriedade. Esta propriedade é do tipo que pode ter seu valor recuperado com a API GetProp, que recebe como argumento o hwnd do formulário e o nome da propriedade. Desta forma, no evento Load do formulário, poderiamos usar SetProp para criar e armazenar o ponteiro para o Form como uma propriedade. Ex:

SetProp Me.Hwnd, "Ponteiro", ObjPtr(Me)

Dentro do procedimento de janela, no módulo de subclassificação, podemos obter uma referência para o form chamando GetProp para obter o ponteiro para o form e usando a função ObjectFromPtr, que nos retorna um objeto a partir do seu ponteiro. Assim:

Dim Ptr As Long, frm As Form

Ptr = GetProp(hwnd, "Ponteiro")

frm = ObjectFromPtr (Ptr)

' Código de Bruce McKinney para obter um objeto
' de um ponteiro (requer a declaração da API CopyMemory):

Public Function ObjectFromPtr(ByVal lPtr As Long) As Object
    Dim objT As Object

    CopyMemory objT, lPtr, 4
    Set ObjectFromPtr = objT
    CopyMemory objT, 0&, 4
End Function

No caso de querermos usar o mesmo procedimento de janela presente no módulo de subclassificação para subclassificar múltiplas janelas, poderemos também armazenar o endereço do procedimento de janela original (anterior à subclassificação) como uma propriedade do formulário. Ex:

Dim pOldWindowProc as Long

'esta API altera o procedimento que recebe as msgs para o form

pOldWindowProc = SetWindowLong(hWnd, GWL_WNDPROC, AddressOf NewWindowProc) 

SetProp Me.Hwnd, "pOldWindowProc", pOldWindowProc

Dentro do novo procedimento de janela, precisaremos remeter as mensagens não processadas para o procedimento original chamando CallWindowProc e passando como argumento o endereço do antigo procedimento da janela e os dados da mensagem. Chamamos GetProp para obtermos o valor da propriedade "pOldWindowProc" e o passamos para CallWindowProc com os dados da mensagem. Assim:

pOldWindwoProc = GetProp (hwnd, "pOldWindowProc")

CallWindowProc pOldWindowProc, hWnd, lMsg, wParam, lParam

Quando formos desfazer a subclassificação no evento Unload, chamamos GetProp para recuperar o endereço do procedimento de janela original e restaurá-lo. Assim:

Dim pOldWindowProc As Long

pOldWindowProc  = GetProp(hwnd, "pOldWindowProc")

SetWindowLong hWnd, GWL_WNDPROC, pOldWindowProc  ' restaura

' remove as propriedades criadas com SetProp no evento Load

RemoveProp hwnd, "pOldWindowProc"

RemoveProp hwnd, "Ponteiro"

Observações finais.

A técnica acima nos livra do problema de termos que armazenar em variáveis o endereço do antigo procedimento de janela para cada formulário que formos subclassificar. Também nos permite descobrir, dentro do procedimento de janela, a qual instância de um formulário se destina a mensagem recém chegada e podermos obter uma referência para esta instância e usá-la para modificar propriedades e chamar métodos do objeto.