MattyD Posted Friday at 10:36 PM Posted Friday at 10:36 PM (edited) So hopefully there's a better way of doing this, but I was pretty happy when this actually worked! We've been looking at some MediaFoundation stuff recently - and in order to spin up the MFMediaEngine object you must provide an interface to handle callbacks. Basically you are meant to write your own handler with an IMFEventNotify interface, then pass it to the factory. But how do you do this in AutoIt? Well it seems you can dodgy something up! IMFEventNotify object inherits from IUnknown has 1 method of its own - EventNotify . write the 4 methods and throw them into memory with DllCallbackRegister. Grab all the function pointers and pop them into an array. You now have yourself a vtable. The pointer to a vtable is an interface ptr. And that is a COM object! expandcollapse popup#include <WinAPI.au3> Global Const $sIID_IUnknown = "{00000000-0000-0000-C000-000000000046}" Global Const $sIID_IMFMediaEngineNotify = "{fee7c112-e776-42b5-9bbf-0048524e2bd5}" Global Const $tag_IMFMediaEngineNotify = "EventNotify hresult(uint; ulong_ptr; uint);" ;The Obj will look like this in memory. ;Only 2 interfaces are supported, IUnknown, and one derived from IUnknown. - They both can coexist in the one place ;for a more complex object we might need to store the locations of multiple vtables, and return the correct one with QueryInterface. ; - pIface -> +- pVtab -> +- pQueryInterface ; | | ; +- iRefCnt +- pAddRef ; | ; +- pRelease ; | ; +- pEventNotify Local $tEventNotify_QI = DllCallbackRegister("IMFEventNotiy_QueryInterface", "long", "ptr;ptr;ptr") Local $tEventNotify_AR = DllCallbackRegister("IMFEventNotiy_AddRef", "long", "ptr") Local $tEventNotify_R = DllCallbackRegister("IMFEventNotiy_Release", "long", "ptr") Local $tEventNotify_EN = DllCallbackRegister("IMFEventNotiy_EventNotify", "long", "ptr;dword;dword_ptr;dword") Local $tIMFMediaEngineNotify_Vtab = DllStructCreate("ptr pQueryInterface;ptr pAddRef;ptr pRelease;ptr pEventNotify") $tIMFMediaEngineNotify_Vtab.pQueryInterface = DllCallbackGetPtr($tEventNotify_QI) $tIMFMediaEngineNotify_Vtab.pAddRef = DllCallbackGetPtr($tEventNotify_AR) $tIMFMediaEngineNotify_Vtab.pRelease = DllCallbackGetPtr($tEventNotify_R) $tIMFMediaEngineNotify_Vtab.pEventNotify = DllCallbackGetPtr($tEventNotify_EN) Local $tIMFMediaEngineNotify = DllStructCreate("ptr pVTab;int iRefCnt") $tIMFMediaEngineNotify.pVtab = DllStructGetPtr($tIMFMediaEngineNotify_Vtab) Func IMFEventNotiy_QueryInterface($pThis, $pIID, $ppObj) ; hResult = oThis.QueryInterface(In pIID, Out pObj*) Local $hResult = $S_OK If Not $ppObj Then Return $E_POINTER Local $tRetObj = DllStructCreate("ptr pObj", $ppObj) ;pObj is ByRef, so ppObj should always be provided. Switch _WinAPI_StringFromGUID($pIID) ;These interfaces are located where we currently are! ;So return ptr to self. Case $sIID_IMFMediaEngineNotify, $sIID_IUnknown $tRetObj.pObj = $pThis IMFEventNotiy_AddRef($pThis) Case Else $tRetObj.pObj = 0 $hResult = $E_NOINTERFACE EndSwitch Return $hResult EndFunc ;==>IMFEventNotiy_QueryInterface Func IMFEventNotiy_AddRef($pThis) ; iRefCnt = oThis.AddRef() Local $tThis = DllStructCreate("ptr VTab;int RefCnt", $pThis) $tThis.RefCnt += 1 Return $tThis.RefCnt EndFunc ;==>IMFEventNotiy_AddRef Func IMFEventNotiy_Release($pThis) ;iRefCnt = oThis.Release() Local $tThis = DllStructCreate("ptr VTab;int RefCnt", $pThis) $tThis.RefCnt -= 1 ;I guess we could probably do some cleanup once RefCnt = 0. ;Not sure how best to do that!. Return $tThis.RefCnt EndFunc ;==>IMFEventNotiy_Release ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Define Event handler. Func IMFEventNotiy_EventNotify($pThis, $iEvent, $iParam1, $iParam2) ConsoleWrite(StringFormat("EventRecieved! Event: %d, Param1: %d, Param2: %d", $iEvent, $iParam1, $iParam2) & @CRLF) EndFunc ;==>IMFEventNotiy_EventNotify ;Ready to go! Local $pIMFMediaEngineNotify = DllStructGetPtr($tIMFMediaEngineNotify) ; Interface ptr (to IMFMediaEngineNotify) IMFEventNotiy_AddRef($pIMFMediaEngineNotify) ;The oject exists in mem, so refCnt should start at 1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Local $oIMFMediaEngineNotify = ObjCreateInterface($pIMFMediaEngineNotify, $sIID_IMFMediaEngineNotify, $tag_IMFMediaEngineNotify) ConsoleWrite("IsObj($oIMFMediaEngineNotify) = " & IsObj($oIMFMediaEngineNotify) & @CRLF & @CRLF) ;Test some methods. - Get IUnknown Iface, then "release" it. ConsoleWrite("QueryInterface Test:" & @CRLF) Local $pIUnknown, $tGUID = _WinAPI_GUIDFromString($sIID_IUnknown) $oIMFMediaEngineNotify.QueryInterface(DllStructGetPtr($tGUID), $pIUnknown) ConsoleWrite(StringFormat("$pIUnknown = %s", Ptr($pIUnknown)) & @CRLF) Local $oIUnknown = ObjCreateInterface($pIUnknown, $sIID_IUnknown, "") ConsoleWrite("RefCnt = " & $oIUnknown.Release() & ", .Release()" & @CRLF & @CRLF) ;Test external Addref call. ConsoleWrite("AddRef Test:" & @CRLF) ConsoleWrite("RefCnt = " & $oIMFMediaEngineNotify.AddRef() & ", .AddRef()" & @CRLF) ConsoleWrite("RefCnt = " & $oIMFMediaEngineNotify.Release() & ", .Release()" & @CRLF & @CRLF) ;Send an event. ConsoleWrite("EventNotify Test:" & @CRLF) $oIMFMediaEngineNotify.EventNotify(1, 2, 3) Edited Saturday at 05:53 AM by MattyD Tweaked example Gianni and KaFu 2
Nine Posted Saturday at 07:54 PM Posted Saturday at 07:54 PM Very interesting. I was curious to do somewhat the inverse and it didn't work well. Maybe you have an explanation. #include <WinAPIDiag.au3> #include <WinAPIConv.au3> Global Const $MF_VERSION = 0x00020070 Global Enum $MFSTARTUP_FULL, $MFSTARTUP_LITE DllCall("Mfplat.dll", "long", "MFStartup", "long", $MF_VERSION, "long", $MFSTARTUP_FULL) Local $aCall = DllCall("Mf.dll", "long", "MFCreateMediaSession", "ptr", 0, "ptr*", 0) Local $pSession = $aCall[2] Local $tSession = DllStructCreate("ptr pVTab;int iRefCnt", $pSession) ConsoleWrite("Ref = " & $tSession.iRefCnt & "/" & Hex($tSession.iRefCnt) & @CRLF) Local $tSession_Vtab = DllStructCreate("ptr pFunc[10]", $tSession.pVtab) For $i = 1 To 10 ConsoleWrite($tSession_Vtab.pFunc(($i)) & @CRLF) Next DllCall("Mfplat.dll", "long", "MFShutdown") The list of the addresses in the vtable seems right. But the ref count is not, it looks like another address of something. “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
MattyD Posted Saturday at 10:31 PM Author Posted Saturday at 10:31 PM (edited) yeah it will be different - I've just made up where to put the reference count. By my understanding the object's resources sit somewhere under the vtable pointers, but I didn't bother trying to unpick what is actually in there. I guess it would make sense for this to be another table of pointers to different properties or something. Edit: just for fun - I found the refcount for MediaSession via a memory dump! Local $oSession = ObjCreateInterface($pSession, $sIID_IUnknown, "") $iOffset = (@AutoItX64) ? 75 : 77 $iPtrSz = (@AutoItX64) ? 8 : 4 $tRefCnt = DllStructCreate("int Count", Ptr($pSession + $iPtrSz * $iOffset)) $oSession.AddRef() ConsoleWrite($tRefCnt.Count & @CRLF) $oSession.AddRef() ConsoleWrite($tRefCnt.Count & @CRLF) Edited Sunday at 01:21 AM by MattyD
Nine Posted Sunday at 02:27 AM Posted Sunday at 02:27 AM (edited) Ok, if I understand you correctly, each object has its own way to store its data. Only thing that relates them all is the vtable which is the first ptr of the structure. Rest of the structure is just a bunch of data/ptr for managing its methods and properties. Once you establish the vtable, you can pretty much do everything you want with the rest. So the structure you get from a pointer of one object can be totally different from another object as long as the vtable is the first part of the structure. Edited Sunday at 02:33 AM by Nine “They did not know it was impossible, so they did it” ― Mark Twain Spoiler Block all input without UAC Save/Retrieve Images to/from Text Monitor Management (VCP commands) Tool to search in text (au3) files Date Range Picker Virtual Desktop Manager Sudoku Game 2020 Overlapped Named Pipe IPC HotString 2.0 - Hot keys with string x64 Bitwise Operations Multi-keyboards HotKeySet Recursive Array Display Fast and simple WCD IPC Multiple Folders Selector Printer Manager GIF Animation (cached) Debug Messages Monitor UDF Screen Scraping Round Corner GUI UDF Multi-Threading Made Easy Interface Object based on Tag
MattyD Posted Sunday at 02:05 PM Author Posted Sunday at 02:05 PM yeah more or less, everything external to the object should be interacting with it via the funcs in its vtable. Don't get me wrong, I'm sure there's some "proper" way to organize things - but I'm not too concerned about the internals. We just need to write and expose methods well enough so the object behaves appropriately.
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now