jugador Posted January 20, 2021 Share Posted January 20, 2021 search Autoit Forum:- solvedcallback-function-and-objet by @trancexx Or ObjectFromTag by @trancexx in Ribbon thread search github for RTD(RealTimeData server) Client:-https://github.com/crosbymichael/thinkorswim/tree/master/ThinkOrSwimhttps://github.com/neberej/tos-client/tree/master/Adapterhttps://github.com/mdvx/kafka-rtd/blob/master/kafka-rtd/ExcelComInterfaces.cshttps://github.com/osullivj/kkaddin/blob/master/RTDServer.cs https://gist.github.com/nochristrequired/c7ee0fe6780fac19faa9344f50f2a4f7#file-gistfile1-txt RTD Client in C# Google searchhttps://docs.microsoft.com/en-us/office/vba/api/excel.irtdserverhttps://docs.microsoft.com/en-us/dotnet/api/microsoft.office.interop.excel.irtdserver?view=excel-pia https://www.codeproject.com/articles/245265/guide-to-writing-custom-functions-in-excel-part Excel RTD Servers: Minimal C++ Implementation Excel RTD Servers: Multiple Topics in C# C# without the Excel Assembly Reference but still no clue All this DllCallbackRegister & DllStructCreate bit complicated for me. expandcollapse popup#include <WinAPI.au3> #include <array.au3> Const $sCLSID_App = "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" Const $sIID_IScripRTD = "{EC0E6191-DB51-11D3-8F3E-00C04F3651B8}" Const $sIID_IRTDEvent = "{A43788C1-D91B-11D3-8F39-00C04F3651B8}" ;;-------- Const $tagIScripRTD_1 = _ "ServerStart hresult(object*;long*);" & _ "ConnectData hresult(long;variant*;bool*;variant*);" & _ "RefreshData hresult(long;variant*);" & _ "DisconnectData hresult(long);" & _ "Heartbeat hresult(long*);" & _ "ServerTerminate hresult();" ;;-------- Const $dtag_IRTDEvent = _ "UpdateNotify hresult();" & _ "get_HeartbeatInterval hresult(long);" & _ "put_HeartbeatInterval hresult(long*);" & _ "Disconnect hresult();" ;;-------- ;;============= Global $oErrorHandler $oErrorHandler = ObjEvent("AutoIt.Error", "_ErrFunc") ;;============= Global $oApp_Obj ;------------------ ;~ when using interface_description as Default ;~ Heartbeat atleast returning -1 ;~ indicating server not started $oApp_Obj = ObjCreateInterface($sCLSID_App, $sIID_IScripRTD, Default) If Not @error Then MsgBox(0, "", "ObjCreate() successful") ;;[ok] Local $oHrt_beat $oHrt_beat = $oApp_Obj.Heartbeat() If Not @error Then MsgBox(0, "", $oHrt_beat) ;;[ok] [returning -1] ;------------------ $oApp_Obj = 0 MsgBox(0, "", "--The End--") Exit ; #FUNCTION# ============================================================================= ; Name...........: _ErrFunc() ; ======================================================================================== ; User's COM error function. Will be called if COM error occurs #forceref $oErrorHandler Func _ErrFunc($oError) ; Do anything here. MsgBox(0, "", @ScriptName & " (" & $oError.scriptline & ") : ==> COM Error intercepted !" & @CRLF & _ @TAB & "err.number is: " & @TAB & @TAB & "0x" & Hex($oError.number) & @CRLF & _ @TAB & "err.windescription:" & @TAB & $oError.windescription & @CRLF & _ @TAB & "err.description is: " & @TAB & $oError.description & @CRLF & _ @TAB & "err.source is: " & @TAB & @TAB & $oError.source & @CRLF & _ @TAB & "err.helpfile is: " & @TAB & $oError.helpfile & @CRLF & _ @TAB & "err.helpcontext is: " & @TAB & $oError.helpcontext & @CRLF & _ @TAB & "err.lastdllerror is: " & @TAB & $oError.lastdllerror & @CRLF & _ @TAB & "err.scriptline is: " & @TAB & $oError.scriptline & @CRLF & _ @TAB & "err.retcode is: " & @TAB & "0x" & Hex($oError.retcode) & @CRLF & @CRLF) EndFunc ;==>_ErrFunc but when using vtable no result due to wrong structure ;~ but when using Vtable ;~ Heartbeat dosent return anything no error nothing ;~ may be due to wrong vtable structure $oApp_Obj = ObjCreateInterface($sCLSID_App, $sIID_IScripRTD, $tagIScripRTD_1) If Not @error Then MsgBox(0, "", "ObjCreate() successful") ;;[returning successful] ;~ Local $oHrt_beat $oApp_Obj.Heartbeat($oHrt_beat) If Not @error Then MsgBox(0, "", $oHrt_beat) ;;[no error nothing] so what's the correct vtable for IScripRTD & IRTDUpdateEvent & how to implement (IRTDUpdateEvent* callback) in Autoit to start the server. TypeLib file.txt Link to comment Share on other sites More sharing options...
markyrocks Posted January 20, 2021 Share Posted January 20, 2021 (edited) I'm not exactly sure as to why you want the vtable if it was fine the way it was. I don't have the patience to look through the methods to see if there's an issue. The only difference I see is the first working attempt you get the HResult return of the function with no parameters passed. The second time tho you attempt to use an autoit variable passed as a long ptr parameter... id guess that is at least part of the issue. If you want that value then the first thing I'd do is create a dllstruct and pass dllstructgetptr($struct) as the parameter and pull the data out via structgetdata(). What's the worst that could happen? Break it twice as hard. Edited January 20, 2021 by markyrocks Spoiler "I Believe array math to be potentially fatal, I may be dying from array math poisoning" Link to comment Share on other sites More sharing options...
junkew Posted January 23, 2021 Share Posted January 23, 2021 Search for objcreateinterface in the forums and see the udf implementations that have used objcreateinterface as a good learning reference. If you have idl maybe this helps FAQ 31 How to click some elements, FAQ 40 Test automation with AutoIt, Multithreading CLR .NET Powershell CMDLets Link to comment Share on other sites More sharing options...
jugador Posted January 25, 2021 Author Share Posted January 25, 2021 (edited) @junkew Sorry forgot to mention that I did try your code but it not work. input:- ServerStart([in] IRTDUpdateEvent* callback, [out, retval] long* result); ConnectData([in] long topicId, [in] SAFEARRAY(VARIANT)* strings, [in, out] VARIANT_BOOL* newValues, [out, retval] VARIANT* values); RefreshData([in, out] long* topicCount, [out, retval] SAFEARRAY(VARIANT)* data); DisconnectData([in] long topicId); Heartbeat([out, retval] long* result); ServerTerminate(); output:- "ServerStart hresult(ptr;long);" & _ "ConnectData hresult(long;intptr;ptr;variant);" & _ "RefreshData hresult(long;intptr);" & _ "DisconnectData hresult(long);" & _ "Heartbeat hresult(long*);" & _ "ServerTerminate hresult();" Main problem is to implement (IRTDUpdateEvent* callback) can you show me how to do it. Edited January 25, 2021 by jugador Link to comment Share on other sites More sharing options...
jugador Posted February 4, 2021 Author Share Posted February 4, 2021 I search through @trancexx, @Danyfirex, @Bilgus and few other’s posts but still no luck on IRTDUpdateEvent interface callback I know you people are busy but still it’s been two week without any help from the forum members. At-least say or give some hint , what I am missing in my post to get solution on this. Or should I assume that ServerStart( IRTDUpdateEvent* callback ) can’t be done in Autoit. again this also failed....... expandcollapse popup#include <AutoItConstants.au3> #include <WinAPI.au3> #include <array.au3> ;;-------- Const $sCLSID_App = "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" Const $sIID_IScripRTD = "{EC0E6191-DB51-11D3-8F3E-00C04F3651B8}" Const $sIID_IRTDEvent = "{A43788C1-D91B-11D3-8F39-00C04F3651B8}" ;;-------- Const $dtag_IRTDUpdateEvent = _ "UpdateNotify hresult();" & _ "GetHeartbeatInterval hresult(long*);" & _ "PutHeartbeatInterval hresult(long);" & _ "Disconnect hresult();" ;;-------- Const $sIID_IUnknown = "{00000000-0000-0000-C000-000000000046}" ;;============= Global $oErrorHandler $oErrorHandler = ObjEvent("AutoIt.Error", "_ErrFunc") ;;============= ;;-------- Local $oApp_Obj $oApp_Obj = ObjCreateInterface($sCLSID_App, $sIID_IScripRTD, Default) If Not @error Then MsgBox(0, "", "ObjCreate() successful") ;;[ok] Local $pIRTDUpdateEvents Local $ooIRTDUpdateEvents = ObjectFromTag("IRTDUpdateEvent_", $dtag_IRTDUpdateEvent, $pIRTDUpdateEvents) If Not @error Then MsgBox(0, "", "ObjCreate() successful") ;;[ok] Local $prt_IUnknown = $ooIRTDUpdateEvents() ;;ServerStart should return a 1 on success, and a negative value or 0 on failure. Local $oServer_Start $oServer_Start = $oApp_Obj.ServerStart($prt_IUnknown) If Not @error Then MsgBox(0, "", $oServer_Start) ;;[error ==> 0x80020005 (Type mismatch) ] #cs err.number is: 0x80020005 err.windescription: Type mismatch. err.description is: err.source is: err.helpfile is: err.helpcontext is: err.lastdllerror is: 0 err.scriptline is: 38 err.retcode is: 0x00000000 #ce $oApp_Obj = 0 MsgBox(0, "", "--The End--") Exit ;;-------- ;;============= Func IRTDUpdateEvent_UpdateNotify($pSelf) Return $S_OK EndFunc ;==>IRTDUpdateEvent_UpdateNotify Func IRTDUpdateEvent_GetHeartbeatInterval($pSelf, $pvalue) Local $S_result = 0 ConsoleWrite('> $S_result: ' & $S_result & @CRLF) Return $S_result EndFunc ;==>IRTDUpdateEvent_GetHeartbeatInterval Func IRTDUpdateEvent_PutHeartbeatInterval($pSelf, $pvalue) ConsoleWrite('> $pvalue: ' & $pvalue & @CRLF) Return $S_OK EndFunc ;==>IRTDUpdateEvent_PutHeartbeatInterval Func IRTDUpdateEvent_Disconnect($pSelf) Return $S_OK EndFunc ;==>IRTDUpdateEvent_Disconnect Func IRTDUpdateEvent_QueryInterface($pSelf, $pRIID, $pObj) Local $sIID = StringFromGUID($pRIID) If $sIID = $sIID_IUnknown Then DllStructSetData(DllStructCreate("ptr", $pObj), 1, $pSelf) Return $S_OK ElseIf $sIID = $sIID_IRTDEvent Then DllStructSetData(DllStructCreate("ptr", $pObj), 1, $pSelf) Return $S_OK Else ConsoleWrite('QueryInterface ' & $sIID & @CRLF) Return $E_NOINTERFACE EndIf EndFunc ;==>IRTDUpdateEvent_QueryInterface Func IRTDUpdateEvent_AddRef($pSelf) Return 1 EndFunc ;==>IRTDUpdateEvent_AddRef Func IRTDUpdateEvent_Release($pSelf) Return 1 EndFunc ;==>IRTDUpdateEvent_Release ;;============= ;;-------- Func StringFromGUID($pGUID) Local $aResult = DllCall("ole32.dll", "int", "StringFromGUID2", "struct*", $pGUID, "wstr", "", "int", 40) If @error Then Return SetError(@error, @extended, "") Return SetExtended($aResult[0], $aResult[2]) EndFunc ;==>StringFromGUID ;;-------- ;;-------- Func ObjectFromTag($sFunctionPrefix, $tagInterface, ByRef $tInterface, $fPrint = False, $bIsUnknown = Default, $sIID = "{00000000-0000-0000-C000-000000000046}") ; last param is IID_IUnknown by default If $bIsUnknown = Default Then $bIsUnknown = True Local $sInterface = $tagInterface ; copy interface description Local $tagIUnknown = "QueryInterface hresult(ptr;ptr*);" & _ "AddRef dword();" & _ "Release dword();" ; Adding IUnknown methods If $bIsUnknown Then $tagInterface = $tagIUnknown & $tagInterface ; Below line is really simple even though it looks super complex. It's just written weird to fit in one line, not to steal your attention Local $aMethods = StringSplit(StringReplace(StringReplace(StringReplace(StringReplace(StringTrimRight(StringReplace(StringRegExpReplace(StringRegExpReplace($tagInterface, "\w+\*", "ptr"), "\h*(\w+)\h*(\w+\*?)\h*(\((.*?)\))\h*(;|;*\z)", "$1\|$2;$4" & @LF), ";" & @LF, @LF), 1), "object", "idispatch"), "hresult", "long"), "bstr", "ptr"), "variant", "ptr"), @LF, 3) Local $iUbound = UBound($aMethods) Local $sMethod, $aSplit, $sNamePart, $aTagPart, $sTagPart, $sRet, $sParams, $hCallback ; Allocation $tInterface = DllStructCreate("int RefCount;int Size;ptr Object;ptr Methods[" & $iUbound & "];int_ptr Callbacks[" & $iUbound & "];ulong_ptr Slots[16]") ; 16 pointer sized elements more to create space for possible private props If @error Then Return SetError(1, 0, 0) For $i = 0 To $iUbound - 1 $aSplit = StringSplit($aMethods[$i], "|", 2) If UBound($aSplit) <> 2 Then ReDim $aSplit[2] $sNamePart = $aSplit[0] $sTagPart = $aSplit[1] $sMethod = $sFunctionPrefix & $sNamePart If $fPrint Then Local $iPar = StringInStr($sTagPart, ";", 2), $t If $iPar Then $t = "Ret: " & StringLeft($sTagPart, $iPar - 1) & " " & _ "Par: " & StringRight($sTagPart, StringLen($sTagPart) - $iPar) Else $t = "Ret: " & $sTagPart EndIf Local $s = "Func " & $sMethod & _ "( $pSelf ) ; " & $t & @CRLF & _ "EndFunc" & @CRLF ConsoleWrite($s) EndIf $aTagPart = StringSplit($sTagPart, ";", 2) $sRet = $aTagPart[0] $sParams = StringReplace($sTagPart, $sRet, "", 1) $sParams = "ptr" & $sParams $hCallback = DllCallbackRegister($sMethod, $sRet, $sParams) If @error Then ConsoleWrite('! ' & @error & ' ' & $sMethod & @CRLF & @CRLF) EndIf DllStructSetData($tInterface, "Methods", DllCallbackGetPtr($hCallback), $i + 1) ; save callback pointer DllStructSetData($tInterface, "Callbacks", $hCallback, $i + 1) ; save callback handle Next DllStructSetData($tInterface, "RefCount", 1) ; initial ref count is 1 DllStructSetData($tInterface, "Size", $iUbound) ; number of interface methods DllStructSetData($tInterface, "Object", DllStructGetPtr($tInterface, "Methods")) ; Interface method pointers Return ObjCreateInterface(DllStructGetPtr($tInterface, "Object"), $sIID, $sInterface, $bIsUnknown) ; pointer that's wrapped into object EndFunc ;==>ObjectFromTag ;;-------- ; #FUNCTION# ============================================================================= ; Name...........: _ErrFunc() ; ======================================================================================== ; User's COM error function. Will be called if COM error occurs #forceref $oErrorHandler Func _ErrFunc($oError) ; Do anything here. ConsoleWrite(@ScriptName & " (" & $oError.scriptline & ") : ==> COM Error intercepted !" & @CRLF & _ @TAB & "err.number is: " & @TAB & @TAB & "0x" & Hex($oError.number) & @CRLF & _ @TAB & "err.windescription:" & @TAB & $oError.windescription & @CRLF & _ @TAB & "err.description is: " & @TAB & $oError.description & @CRLF & _ @TAB & "err.source is: " & @TAB & @TAB & $oError.source & @CRLF & _ @TAB & "err.helpfile is: " & @TAB & $oError.helpfile & @CRLF & _ @TAB & "err.helpcontext is: " & @TAB & $oError.helpcontext & @CRLF & _ @TAB & "err.lastdllerror is: " & @TAB & $oError.lastdllerror & @CRLF & _ @TAB & "err.scriptline is: " & @TAB & $oError.scriptline & @CRLF & _ @TAB & "err.retcode is: " & @TAB & "0x" & Hex($oError.retcode) & @CRLF & @CRLF) EndFunc ;==>_ErrFunc Link to comment Share on other sites More sharing options...
junkew Posted February 4, 2021 Share Posted February 4, 2021 Check event examples on iuiautomation from @larsj as they look similar in way of defining them. FAQ 31 How to click some elements, FAQ 40 Test automation with AutoIt, Multithreading CLR .NET Powershell CMDLets Link to comment Share on other sites More sharing options...
Bilgus Posted February 5, 2021 Share Posted February 5, 2021 uh maybe a dumb question.. did you set interval and trying UpdateNotify() manually I didnt see that in your examples above https://docs.microsoft.com/en-us/dotnet/api/microsoft.office.interop.excel.irtdupdateevent.heartbeatinterval?view=excel-pia#Microsoft_Office_Interop_Excel_IRTDUpdateEvent_HeartbeatInterval Quote Setting the HeartbeatInterval property to -1 will result in the Heartbeat() method not being called. your vtable looks right to me.. https://github.com/objectcomputing/OpenDDS/blob/master/tools/excelRTD/IRTDServer.idl have you tried changing the pointers form long* to ptr? sometimes it atleast crashes to show it got called Link to comment Share on other sites More sharing options...
jugador Posted February 5, 2021 Author Share Posted February 5, 2021 (edited) 9 hours ago, Bilgus said: did you set interval and trying UpdateNotify() manually I didnt see that in your examples above Thank's @Bilgus No. I haven’t set anything The thing is I am using software that has a feature to get Real-time Data (RTD) to excel. As RTD function is based on COM technology, so instead of excel I want to use Autoit to retrieve this data. Basically trying to make Rtd Client in Autoit. Now when I do.... ;~ [IRtdServer.Heartbeat method Returns a Long value. a positive number indicates that the server is active] ;~ [work perfectly] ;~ [return -1] [indicate server not started] Local $oHrt_beat $oHrt_beat = $oApp_Obj.Heartbeat() If Not @error Then MsgBox(0, "", $oHrt_beat) So to make it work need to implement IRtdServer.ServerStart method first. This is Rtd Client in C# https://github.com/crosbymichael/thinkorswim/tree/master/ThinkOrSwim but my bad as no knowledge in C# Edited February 5, 2021 by jugador Link to comment Share on other sites More sharing options...
Bilgus Posted February 5, 2021 Share Posted February 5, 2021 Its a little fuzzy but I remember having to go through hoops to make a callback function that was compatible with autoit and my COM object I still think you should try setting the interval and try calling the update function directly to see if it works Butttttttttttttt as referenced here: https://weblogs.asp.net/kennykerr/Rtd8 Quote There are two very important requirements that you must keep in mind. This applies equally to developers using C# or C++ for their implementations. The first is that the IRTDUpdateEvent interface pointer that the RTD server receives must only be called from the apartment in which it was received. Since this interface pointer is received in a call to the RTD server’s ServerStart method it follows that this interface must only be called from the apartment that the RTD server lives in. This is where a strong foundation in COM is helpful. If you’re a .NET developer you’re probably scratching your head wondering what on earth an apartment is. so it might be that you have to jump through hoops like creating a shared piece of memory in the excel process and then monkey patch in some asm to act as an intermediary maybe I'm just making it complicated.. jugador 1 Link to comment Share on other sites More sharing options...
junkew Posted February 6, 2021 Share Posted February 6, 2021 Start with this thread from @LarsJ as I feel your server looks logically similar to whats done with iuiautomation events. Clients in python seem to exist so no reason to believe it cannot be done with Autoit https://github.com/brotchie/pyrtd/blob/master/rtd/client.py https://www.cin.ufpe.br/~cfms/graduacao/2007-2/Projeto_Desenvolvimento/Software-PC/python/Lib/site-packages/win32com/demos/excelRTDServer.py I think there are only a dozen of people max in the forum that can help in detail. My advice make a c# server with visual studio that works with excel. Share that so we have a reproducer for working with an rtd server. From there we could look to make an RTD client similar as excel would do. Probably easier is just to use a different api to the server thats less complicated. jugador 1 FAQ 31 How to click some elements, FAQ 40 Test automation with AutoIt, Multithreading CLR .NET Powershell CMDLets Link to comment Share on other sites More sharing options...
jugador Posted February 17, 2021 Author Share Posted February 17, 2021 (edited) @Bilgus After lot of search found this github C++ project which is working.....https://github.com/seenuchennai/TradingTools/tree/master/AmibrokerFeeder/RTDMan/source He created callback interface to implement (IRTDUpdateEvent* callback) and use OpenEvent & SetEvent to call IRTDUpdateEvent. UpdateNotify() which is _WinAPI_CreateEvent & _WinAPI_SetEvent in Autoit. https://github.com/seenuchennai/TradingTools/blob/master/AmibrokerFeeder/RTDMan/source/rtd_callback.cpp //https://github.com/seenuchennai/TradingTools/blob/master/AmibrokerFeeder/RTDMan/source/rtd_callback.cpp #include "rtd_callback.h" CallbackImpl::CallbackImpl(){ Event_RTD_Update = OpenEvent( EVENT_MODIFY_STATE , false, _T("RTD_UPDATE") ); if( Event_RTD_Update == NULL ){ throw "RTD_UPDATE Open error - " + GetLastError() ; } } HRESULT STDMETHODCALLTYPE CallbackImpl::UpdateNotify(){ if( Event_RTD_Update ) SetEvent ( Event_RTD_Update ); // Signal Update return S_OK; } HRESULT STDMETHODCALLTYPE CallbackImpl::Disconnect(){ return S_OK; } HRESULT STDMETHODCALLTYPE CallbackImpl::get_HeartbeatInterval( long *value){ *value=-1; return S_OK; } HRESULT STDMETHODCALLTYPE CallbackImpl::put_HeartbeatInterval( long value ){ return S_OK; } https://github.com/seenuchennai/TradingTools/blob/master/AmibrokerFeeder/RTDMan/source/rtd_client.cpp //https://github.com/seenuchennai/TradingTools/blob/master/AmibrokerFeeder/RTDMan/source/rtd_client.cpp void RTDClient::startServer(){ long server_status = -1; CComObject<CallbackImpl>::CreateInstance(&callback); // Create Callback Object callback->AddRef(); HRESULT hr = comObjectScripRTD->ServerStart( callback , &server_status ); while( server_status <= 0 ){ // Returns 0 if callback null, +ve if SUCCESS std::cout << "Unable to start RTD Server. Waiting \r"; std::flush(std::cout); Sleep(1000); hr = comObjectScripRTD->ServerStart( callback , &server_status ); // Try to connect to RTD server again } std::cout << "RTD Server Started \t\t\t\t\t\t\t" << std::endl; } @LarsJ has also started a thread on ObjectFromTag() to implement COM callback. So guys please help me to convert this c++ code to autoit. not the whole code just callback interface [ CallbackImpl() ] & RTDClient::startServer () portion. Edited February 17, 2021 by jugador Link to comment Share on other sites More sharing options...
LarsJ Posted February 17, 2021 Share Posted February 17, 2021 Do you have a server that we can use to test the code? I would rather not have to start implementing a server myself. jugador and junkew 2 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions Link to comment Share on other sites More sharing options...
jugador Posted February 18, 2021 Author Share Posted February 18, 2021 (edited) On 2/17/2021 at 7:33 PM, LarsJ said: Do you have a server that we can use to test the code? I would rather not have to start implementing a server myself. @LarsJ sorry no such server as its for a trading software. but on google search “Rtd Server Github” got this link...... https://www.codeproject.com/articles/245265/guide-to-writing-custom-functions-in-excel-part https://github.com/mdvx/kafka-rtd https://github.com/mdvx/redis-rtd https://github.com/mdvx/crypto-rtd https://github.com/mdvx/rabbit-rtd Edited February 18, 2021 by jugador Link to comment Share on other sites More sharing options...
junkew Posted February 18, 2021 Share Posted February 18, 2021 @genius257 could we combine above with the autoitobject_internal and your latest register ROT functions to make "easily" an RTD com server as is discussed in this thread? FAQ 31 How to click some elements, FAQ 40 Test automation with AutoIt, Multithreading CLR .NET Powershell CMDLets Link to comment Share on other sites More sharing options...
genius257 Posted February 18, 2021 Share Posted February 18, 2021 Hi @junkew. I'm not 100% sure. Out of the box, autoitobject_internal won't work as the interface needed is IRtdServer. my code only support IDispatch and IUnknown. You can easily override the IUnknown::QueryInterface when calling the IDispatch function from my code. That would allow you to return the IRtdServer interface pointer, created and managed in AutoIt code. I'm a bit unsure how the callback works. If Excel provides the IRTDUpdateEvent object to our IRtdServer method, then there is no problem, and it should be possible. If Excel does not provide the IRTDUpdateEvent object i am not sure where it is supposed to come from. TLDR; Maybe, but it would require building the IRtdServer part of the code yourself My highlighted topics: AutoIt Package Manager, AutoItObject Pure AutoIt, AutoIt extension for Visual Studio Code Github: AutoIt HTTP Server, AutoIt HTML Parser Link to comment Share on other sites More sharing options...
junkew Posted February 18, 2021 Share Posted February 18, 2021 @genius257 I was triggered by this example in visual basic with an RTD server which at first sight looks like excel is just calling a com object https://docs.microsoft.com/en-us/previous-versions/office/troubleshoot/office-developer/create-realtimedata-server-in-excel FAQ 31 How to click some elements, FAQ 40 Test automation with AutoIt, Multithreading CLR .NET Powershell CMDLets Link to comment Share on other sites More sharing options...
genius257 Posted February 18, 2021 Share Posted February 18, 2021 @junkew. They define a class that implements IRtdServer IRtdServer is indeed a COM Object ALL COM Objects (to my knowledge) inherits from IUnknown. When a COM object is recived, IUnknown::QueryInterface is called to receive the pointer for the requested Interface, specified by a IID. A COM Object can have many interfaces associated. So if you use my code as is, Excel would call the IUnknown::QueryInterface with IID for IRtdServer and since the default QueryInterface function in my code does not support that IID, it would return 0x80004002 (E_NOINTERFACE) and Excel would do nothing else with the Object. junkew 1 My highlighted topics: AutoIt Package Manager, AutoItObject Pure AutoIt, AutoIt extension for Visual Studio Code Github: AutoIt HTTP Server, AutoIt HTML Parser Link to comment Share on other sites More sharing options...
genius257 Posted February 19, 2021 Share Posted February 19, 2021 (edited) @junkew. I've just tried to see if this approach would work. I don't have a registered Excel version to play around with, so i used WPS as a substitute. It does not seem like Excel looks for the object in the ROT. I suspect it looks in the windows registry for the ActiveX DLL. If so, a dummy DLL would be needed to deliver the object, at least. Disclaimer: i do not have the full grasp of the RTD method in Excel, i may just not call it right. Here's my code: #include "AutoItObject_Internal.au3" #include "AutoItObject_Internal_ROT.au3" $sIIDs = FileRead("C:\Users\Frank\Downloads\iids.txt") $oIDispatch = IDispatch(QueryInterface2) $dwRegister = _AOI_ROT_register($oIDispatch, "MyServer.MyClass", True) OnAutoItExitRegister("CleanUp") ConsoleWrite(@CRLF&@CRLF) ;Spacer to see if QueryInterface is called at any time after the registration While 1 Sleep(10) WEnd Func QueryInterface2($pSelf, $pRIID, $pObj) Local $sGUID=DllCall("ole32.dll", "int", "StringFromGUID2", "PTR", $pRIID, "wstr", "", "int", 40)[2] ConsoleWrite($sGUID&@CRLF) Return QueryInterface($pSelf, $pRIID, $pObj) EndFunc Func CleanUp() _AOI_ROT_revoke($dwRegister) EndFunc Here's the line used in "Excel": =RTD("MyServer.MyClass","","AAA",10) So my main issue currently is delivering the object to "Excel" Edited February 19, 2021 by genius257 My highlighted topics: AutoIt Package Manager, AutoItObject Pure AutoIt, AutoIt extension for Visual Studio Code Github: AutoIt HTTP Server, AutoIt HTML Parser Link to comment Share on other sites More sharing options...
jugador Posted February 21, 2021 Author Share Posted February 21, 2021 @LarsJ one more find.... may be this one will solve the purpose of testing ServerStart(IRTDUpdateEvent* callback). https://gist.github.com/govert/03df749f38b9582b1217 Create a minimal RTD server in C# => Start Visual Studio "As Administrator" (to enable the COM registration - see below). => Create a new Class Library project. => In the project properties, on the Build tab, enable "Register for COM interop". (Building with this setting on requires admin permissions, so Visual Studio should be run "As Administrator" if the build fails with an "access denied" error.) => In the project properties, on the Debug tab, set the Start Action to be "Start external program" with Excel as the program (on my machine, the path to Excel is "C:\Program Files (x86)\Microsoft Office\Office15\EXCEL.EXE"). => Add a Reference to the Microsoft.Office.Interop.Excel assembly. => Add the following code in the .cs file: expandcollapse popupusing System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using Microsoft.Office.Interop.Excel; namespace MinimalRtd { [ ComVisible(true), Guid("86541CE9-EA39-4175-B37A-FDAE655AD30C"), ProgId("Minimal.RtdServer"), ] public class RtdServer : IRtdServer { private IRTDUpdateEvent _callback; private Timer _timer; private List<int> _topicIds; public int ServerStart(IRTDUpdateEvent callback) { Log("ServerStart"); _callback = callback; _topicIds = new List<int>(); _timer = new Timer(); _timer.Tick += new EventHandler(TimerEventHandler); _timer.Interval = 1000; _timer.Start(); return 1; } public void ServerTerminate() { Log("ServerTerminate"); _timer.Dispose(); _timer = null; } public object ConnectData(int topicId, ref Array strings, ref bool newValues) { Log("ConnectData: {0}, {1}", topicId, strings.GetValue(0)); _topicIds.Add(topicId); return GetTime(); } public void DisconnectData(int topicId) { Log("DisconnectData: {0}", topicId); } // Because we're using a System.Windows.Forms.Timer that was created on the main thread, // this call will always be on the main thread. private void TimerEventHandler(object sender, EventArgs args) { _callback.UpdateNotify(); } // All topics are updated, and get the same value public Array RefreshData(ref int topicCount) { string time = GetTime(); topicCount = _topicIds.Count; object[,] data = new object[2, topicCount]; for (int i = 0; i < topicCount; i++) { data[0, i] = _topicIds[i]; data[1, i] = time; } return data; } public int Heartbeat() { return 1; } private string GetTime() { return DateTime.Now.ToString("hh:mm:ss:ff"); } private void Log(string format, params object[] args) { Debug.Print(format, args); } } } Test the RTD call directly in Excel => In a new Workbook, enter the formula: =RTD("Minimal.RtdServer", "", "test") => The result should be a ticking time string, updated every 2 seconds (the default Application.RTD.ThrottleInterval is 2000). Link to comment Share on other sites More sharing options...
LarsJ Posted February 21, 2021 Share Posted February 21, 2021 I'll try to find time to look at the code over the coming week. jugador 1 Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions Link to comment Share on other sites More sharing options...
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