Чистый виртуальный вызов функции R6025
Я получаю случайные R6025 - чисто виртуальные ошибки вызова функций в случайное время при использовании этого пользовательского C# RTD-клиента для ThinkOrSwim.
Как я могу а) отладить его, чтобы выяснить, что происходит не так, и б) исправить это?
Когда в окне появляется окно с сообщением об ошибке, код продолжает работать в фоновом режиме, и исключений не выдается. Но когда я нажимаю кнопку ОК в окне сообщения, Windows закрывает приложение.
Вот фрагмент кода, запрашивающий данные RTD:
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;
}
Вот реализация клиента RTD:
// 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();
}
1 ответ
Вот как я "исправил" проблему, делая ее менее частой. Теперь начальная задержка составляет 200 мс, и эта задержка удваивается при каждой итерации цикла. (Все еще ищу реальное решение)
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;
}