COM dynamic proxy


Author: Dave
Date: 10.07.25 - 7:13pm



SO i had a little fun with chatgpt this week and I was able to coax out something I had wanted for a lonnng time.

This project is a dynamic COM object proxy which wraps an arbitrary COM object and lets you intercept methods and add arbitrary ones on any COM object.

Download: dynproxy.zip

Below is the read me:
# 🧩 dynproxy β€” Dynamic COM Proxy for VB6 (and beyond)

> *β€œThe missing dynamic layer VB6 never had.”*  
> A raw C++ COM proxy that turns VB6 into a dynamic runtime.

---

## πŸš€ What This Does

`dynproxy.dll` lets **VB6 (or any COM client)** create objects that 
respond to *any* property or method call dynamically β€” even ones that 
don’t exist.

It acts like a **programmable middle-man** between VB6 and COM:

- Intercepts every `IDispatch::Invoke` and `GetIDsOfNames`.
- Can forward to a real inner COM object or fake a response.
- Lets your **VB6 class** decide in real time what happens.

**No ATL. No MFC. Pure COM.**

---

## 🧠 Why It Exists

No good reason..just a wish list

`dynproxy` fixes that: it lets you build **dynamic COM faΓ§ades** that VB6 treats as real.

Now you can:
- Build **synthetic COM trees** (`o.Kitty.Meow = 12`).
- **Mock** sprawling APIs (Acrobat, Office, etc.).
- **Log or reroute** calls before they hit the real object.
- **Bridge** VB6 to scripting engines or remote APIs.
- Turn VB6 into something approaching Python or JavaScript’s `Proxy`.

---

## βš™οΈ How It Works

### 🧭 The Proxy Pipeline

```text
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚   VB6 Runtime            β”‚
 β”‚   (calls o.SomeMethod)   β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                β”‚  IDispatch::Invoke("SomeMethod")
                β–Ό
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚  ProxyDispatch     β”‚
       β”‚  (dynproxy.dll)    β”‚
       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
          β”‚
          β”œβ”€β”€β–Ά 1️⃣ Inner object?  β†’ Forward call
          β”‚
          β”œβ”€β”€β–Ά 2️⃣ Resolver class?
          β”‚        β€’ Call VB6: ResolveGetID / ResolveInvoke
          β”‚        β€’ Pass name, args, and DISPATCH_ flags
          β”‚
          └──▢ 3️⃣ Neither? Invent a DISPID and let resolver fake it
````

### πŸ” Call Flow Details

1. **VB6** late-bound calls always go through `IDispatch::Invoke`.
2. The proxy catches that and checks a cache (`name β†’ DISPID`).
3. If unknown, it calls:

   * `inner->GetIDsOfNames()` first (default), or
   * `resolver->ResolveGetID()` first if resolver-wins mode is active.
4. The final `Invoke` routes accordingly:

   * Forward to the inner COM object, **or**
   * Call your resolver’s `ResolveInvoke(name, flags, args())`.

---

## 🧩 VB6 Resolver Interface

Your resolver implements two public methods:

```vb
Public Function ResolveGetID(ByVal name As String) As Long
    ' Return non-zero to claim this name
    ' Return 0 to let the inner object handle it
End Function

Public Function ResolveInvoke(ByVal name As String, _
                              ByVal flags As Long, _
                              args() As Variant) As Variant
    ' Handle the call
End Function
```

### `flags` meanings

| Flag | Hex | Meaning                        |
| ---- | --- | ------------------------------ |
| 1    | 0x1 | DISPATCH_METHOD (Sub/Function) |
| 2    | 0x2 | DISPATCH_PROPERTYGET           |
| 4    | 0x4 | DISPATCH_PROPERTYPUT           |
| 8    | 0x8 | DISPATCH_PROPERTYPUTREF        |

---

## 🐱 Example: Dynamic Tree

```vb
' --- CResolver.cls ---
Option Explicit
Private m_children As Object, m_props As Object

Private Sub Class_Initialize()
    Set m_children = CreateObject("Scripting.Dictionary")
    Set m_props = CreateObject("Scripting.Dictionary")
End Sub

Public Function ResolveGetID(ByVal name As String) As Long
    Select Case LCase$(name)
        Case "kitty", "meow": ResolveGetID = -30000
        Case Else: ResolveGetID = 0
    End Select
End Function

Public Function ResolveInvoke(ByVal name As String, ByVal flags As Long, args() As Variant) As Variant
    Dim lname As String: lname = LCase$(name)

    ' Property GET
    If (flags And 2) <> 0 Then
        If lname = "kitty" Then
            If Not m_children.Exists("kitty") Then
                Dim child As CResolver: Set child = New CResolver
                Dim p As Long: p = CreateProxyForObjectRaw(0&, ObjPtr(child))
                Dim o As Object: Set o = ObjectFromPtr(p)
                m_children.Add "kitty", o
            End If
            Set ResolveInvoke = m_children("kitty"): Exit Function
        End If
        If m_props.Exists(lname) Then ResolveInvoke = m_props(lname)
        Exit Function
    End If

    ' Property PUT
    If (flags And 4) <> 0 Then
        m_props(lname) = args(0): Exit Function
    End If
End Function
```

Demo:

```vb
Dim root As New CResolver
Dim p As Long: p = CreateProxyForObjectRaw(0&, ObjPtr(root))
Dim o As Object: Set o = ObjectFromPtr(p)

o.kitty.meow = 12
Debug.Print o.kitty.meow   ' β†’ 12
```

---

## 🧩 Key Exports

| Function                                                   | Description                                            |
| ---------------------------------------------------------- | ------------------------------------------------------ |
| `CreateProxyForObjectRaw(inner, resolver)`                 | Create proxy with an optional inner and resolver.      |
| `CreateProxyForObjectRawEx(inner, resolver, resolverWins)` | Same, but choose resolver-first at creation.           |
| `SetProxyResolverWins(proxy, enable)`                      | Toggle resolver-first mode at runtime.                 |
| `ClearProxyNameCache(proxy)`                               | Clear cached DISPIDs (call after toggling).            |
| `SetProxyOverride(proxy, name, dispid)`                    | Force a name to route to resolver.                     |
| `ReleaseDispatchRaw(ptr)`                                  | Manual release if you never wrapped the pointer in VB. |

All exports are `stdcall`, callable from VB6 directly.

---

## 🧰 Build Notes

* **Language:** C++17
* **No** ATL or MFC
* **Link:** `oleaut32.lib`
* Build as **Win32 DLL**

```
cl /LD /std:c++17 dynproxy.cpp oleaut32.lib /EHsc /Fe:dynproxy.dll
```

---

## 🧩 Project Layout

```
dynproxy/
 β”œβ”€β”€ dynproxy.cpp      # core proxy
 β”œβ”€β”€ dynproxy.h        # exports & ProxyDispatch class
 β”œβ”€β”€ msgf.cpp/.h       # debug output
 β”œβ”€β”€ VB6/
 β”‚   β”œβ”€β”€ CResolver.cls
 β”‚   β”œβ”€β”€ modProxyDecls.bas
 β”‚   β”œβ”€β”€ modDemo.bas
 β”‚   └── README_demo.txt
 └── README.md
```

---

## ⚑ Why It’s Cool

* Intercepts and rewrites COM calls in real time.
* Lets VB6 behave like Python or JavaScript β€” dynamic and late-bound.
* Mocks any proprietary API instantly (no IDL hell).
* Enables powerful debugging, scripting, and adapter layers.

---

## ⚠️ Caveats

* Works only for **late-bound** (`IDispatch`) calls.
* STA threading only (standard VB6 COM).
* Don’t double-release VB6-wrapped objects.
* VB6 only runs 32-bit, so the DLL should be x86.

---

## πŸ§™β€β™‚οΈ Credits & Origin

Implemented in pure C++ because ATL/MFC got in the way.

This project turns that pain into power β€” a *universal COM proxy* that lets old tech do new tricks.

---

**Platform:** Win32 COM
**Language:** C++ / VB6
**Keywords:** VB6, COM, IDispatch, dynamic proxy, API mocking, automation, Acrobat






Comments: (0)

 
Leave Comment:
Name:
Email: (not shown)
Message: (Required)
Math Question: 94 + 66 = ? followed by the letter: O 



About Me
More Blogs
Main Site
Posts: (All)
2026 ( 1 )
2025 (6)
     js4vb vtComObj
     Vb6 CallByNameEx
     js4vb
     COM dynamic proxy
     go get lost
     Courier Regular Win11 Missing
2024 (3)
     VB6 JSON
     ffmpeg voodoo
     RegJump Vb
2023 ( 9 )
2022 ( 4 )
2021 ( 2 )
2020 ( 4 )
2019 ( 5 )
2018 ( 6 )
2017 ( 6 )
2016 ( 22 )
2015 ( 15 )
2014 ( 25 )
2013 ( 4 )
2012 ( 10 )
2011 ( 7 )
2010 ( 11 )
2009 ( 3 )