fedarovich / qbittorrent-net-client

qBittorrent client library for .Net
MIT License
32 stars 14 forks source link

Seems to cause qBitTorrent to crash #5

Closed devonuto closed 2 years ago

devonuto commented 4 years ago

I have an application that uses this package, and if I call the external application on torrent completion, qBittorrent crashes. However if I run it on a schedule, it doesn't seem to.

Not sure if the bug is on the package side, or the web interface of qBitTorrent.

This is the error I get (qBittorrent claim it's not their fault...):

qBittorrent has crashed Please file a bug report at http://bugs.qbittorrent.org and provide the following information:

qBittorrent version: v4.1.8 (64-bit) Libtorrent version: 1.1.13.0 Qt version: 5.13.1 Boost version: 1.71.0 OS version: Windows 10 (10.0) 10.0.17134 x86_64

Caught signal: SIGABRT


#  0 qbittorrent.exe      0x00007ff66f02c3ca straceWin::getBacktrace()[ app\stacktrace_win.h : 213 ]
#  1 qbittorrent.exe      0x00007ff66f02f58d sigAbnormalHandler(signum)[ app\main.cpp : 302 ]
#  2 qbittorrent.exe      0x00007ff66fbbfe62 raise(signum)[ d:\th\minkernel\crts\ucrt\src\appcrt\misc\signal.cpp : 516 ]
#  3 qbittorrent.exe      0x00007ff66fbcab98 abort()[ d:\th\minkernel\crts\ucrt\src\appcrt\startup\abort.cpp : 64 ]
#  4 qbittorrent.exe      0x00007ff66fbb910d _purecall()[ d:\agent\_work\1\s\src\vctools\crt\vcruntime\src\misc\purevirt.cpp : 29 ]
#  5 qbittorrent.exe      0x00007ff66f04ed24 BitTorrent::Session::handleAlert(a, a)[ base\bittorrent\session.cpp : 4139 ]
#  6 qbittorrent.exe      0x00007ff66f04ec74 BitTorrent::Session::readAlerts()[ base\bittorrent\session.cpp : 4129 ]
#  7 qbittorrent.exe      0x00007ff66f1bd7f8 BitTorrent::Session::qt_static_metacall(_o, _c, _id, _a)[ release\moc_session.cpp : 401 ]
#  8 qbittorrent.exe      0x00007ff66f9c3afa QMetaCallEvent::placeMetaCall()
#  9 qbittorrent.exe      0x00007ff66f9c475f QObject::event()
# 10 qbittorrent.exe      0x00007ff66f543833 QApplicationPrivate::notify_helper()
# 11 qbittorrent.exe      0x00007ff66f543340 QApplication::notify()
# 12 qbittorrent.exe      0x00007ff66f023670 Application::notify(receiver, event)[ app\application.cpp : 630 ]
# 13 qbittorrent.exe      0x00007ff66f9fdbcc QCoreApplication::notifyInternal2()
# 14 qbittorrent.exe      0x00007ff66f9fe75a QCoreApplicationPrivate::sendPostedEvents()
# 15 qbittorrent.exe      0x00007ff66f47f387 QWindowsGuiEventDispatcher::sendPostedEvents()
# 16 qbittorrent.exe      0x00007ff66fae4f9d qt_internal_proc()
# 17 USER32.dll           0x00007ffbf9e27281 CallWindowProcW()
# 18 USER32.dll           0x00007ffbf9e26c53 DispatchMessageW()
# 19 qbittorrent.exe      0x00007ff66fae61c4 QEventDispatcherWin32::processEvents()
# 20 qbittorrent.exe      0x00007ff66f47f361 QWindowsGuiEventDispatcher::processEvents()
# 21 qbittorrent.exe      0x00007ff66fae99a1 QEventLoop::exec()
# 22 qbittorrent.exe      0x00007ff66f9fe127 QCoreApplication::exec()
# 23 qbittorrent.exe      0x00007ff66f023340 Application::exec(params)[ app\application.cpp : 575 ]
# 24 qbittorrent.exe      0x00007ff66f02f337 main(argc, argv, argv)[ app\main.cpp : 253 ]
# 25 qbittorrent.exe      0x00007ff66fbb4d53 WinMain()
# 26 qbittorrent.exe      0x00007ff66fbb79b2 __scrt_common_main_seh()[ d:\agent\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl : 288 ]
# 27 KERNEL32.DLL         0x00007ffbf8704034 BaseThreadInitThunk()
# 28 ntdll.dll            0x00007ffbfb2e3691 RtlUserThreadStart()
fedarovich commented 4 years ago

Hm, according to the stacktrace, it looks like qBittorrent tries to call an abstract method somewhere in their code. So it seems to be their bug. But I can try to investigate this issue too.

Do you know which library method or API call causes this crash?

devonuto commented 4 years ago

Unfortunately it's impossible to tell as it doesn't trigger an error in my code which I can capture, and only seems to happen when called from the qBitTorrent application itself. Error doesn't ever seem to occur when stepping through.

devonuto commented 4 years ago

I can tell you that these are the only methods I use (tried both API versions 1 and 2):

`using (QBittorrentClient qBitClient = new QBittorrentClient(new Uri("http://192.168.1.2:8080/"))) { try { await qBitClient.LoginAsync(Globals.TorrentUser, Globals.TorrentPw); } catch { return; }

foreach (TorrentInfo torrent in qBitClient.GetTorrentListAsync().Result)
{
    if (torrent.Progress < 1)
    {
        continue;
    }
    // Check if all pieces downloaded successfully.
    IReadOnlyList<TorrentPieceState> pieces = await qBitClient.GetTorrentPiecesStatesAsync(torrent.Hash);
    if (pieces.Any(piece => piece.ToString() != "Downloaded"))
    {
        await qBitClient.RecheckAsync(new List<string> {torrent.Hash});
        continue;
    }
    foreach (TorrentContent file in qBitClient.GetTorrentContentsAsync(torrent.Hash).Result)
    {
        // Do stuff
    }
    await qBitClient.PauseAsync(torrent.Hash);

    // Other calls made
    await qBitClient.ResumeAsync(torrent.Hash);
    await qBitClient.DeleteAsync(torrent.Hash, true)
}
await qBitClient.LogoutAsync();
qBitClient.Dispose();

}`

fedarovich commented 4 years ago

Ok, I can see some issues with your code.

The main issue, is that you use RecheckAsync for the incompleted torrent. The purpose of this method is to check, whether the completed torrent was correctly downloaded. It calculates the hash of each downloaded part and compares it with the hash stored in the .torrent file. If the hash of some part is incorrect, qBittorrent will redownload it. So it is likely that there is a bug in qBittorrent that causes it crash if you try to recheck the incompleted torrent.

I would also recommend against using Task.Result property (e.g. qBitClient.GetTorrentListAsync().Result) as it can cause deadlocks in some case. You can use await qBitClient.GetTorrentListAsync() instead.

If you explain a little bit more what you want to achieve, I can probably help you.

devonuto commented 4 years ago

I did have the call to check if progress is completed: if (torrent.Progress < 1) { continue; } before calling the GetTorrentPiecesStatesAsync. I have moved the paused call on the torrent to above that code, and using await calls. I'll see how that goes.

devonuto commented 4 years ago

Tried the above still crashing. Going to try removing the pieces check altogether and see if that helps.

devonuto commented 4 years ago

Managed to capture an exception on IReadOnlyList<TorrentInfo> torrents = await qBitClient.GetTorrentListAsync(); bool hasTorrent = torrents.Count > 0;

EXCEPTION: System.InvalidCastException: Unable to cast object of type 'System.Numerics.BigInteger' to type 'System.IConvertible'. at System.Convert.ToInt64(Object value) at QBittorrent.Client.Converters.SecondsToTimeSpanConverter.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, Object existingValue) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, String id) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent) at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType) at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings) at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings) at QBittorrent.Client.QBittorrentClient.<>c__DisplayClass22_0.<gExecuteAsync|0>d.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at ProcessTorrents.ProcessTorrents.d13.MoveNext() EXCEPTION INNER EXCEPTION:

fedarovich commented 4 years ago

Though it is not the crash of qBittorrent itself, it still looks interesting. Probably, qBittorrent team has broken API compatibility once again. I will investigate it.

devonuto commented 4 years ago

I wonder if this is possibly caused by the new Newtonsoft.json (12.0.2)

devonuto commented 2 years ago

Don't use this anymore