0

I am getting random R6025 - pure virtual function call errors at random times when using this custom c# RTD client for ThinkOrSwim.

How can I a) debug it to find out what's going wrong, and b) fix it?

When windows pops up the message box saying there is an error, the code keeps running in the backgroud and no exceptions are thrown. But when I click OK on the message box, windows shuts the application down.

Here is a snippet of the code requesting RTD data:

var tosClassId = new Guid(Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Tos.RTD\CLSID", "", null).ToString());
var rtdClient = new RtdClient(tosClassId);

var date = DateTime.Now.Date;
foreach (var futureSymbol in futureSymbols) {
    var settlement = GetDouble(rtdClient, futureSymbol, "CLOSE");
    yield return new TOSEODDataPoint {
        Date = date,
        Settlement = settlement,
    };
}

static double GetDouble(IRtdClient client, string symbol, string topic) {
    object value;
    if (client.GetValue(TimeSpan.FromSeconds(3), out value, topic, symbol)) {
        try { return double.Parse(value.ToString()); } catch { return 0; }
    }
    return 0;
}

Here is the RTD client implementation:

// Inspired by http://awkwardcoder.com/2014/01/24/excel-rtd-client-in-c/

public interface IRtdClient {
    bool GetValue(TimeSpan timeout, out object value, params object[] args);
}

public class RtdClient : IRtdClient {

    readonly Guid ServerId;
    static readonly Dictionary<Guid, IRtdServer> servers = new Dictionary<Guid, IRtdServer>();
    static readonly Dictionary<Guid, int> topicIds = new Dictionary<Guid, int>();

    public RtdClient(Guid serverId) {
        ServerId = serverId;
    }

    public bool GetValue(TimeSpan timeout, out object value, params object[] args) {

        value = null;
        var server = GetRtdServer();
        var topicId = GetTopicId();

        var sw = Stopwatch.StartNew();

        try {
            server.ConnectData(topicId, args, true);
            while (sw.Elapsed < timeout) {
                var alive = server.Heartbeat();
                if (alive != 1) {
                    // TODO: What should be done here?
                    return false;
                }
                var refresh = server.RefreshData(1);
                if (refresh.Length > 0) {
                    if (refresh[0, 0].ToString() == topicId.ToString()) {
                        value = refresh[1, 0];
                        return true;
                    }
                }
                Thread.Sleep(20);
            }
        } catch (Exception ex) {
            // TODO: Log exception
            return false;
        } finally {
            server.DisconnectData(topicId);
            sw.Stop();
        }
        return false;
    }

    IRtdServer GetRtdServer() {
        IRtdServer server;
        if (!servers.TryGetValue(ServerId, out server)) {
            Type rtd = Type.GetTypeFromCLSID(ServerId);
            server = (IRtdServer)Activator.CreateInstance(rtd);
            servers[ServerId] = server;
        }
        return server;
    }

    int GetTopicId() {
        int topicId = 0;
        if (topicIds.TryGetValue(ServerId, out topicId)) {
            topicId++;
        }
        topicIds[ServerId] = topicId;
        return topicId;
    }
}

[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();
}
bboyle1234
  • 4,859
  • 2
  • 24
  • 29
  • Is the error happening during a call to the server? – Ben Voigt Nov 04 '14 at 02:11
  • Hi Ben :) I can't debug to figure out exactly what precipitates the error. I assume it's somewhere in the GetValue method – bboyle1234 Nov 04 '14 at 02:17
  • I mean, I can't debug because the R6025 error doesn't interrupt the c# code, which keeps running until I click OK on the windows message box – bboyle1234 Nov 04 '14 at 02:18
  • I managed to "fix" the code by making it fail less often by introducing larger and increasing delays in the main while loop. – bboyle1234 Nov 04 '14 at 02:35
  • Is it an out-of-process server? Could the message be coming from the server and not your application? – Ben Voigt Nov 04 '14 at 02:45

1 Answers1

0

Here's how I "fixed" the issue by making it fail less often ... there is now an initial delay of 200ms and the delay doubles every iteration of the loop. (Am still looking for a real solution)

    public bool GetValue(TimeSpan timeout, out object value, params object[] args) {

        value = null;
        var server = GetRtdServer();
        var topicId = GetTopicId();

        var sw = Stopwatch.StartNew();
        var delay = 200;

        try {
            server.ConnectData(topicId, args, true);
            while (sw.Elapsed < timeout) {
                Thread.Sleep(delay);
                delay *= 2;
                var alive = server.Heartbeat();
                if (alive != 1) {
                    // TODO: What should be done here?
                    return false;
                }
                var refresh = server.RefreshData(1);
                if (refresh.Length > 0) {
                    if (refresh[0, 0].ToString() == topicId.ToString()) {
                        value = refresh[1, 0];
                        return true;
                    }
                }
            }
        } catch (Exception ex) {
            // TODO: Log exception
            return false;
        } finally {
            server.DisconnectData(topicId);
            sw.Stop();
        }
        return false;
    }
bboyle1234
  • 4,859
  • 2
  • 24
  • 29