Skip to content

Instantly share code, notes, and snippets.

@bsommardahl
Last active January 17, 2024 15:42
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save bsommardahl/8605208 to your computer and use it in GitHub Desktop.
Save bsommardahl/8605208 to your computer and use it in GitHub Desktop.
RTD Client in C#
public interface IRtdClient
{
object GetValue(params object[] args);
}
public class RtdClient : IRtdClient
{
readonly string _rtdProgId;
static IRtdServer _rtdServer;
public RtdClient(string rtdProgId)
{
_rtdProgId = rtdProgId;
}
public object GetValue(params object[] args)
{
const int topicCount = 1;
var rnd = new Random();
var topicId = rnd.Next(int.MaxValue);
var rtdServer = GetRtdServer();
rtdServer.ConnectData(topicId, args, true);
object val = null;
while (val == null)
{
var alive = rtdServer.Heartbeat();
if (alive != 1)
GetRtdServer();
else
{
var refresh = rtdServer.RefreshData(topicCount);
if (refresh.Length <= 0) continue;
if (refresh[0, 0].ToString() == topicId.ToString())
{
val = refresh[1, 0];
}
}
}
rtdServer.DisconnectData(topicId);
return val;
}
IRtdServer GetRtdServer()
{
if(_rtdServer==null)
{
Type rtd = Type.GetTypeFromProgID(_rtdProgId);
_rtdServer = (IRtdServer)Activator.CreateInstance(rtd);
}
return _rtdServer;
}
}
[ComImport,
TypeLibType((short)0x1040),
Guid("EC0E6191-DB51-11D3-8F3E-00C04F3651B8")]
public interface IRtdServer
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(10)]
int ServerStart([In, MarshalAs(UnmanagedType.Interface)] IRTDUpdateEvent callback);
[return: MarshalAs(UnmanagedType.Struct)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(11)]
object ConnectData([In] int topicId, [In, MarshalAs(UnmanagedType.SafeArray,
SafeArraySubType = VarEnum.VT_VARIANT)] ref object[] parameters, [In, Out] ref bool newValue);
[return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(12)]
object[,] RefreshData([In, Out] ref int topicCount);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(13)]
void DisconnectData([In] int topicId);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(14)]
int Heartbeat();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(15)]
void ServerTerminate();
}
[ComImport,
TypeLibType((short) 0x1040),
Guid("A43788C1-D91B-11D3-8F39-00C04F3651B8")]
public interface IRTDUpdateEvent
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(10),
PreserveSig]
void UpdateNotify();
[DispId(11)]
int HeartbeatInterval { [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(11)]
get; [param: In]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(11)]
set; }
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime), DispId(12)]
void Disconnect();
}
@avengerx
Copy link

This looks like the best I can find on the web about code to do this. But for some reason I cannot pass the topics args at all.

Having this on MS-Excel: =RTD("rtdtrading.rtdserver",, "WINZ20_F_0", "HOR") This formula works on Excel and pulls fresh data.

I then tried to instantiate the object with:

var handler = new RtdClient("rtdtrading.rtdserver");

So far, so good. But then, when I try to

var valList = handler.GetValue(new object[] { "WINZ20_G_0", "HOR" });

I get a big Catastrophic failure (0x8000FFFF (E_UNEXPECTED)) from IRtdServer.ConnectData(Int32 topicId, Object[]& parameters, Boolean& newValue), line 24 in the code above.

If I try to use new string[] { "WINZ20_G_0", "HOR" });, then the error changes to Specified array was not of the expected type, happening a bit deeper, but I believe still before ConnectData() is actually run, as the call stack suggestes:

at System.StubHelpers.MngdSafeArrayMarshaler.ConvertSpaceToNative(IntPtr pMarshalState, Object& pManagedHome, IntPtr pNativeHome)
at ProfitRTDAnalyzer.Program.IRtdServer.ConnectData(Int32 topicId, Object[]& parameters, Boolean& newValue)

@Neznakomec
Copy link

Neznakomec commented Aug 5, 2021

It my case of server (TwsRtdServer) I couldn't understand for a long time,
why data isn't going, response is empty array...

And explicit starting of server helped me,
I added that line:

    IRtdServer GetRtdServer()
    {
        if(_rtdServer==null)
        {
            Type rtd = Type.GetTypeFromProgID(_rtdProgId);
            _rtdServer = (IRtdServer)Activator.CreateInstance(rtd);      
            // ++
            _rtdServer.startServer(/*Define class with dummy IRTDUpdateEvent implementation and pass it HERE */);
            // ++
        }
        return _rtdServer;
    }

Only after that everything worked. Good if you have access to sources of the server.

@julianozucatti
Copy link

It my case of server (TwsRtdServer) I couldn't understand for a long time,
why data isn't going, response is empty array...

And explicit starting of server helped me,
I added that line:

    IRtdServer GetRtdServer()
    {
        if(_rtdServer==null)
        {
            Type rtd = Type.GetTypeFromProgID(_rtdProgId);
            _rtdServer = (IRtdServer)Activator.CreateInstance(rtd);      
            // ++
            _rtdServer.startServer(/*Define class with dummy IRTDUpdateEvent implementation and pass it HERE */);
            // ++
        }
        return _rtdServer;
    }

Only after that everything worked. Good if you have access to sources of the server.

@Neznakomec
I could demonstrate what your RTD client looks like, I'm facing the same problem with the exception of calling IRtdServer.ConnectData(Int32

@paulorodriguesxv
Copy link

paulorodriguesxv commented Oct 7, 2021

This looks like the best I can find on the web about code to do this. But for some reason I cannot pass the topics args at all.

Having this on MS-Excel: =RTD("rtdtrading.rtdserver",, "WINZ20_F_0", "HOR") This formula works on Excel and pulls fresh data.

I then tried to instantiate the object with:

var handler = new RtdClient("rtdtrading.rtdserver");

So far, so good. But then, when I try to

var valList = handler.GetValue(new object[] { "WINZ20_G_0", "HOR" });

I get a big Catastrophic failure (0x8000FFFF (E_UNEXPECTED)) from IRtdServer.ConnectData(Int32 topicId, Object[]& parameters, Boolean& newValue), line 24 in the code above.

If I try to use new string[] { "WINZ20_G_0", "HOR" });, then the error changes to Specified array was not of the expected type, happening a bit deeper, but I believe still before ConnectData() is actually run, as the call stack suggestes:

at System.StubHelpers.MngdSafeArrayMarshaler.ConvertSpaceToNative(IntPtr pMarshalState, Object& pManagedHome, IntPtr pNativeHome)
at ProfitRTDAnalyzer.Program.IRtdServer.ConnectData(Int32 topicId, Object[]& parameters, Boolean& newValue)

Hi @avengerx, to solve this problem, I do the follwing:

Create a a new class to implement UpdateEvent:

public class UpdateEvent : IRTDUpdateEvent
{
    public long Count { get; set; }
    public int HeartbeatInterval { get; set; }

    public UpdateEvent()
    {
        // Do not call the RTD Heartbeat()
        // method.
        HeartbeatInterval = -1;
    }

    public void Disconnect()
    {
        // Do nothing.
    }

    public void UpdateNotify()
    {
        Count++;
    }

and then, replace it into GetRtdServer function

    IRtdServer GetRtdServer()
    {
        if (_rtdServer == null)
        {
            Type rtd = Type.GetTypeFromProgID(_rtdProgId);
            _rtdServer = (IRtdServer)Activator.CreateInstance(rtd);

            // Create the updateEvent.
            UpdateEvent updateEvent = new UpdateEvent();
            _rtdServer.ServerStart(updateEvent);
            
        }
        return _rtdServer;
    }

    Now it's working for me. 

@Neznakomec
Copy link

@julianozucatti I can give my dirty sandbox application
where I tried to make RTD Client for "TWS RTD Server API".
What is good here, you can see the sources of this API and what is going
with your requests.

Google Drive

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment