1

Тема: Microsoft Outlook View Control

В целях экономии места привожу ссылку на обсуждение проблемы
http://forum.script-coding.com/viewtopic.php?id=7144

и код, работающий на Win7 + 2010 office

<html>
    <head>
        <script language="vbscript">
            //{Public Const==============================================
            Public Const HEAP_ZERO_MEMORY             = &h00000008
            Public Const PAGE_EXECUTE_READWRITE       = &h40
            Public Const S_OK                         = &h0    
            Public Const E_NOINTERFACE                = &h80004002
            
            Public Const IID_IUnknown                 = "{00000000-0000-0000-C000-000000000046}"
            Public Const IID_IHTMLDocument2           = "{332C4425-26CB-11D0-B483-00C04FD90119}"
            Public Const IID_IOleObject               = "{00000112-0000-0000-C000-000000000046}"
            
            Public Const CLSID_Outlook                = "{261B8CA9-3BAF-4BD0-B0C2-BF04286785C6}"
            Public Const IID_IClassFactory            = "{00000001-0000-0000-C000-000000000046}"
            Public Const IID_IViewCtl                 = "{00067274-0000-0000-C000-000000000046}"
            Public Const IID_IDispatch                = "{00020400-0000-0000-C000-000000000046}"
            
            Public Const HKEY_CLASSES_ROOT             = &h80000000
            //}==========================================================
            
            //{Public Vars==============================================    
            Public Wrap
            Set Wrap = CreateObject("DynamicWrapperX")
            
            Public oServ
            
            Public buf
            Public swIID
            Public pCLSID
            Public pIID
            Public ppv
            Public ppOle
            Public res
            
            Public pIClassFactory
            Public pOleContainer
            Public pIOleObject
            
            Public Addr1
            Public oldAddr1
            Public Addr2
            Public oldAddr2
            Public Addr3
            //}==========================================================
            
            //{Class Service==============================================    
            Class Service
                Private hHeap
                Private Ref1
                Private Ref2    
                
                Private dbuf
                Private pIID_Disp
                Private AddrCoGet
                
                //{Class_Initialize
                Private Sub Class_Initialize
                    Wrap.Register "Kernel32",    "VirtualProtect" , "i=lllp", "r=l"
                    Wrap.Register "Kernel32",    "GetProcessHeap", "r=l"
                    Wrap.Register "Kernel32",    "HeapAlloc",    "i=lll",    "r=l"
                    Wrap.Register "Kernel32",    "HeapFree", "i=lll","r=l"
                    Wrap.Register "Advapi32",    "RegOpenKey", "i=hsp","r=l"
                    Wrap.Register "Advapi32",    "RegQueryValue", "i=hlpl","r=l"
                    Wrap.Register "Ole32"   ,     "StringFromGUID2", "i=ppl", "r=l"
                    Wrap.Register "Ole32"   ,     "IIDFromString", "i=pp", "r=l"
                    
                    Wrap.Register "Ole32"   ,     "CoMarshalInterThreadInterfaceInStream", "i=ppp", "r=l"
                    AddrCoGet = Wrap.Register("Ole32"   ,     "CoGetInterfaceAndReleaseStream", "i=ppp", "r=l")
                    AddrCoGet = Wrap.NumGet(AddrCoGet, &h14)    //адрес ф-ии CoGetInterfaceAndReleaseStream
            
                    hHeap     = Wrap.GetProcessHeap()
                    res        = Wrap.HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 4)
                    swIID    = Wrap.HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 256)
                    pIID    = Wrap.HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 16)
                    pCLSID    = Wrap.HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 16)
                    ppv        = Wrap.HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 4)
                    ppOle    = Wrap.HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 4)
                    
                    dbuf    = Wrap.HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 100)
                    Wrap.VirtualProtect dbuf, 100, PAGE_EXECUTE_READWRITE, res
                    
                    pIID_Disp    = Wrap.HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 16)
                    
                    //'Буфер под вызовы
                    buf        = Wrap.HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 5)
                    Wrap.NumPut    &hE9, buf, 0, "b"                    //'jmp ...Addr
            
                    //'IUnknown
                    Wrap.RegisterAddr buf, "IUnknown_QueryInterface", "i=ppp", "r=l"        //'IUnknown::QueryInterface
                    Wrap.RegisterAddr buf, "IUnknown_AddRef", "i=p", "r=l"                    //'IUnknown::AddRef
                    Wrap.RegisterAddr buf, "IUnknown_Release", "i=p", "r=l"                    //'IUnknown::Release
            
                    //'IOleClientSite
                    Wrap.RegisterAddr buf, "IOleClientSite_GetContainer", "i=pp", "r=l"        //'IOleClientSite::GetContainer
            
                    //'IClassFactory
                    Wrap.RegisterAddr buf, "IClassFactory_CreateInstance", "i=pppp", "r=l"    //'IClassFactory::CreateInstance
            
                    //'IOleObject
                    Wrap.RegisterAddr buf, "IOleObject_SetClientSite", "i=pp", "r=l"        //'IOleObject::SetClientSite
            
                    Set Ref1     = GetRef("IUnknown_QueryInterface")
                    Addr1        = Wrap.RegisterCallback(Ref1, "i=ppp", "r=l")
                    Wrap.VirtualProtect Addr1 - 6, 21, PAGE_EXECUTE_READWRITE, res        
                    
                    Set Ref2     = GetRef("IOleObject_SetClientSite")
                    Addr2        = Wrap.RegisterCallback(Ref2, "i=pp", "r=l")
                    Wrap.VirtualProtect Addr2 - 6, 21, PAGE_EXECUTE_READWRITE, res    
                    
                    //    Адреса параметров у вспомогательных ф-й DWX                    
                    params1    = Wrap.NumGet(Addr1, 4)
                    params2    = Wrap.NumGet(Addr2, 4)
                    
                    // Адреса IDispatch объектов VBS, через которые вызываются ф-и скрипта 
                    pRef1    = Wrap.NumGet(params1, 0)
                    pRef2    = Wrap.NumGet(params2, 0)
                    
                    Wrap.IIDFromString IID_IDispatch, pIID_Disp
                    
                    // Заворачиваем для передачи в другую нить
                    Wrap.CoMarshalInterThreadInterfaceInStream pIID_Disp, pRef1, ppv
                    pStm1 = Wrap.NumGet(ppv)
                    
                    Wrap.CoMarshalInterThreadInterfaceInStream pIID_Disp, pRef2, ppv
                    pStm2 = Wrap.NumGet(ppv)
                    
                    //    main - я так назвал ф-ю из DWX, через которую проходят Callback-вызовы 
                    Offset         = Wrap.NumGet(Addr2, 8 + 1)        // относительный адрес ф-и 'main'
                    AddrMain     = Addr2 + 8 + 5 + Offset        // абсолютный    адрес ф-и 'main'
                    
                    
                    //========================================================
                    // Этот код будет вызван вместо IOleObject::SetClientSite
                    // Сначала получим контейнер IOleClientSite::GetContainer.
                    // Далее передадим управление CallBack-функции из скрипта.
                    // Почему в скрипте не вызвать IOleClientSite::GetContainer? - 
                    // Через скрипт мы контейнер не получим,
                    // т.к. в IOleClientSite::GetContainer сравниваются id нитей
                    // и если они не равны, возвращается E_UNEXPECTED
                    
                    Wrap.NumPut &h0824448b,     dbuf, 0                        // mov eax, [esp+08]    pClientSite
                    Wrap.NumPut &h68,             dbuf, 4, "b"                // push ppOle            сюда должен прилететь IOleContainer
                    Wrap.NumPut ppOle,             dbuf, 5
                    Wrap.NumPut &h50,             dbuf, 9, "b"                // push eax            указатель на объект IOleClientSite
                    Wrap.NumPut &h008b,         dbuf, 10, "n"                // mov eax, [eax]        vtable IOleClientSite
                    Wrap.NumPut &h0408b,         dbuf, 12, "n"                // mov eax, [eax+14]    адрес IOleClientSite::GetContainer
                    Wrap.NumPut &h14,             dbuf, 14, "b"
                    Wrap.NumPut &hD0FF,         dbuf, 15, "n"                // call [eax]            IOleClientSite::GetContainer
                    Wrap.NumPut &hE9,             dbuf, 17, "b"                // jmp Addr2              адрес вспом. ф-и, для вызова IOleObject::SetClientSite
                    Wrap.NumPut (Addr2 - (dbuf + 17 + 5)),     dbuf, 18
                    
                    Addr3 = dbuf
                    //========================================================
                    
                    // Перед вызовом вспомогательной ф-и DWX, которая в свою очередь вызывает 
                    // ф-и VBS, вставим код, который развернет объекты VBS в другой нити.
                    // Подготавливаем код для перехвата (программируем в кодах)
                    // этот участок кода будет вызван 1 раз в другой нити,
                    // в нем разворачиваются дисп-интерфейсы VBS и заменяются в Callback-функциях.
                    //========================================================
                    preCode 22,         pStm1, params1
                    preCode 22 + 30,     pStm2, params2
                    
                    //    вот мы подменяем вызов на участок нового кода 
                    Wrap.NumPut (dbuf + 22) - (Addr2 + 8 + 5), Addr2, 8 + 1 
                    
                    // возвращаем управление ф-и main
                    // при этом, объекты VBS уже изменены
                    Wrap.NumPut &hE9,     dbuf, 82, "b"                    // jmp main
                    Wrap.NumPut (AddrMain - (dbuf + 82 + 5)),     dbuf, 83
                    //========================================================
                    
                    pOleContainer = 0
                End Sub
                //}                
            
                //Prepare code
                Private Function preCode(offset, pStm, params)
                    // это вызов CoGetInterfaceAndReleaseStream
                    // результат помещаем в params
                    Wrap.NumPut &h68,         dbuf, offset, "b"                                //    push ppv
                    Wrap.NumPut ppv,         dbuf, offset + 1
                    Wrap.NumPut &h68,         dbuf, offset + 5, "b"                            //    push IID_Dispatch
                    Wrap.NumPut pIID_Disp,    dbuf, offset + 6
                    Wrap.NumPut &h68,         dbuf, offset + 10, "b"                            //    push pStm
                    Wrap.NumPut pStm,         dbuf, offset + 11                                
                    Wrap.NumPut &hE8,         dbuf, offset + 15, "b"                            //    call CoGetInterfaceAndReleaseStream
                    Wrap.NumPut (AddrCoGet - (dbuf + offset + 20)),     dbuf, offset + 16
                    Wrap.NumPut &hA1,         dbuf, offset + 20, "b"                            // mov eax, [ppv]
                    Wrap.NumPut ppv,         dbuf, offset + 21
                    Wrap.NumPut &hA3,         dbuf, offset + 25, "b"                            // mov [params], eax
                    Wrap.NumPut params,     dbuf, offset + 26            
                End Function     
                
                //{Class_Terminate
                Private Sub Class_Terminate
                    Wrap.HeapFree hHeap, 0, res
                    Wrap.HeapFree hHeap, 0, swIID
                    Wrap.HeapFree hHeap, 0, pCLSID
                    Wrap.HeapFree hHeap, 0, pIID
                    Wrap.HeapFree hHeap, 0, ppv
                    Wrap.HeapFree hHeap, 0, ppOle
                    Wrap.HeapFree hHeap, 0, buf
                    Wrap.HeapFree hHeap, 0, dbuf
                    Wrap.HeapFree hHeap, 0, pIID_Disp
                    
                    Set Ref1 = Nothing
                    Set Ref2 = Nothing
                End Sub
                //}
            End Class    
            
            Set oServ = New Service
            //}==========================================================
            
            //vfunc  .........Универсальная, для вызова функций интерфейса..............
            Public Function vfunc(pObj, offset)    
                Addr = Wrap.NumGet(Wrap.NumGet(pObj), offset)    
                Wrap.NumPut    Addr - (buf + 1 + 4),     buf, 1
            End Function
        
            //IUnknown_QueryInterface    
            Public Function IUnknown_QueryInterface(pObj, riid, ppv1)
                Wrap.StringFromGUID2 riid, swIID, 256
                sIID = Wrap.StrGet(swIID)    
            
                If sIID = IID_IHTMLDocument2 Then
                    IUnknown_QueryInterface = E_NOINTERFACE
                    Wrap.NumPut 0, ppv1
                Else
                    Wrap.NumPut oldAddr1 - (buf + 1 + 4), buf, 1
                    IUnknown_QueryInterface = Wrap.IUnknown_QueryInterface(pObj, riid, ppv1) 
                End If
            End Function
            
            //IOleObject_SetClientSite
            Public Function IOleObject_SetClientSite(pObj, pClientSite)
                Wrap.NumPut oldAddr2 - (buf + 1 + 4), buf, 1
                IOleObject_SetClientSite = Wrap.IOleObject_SetClientSite(pObj, pClientSite) 
                
                //'=====================================================
                //'Перехват IOleContainer::QueryInterface
                pOleContainer     = Wrap.NumGet(ppOle)
                vtable            = Wrap.NumGet(pOleContainer)
                oldAddr1        = Wrap.NumGet(vtable, (1 - 1) * 4)    
                Wrap.VirtualProtect vtable, 4, PAGE_EXECUTE_READWRITE, res
                Wrap.NumPut    Addr1,     vtable, 0
            End Function
            //
            
            //Hook
            Public Function Hook()
                sKey     = "CLSID\" & CLSID_Outlook & "\InprocServer32"
                r         = Wrap.RegOpenKey(HKEY_CLASSES_ROOT, sKey, ppv)    
                hk         = Wrap.NumGet(ppv)
            
                Wrap.NumPut 256, ppv
                r          = Wrap.RegQueryValue(hk, 0, swIID, ppv)
                fullPath =  Wrap.StrGet(swIID, "s")
            
                //MsgBox fullPath
            
                r = Wrap.Register(fullPath,     "DllGetClassObject" , "i=ppp", "r=l")
            
                //'=====================================================
                //'Создаем фабрику-классов
                Wrap.NumPut 0, ppv    
                Wrap.IIDFromString CLSID_Outlook, pCLSID
                Wrap.IIDFromString IID_IClassFactory, pIID
                q = Wrap.DllGetClassObject(pCLSID, pIID, ppv)
            
                //'=====================================================
                //'Создаем объект IViewCtl
                pIClassFactory = Wrap.NumGet(ppv)
                Wrap.NumPut 0, ppv    
                Wrap.IIDFromString IID_IViewCtl, pIID
                vfunc pIClassFactory, &h0C    
                q = Wrap.IClassFactory_CreateInstance(pIClassFactory, 0, pIID, ppv)
            
                //'=====================================================
                //'Запрашиваем интерфейс IOleObject    
                pIViewCtl = Wrap.NumGet(ppv)
                Wrap.NumPut 0, ppv    
                Wrap.IIDFromString IID_IOleObject, pIID
                vfunc pIViewCtl, (1 - 1 ) * 4
                q = Wrap.IUnknown_QueryInterface(pIViewCtl, pIID, ppv)
            
                //'=====================================================
                //'Перехватываем IOleObject::SetClientSite
                pIOleObject = Wrap.NumGet(ppv)
                vtable        = Wrap.NumGet(pIOleObject)
                oldAddr2    = Wrap.NumGet(vtable, (4 - 1) * 4)    
                Wrap.VirtualProtect vtable + &h0C, 4, PAGE_EXECUTE_READWRITE, res
                Wrap.NumPut    Addr3,     vtable, &h0C
            End Function

            //DelHook
            Public Function DelHook()
                //'Убираем перехват на IOleObject::SetClientSite
                vtable        = Wrap.NumGet(pIOleObject)
                Wrap.NumPut    oldAddr2,     vtable, &h0C
                Wrap.VirtualProtect vtable + &h0C, 4, Wrap.NumGet(res), res
                
                //'Снимаем перехват    
                If pOleContainer <> 0 Then
                    vtable        = Wrap.NumGet(pOleContainer)
                    Wrap.NumPut    oldAddr1,     vtable, 0
                    Wrap.VirtualProtect vtable, 4, Wrap.NumGet(res), res
                End If
            
                vfunc pIClassFactory, (3 - 1) * 4
                q = Wrap.IUnknown_Release(pIClassFactory)
            
                Set oServ     = Nothing
                Set Wrap     = Nothing
            End Function
            
            Hook()
            
            Function Window_Onresize()    
                Set Obj = document.getElementById("Outlook")

                Obj.width     = document.body.clientWidth  - 20
                Obj.height     = document.body.clientHeight - 20
            End Function    
                
            Function Window_Onload()
                //MsgBox "Window_Onload"
                Window_Onresize()    
            End Function
                
            Function Window_OnUnload()
                //MsgBox "Window_OnUnload"
                DelHook()
            End Function
        </script>
    </head>
<body>
    <object id=Outlook classid=clsid:261B8CA9-3BAF-4BD0-B0C2-BF04286785C6></object>

    <script FOR=Outlook EVENT=SelectionChange language=vbscript>
        Set Selection = Outlook.Selection
        If Selection.Count() >0 Then
            Set mes   = Selection.Item(1)
            MsgBox mes.EntryID
        End If
    </script>

</body>
</html>

2 (изменено: chessman, 2012-07-09 17:23:14)

Re: Microsoft Outlook View Control

Кратко остановлюсь на "техниках", которые я использовал:
1. Еще перед созданием активикса в самом браузере, создаем объект фабрики-классов в контексте процесса с помощью DllGetClassObject.
2. Создание через фабрику самого активИкса: IClassFactory::CreateInstance.
3. Получение интерфейса IOleObject у (2).
4. Установка перехвата на метод IOleObject::SetClientSite в скриптовую ф-ю.
5. В связи с тем, что активИкс создается в другом процессе, пришлось заворачивать/разворачивать интерфейс VBS'а для работы перехвата (маршалинг).
6. Так же пришлось перехватывать промежуточную ф-ю DynamicWrapperX (кодовая вставка).