Rss

MonoTouch + SQLite = SIGSEGV Crash

If you found this post by of the first line of the stack trace at the bottom of the page, then you might find some help here. I wrestled with this very issue for a while and found a real answer that didn’t involve changing from ADO.NET to sqlite-net or some other framework.

In case you’re not sure, I’m talking about using SQLite in your MonoTouch application and the app just spontaneously dies from a SIGSEGV. If that’s happening to you, you’re in the right place.

My app is fairly busy on the DB end. It uses a hand-spun model layer that’s based off ADO.NET so that it could be (to some extent) platform independent. My app could then share a model with the server and have different low level implementations with the same codebase. I’m sure there are a gazillion ways to do that better or different, but it’s the way I went.

Everything went fine on the server. I use SQL server on the server side, and I did most of the development of the framework while testing against the server side. When I got to the mobile side, I expected there to be some issues. The issues cleaned up quickly and I was able to start testing in a short amount of time.

Now enter our pal SIGSEGV. I’m new to MonoTouch, though not new to iOS, but I was surprised at how my application just died. I’m debugging, and I’ve seen it catch exceptions, but here it’s just dying. It looks like an OS level exception, and the exception appears to be occurring in SQLite.

So…Search the web! Hmmm, yes, I see…there are a lot of people out there running into this, but not many good answers. No, I don’t want to change the framework I’m using. This busy_timeout thing sounds interesting, why did Mono drop it from the current implementation?? Yes, I do know about writing software. No, I’m not a complete idiot.

Anyway, you probably already know about all of that poop. How about the interesting stuff? How does one get around this highly annoying problem?

Simple: do what Mono tells you to do. They don’t come right out and say it, but on the SQLite page they demonstrate it:

 using System;
 using System.Data;
 using Mono.Data.SqliteClient;

 public class Test
 {
    public static void Main(string[] args)
    {
       string connectionString = "URI=file:SqliteTest.db";
       IDbConnection dbcon;
       dbcon = (IDbConnection) new SqliteConnection(connectionString);
       dbcon.Open();
       IDbCommand dbcmd = dbcon.CreateCommand();
       // requires a table to be created named employee
       // with columns firstname and lastname
       // such as,
       //        CREATE TABLE employee (
       //           firstname varchar(32),
       //           lastname varchar(32));
       string sql =
          "SELECT firstname, lastname " +
          "FROM employee";
       dbcmd.CommandText = sql;
       IDataReader reader = dbcmd.ExecuteReader();
       while(reader.Read()) {
            string FirstName = reader.GetString (0);
            string LastName = reader.GetString (1);
            Console.WriteLine("Name: " +
                FirstName + " " + LastName);
       }
       // clean up
       reader.Close();
       reader = null;
       dbcmd.Dispose();
       dbcmd = null;
       dbcon.Close();
       dbcon = null;
    }
 }

Its so subtle you almost miss it. I know I missed it. I don’t know how many times I looked at this page, I never noticed how the framework developers recommended I explicitly dispose my commands. I was sure to close my readers and connections, but I just left the commands to the garbage collector.

But that makes all the difference. At this stage my app is only a NUnitLite test runner, and every time I touch “Run All”, it’s pretty much a guaranteed SIGSEGV. Sometimes I might get one or two successful runs, but never three.

In fact, just for the sake of very clearly illustrating the issue, I did some testing. I ran my test app 20 times from the MonoTouch debugger on the iPhone Simulator and recorded the results. Every single time it crashed. Sometimes I could run my test suite successfully once or twice, but by the third time (and usually on the first time), it would crash.

Then, I made the change. Just as the Mono developer did in the sample above, I went to every place I executed a command, and after the connection was done being used (either by ExecuteScalar, by ExecuteNoQuery, or by ExecuteReader and subsequently closing the reader), I manually called Dispose() on the command. I then performed the same testing. I ran the program 20 times, only this time I ran my test suite at least 3 times each time I launched the app, and usually more. I ran it 15 times once. But through it all, the application never crashed. I never got the signal.

I did some pretty crazy stuff before I got to this solution. One of the things gave me some success but really slowed my app down: I preceded each usage of the connection by a Thread.Sleep(10), such that each time the database was used, the app would wait a bit to let “something” happen. This actually worked, and the higher the number the less likely the app was to receive the SIGSEGV signal. However, it was clear that there was a lot of time wasted on this, that it wasn’t 100%, and that I still didn’t know what was actually causing the problem.

Now in retrospect, what was probably happening was the garbage collector was cleaning up unused references to commands, thereby Dispose()-ing them and releasing whatever locks they had on tables.

Chances are, if there had been a busy_timeout available, I would have used that and never known there was any contention under the hood that was relying on the garbage collector to free things up.

SO, just to reiterate and make the solution crystal clear: to resolve the issue with  receiving a SIGSEGV when Mono calls Mono.Data.Sqlite.UnsafeNativeMethods.sqlite3_prepare, make sure that every time you execute a SqliteCommand instance, you explicitly dispose that command (after any reader has finished and been closed) by calling the Dispose() method on it.

Thanks for reading!

UPDATE: .NET makes it easy to do this right. If you use using blocks, the call to Dispose is made for you! Check out this page for more info.

Stacktrace:

at (wrapper managed-to-native) Mono.Data.Sqlite.UnsafeNativeMethods.sqlite3_prepare (intptr,intptr,int,intptr&,intptr&) <IL 0x0002a, 0xffffffff>
at Mono.Data.Sqlite.SQLite3.Prepare (Mono.Data.Sqlite.SqliteConnection,string,Mono.Data.Sqlite.SqliteStatement,uint,string&) [0x00044] in /Developer/MonoTouch/Source/mono/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLite3.cs:268
at Mono.Data.Sqlite.SqliteCommand.BuildNextCommand () [0x00019] in /Developer/MonoTouch/Source/mono/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteCommand.cs:230
at Mono.Data.Sqlite.SqliteCommand.GetStatement (int) [0x0000b] in /Developer/MonoTouch/Source/mono/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteCommand.cs:264
at (wrapper remoting-invoke-with-check) Mono.Data.Sqlite.SqliteCommand.GetStatement (int) <IL 0x00039, 0xffffffff>
at Mono.Data.Sqlite.SqliteDataReader.NextResult () [0x000cc] in /Developer/MonoTouch/Source/mono/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteDataReader.cs:896
at Mono.Data.Sqlite.SqliteDataReader..ctor (Mono.Data.Sqlite.SqliteCommand,System.Data.CommandBehavior) [0x00051] in /Developer/MonoTouch/Source/mono/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteDataReader.cs:89
at (wrapper remoting-invoke-with-check) Mono.Data.Sqlite.SqliteDataReader..ctor (Mono.Data.Sqlite.SqliteCommand,System.Data.CommandBehavior) <IL 0x00021, 0xffffffff>
at Mono.Data.Sqlite.SqliteCommand.ExecuteReader (System.Data.CommandBehavior) [0x00006] in /Developer/MonoTouch/Source/mono/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteCommand.cs:539
at Mono.Data.Sqlite.SqliteCommand.ExecuteDbDataReader (System.Data.CommandBehavior) [0x00000] in /Developer/MonoTouch/Source/mono/mcs/class/Mono.Data.Sqlite/Mono.Data.Sqlite_2.0/SQLiteCommand.cs:527
at System.Data.Common.DbCommand.ExecuteReader () [0x00000] in /Developer/MonoTouch/Source/mono/mcs/class/System.Data/System.Data.Common/DbCommand.cs:123
at (wrapper remoting-invoke-with-check) System.Data.Common.DbCommand.ExecuteReader () <IL 0x00038, 0xffffffff>
at HydriteMobileSurveys.Model.Base.DataContext`1.CommandToTable (System.Data.Common.DbCommand) [0x00000] in /Users/aaron/Documents/Software Development/Hydrite Mobile Surveys/Branches/Hoss/Model/Base/DataContext.cs:961
at HydriteMobileSurveys.Model.Base.DataContext`1.FetchLoadObject (T,System.Guid,System.Type) [0x00000] in /Users/aaron/Documents/Software Development/Hydrite Mobile Surveys/Branches/Hoss/Model/Base/DataContext.cs:938
at HydriteMobileSurveys.Model.Base.DataContext`1.FetchLoadObject (T,System.Guid,System.Type) [0x00042] in /Users/aaron/Documents/Software Development/Hydrite Mobile Surveys/Branches/Hoss/Model/Base/DataContext.cs:945
at HydriteMobileSurveys.Model.Base.DataContext`1.FetchLoadObject (T,System.Guid,System.Type) [0x00042] in /Users/aaron/Documents/Software Development/Hydrite Mobile Surveys/Branches/Hoss/Model/Base/DataContext.cs:945
at HydriteMobileSurveys.Model.Base.DataContext`1.Fetch (System.Guid) [0x00060] in /Users/aaron/Documents/Software Development/Hydrite Mobile Surveys/Branches/Hoss/Model/Base/DataContext.cs:888
at HydriteMobileSurveys.Model.Base.DataContext`1.Fetch (System.Collections.Generic.IEnumerable`1<System.Guid>) [0x00019] in /Users/aaron/Documents/Software Development/Hydrite Mobile Surveys/Branches/Hoss/Model/Base/DataContext.cs:851
at HydriteMobileSurveys.Model.Base.DataContext`1.FetchAll (System.Type) [0x0006a] in /Users/aaron/Documents/Software Development/Hydrite Mobile Surveys/Branches/Hoss/Model/Base/DataContext.cs:817
at HydriteMobileSurveys.Model.Base.DataContext`1.FetchAll<TObject> () [0x00000] in /Users/aaron/Documents/Software Development/Hydrite Mobile Surveys/Branches/Hoss/Model/Base/DataContext.cs:799
at DeviceTests.Big_Model_Tests.TestGetObjects () [0x0005d] in /Users/aaron/Documents/Software Development/Hydrite Mobile Surveys/Branches/Hoss/DeviceTests/Model_Tests.cs:187
at (wrapper runtime-invoke) object.runtime_invoke_void__this__ (object,intptr,intptr,intptr) <IL 0x0004e, 0xffffffff>
at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&) <IL 0x00030, 0xffffffff>
at System.Reflection.MonoMethod.Invoke (object,System.Reflection.BindingFlags,System.Reflection.Binder,object[],System.Globalization.CultureInfo) [0x000c0] in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Reflection/MonoMethod.cs:234
at System.Reflection.MethodBase.Invoke (object,object[]) [0x00000] in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Reflection/MethodBase.cs:98
at NUnit.Framework.Internal.Reflect.InvokeMethod (System.Reflection.MethodInfo,object,object[]) [0x00006] in /Developer/MonoTouch/Source/NUnitLite/NUnitLite-0.7.0/src/framework/Internal/Reflect.cs:215
at NUnit.Framework.Internal.Commands.TestMethodCommand.Execute (NUnit.Framework.Internal.TestExecutionContext) [0x00000] in /Developer/MonoTouch/Source/NUnitLite/NUnitLite-0.7.0/src/framework/Internal/Commands/TestMethodCommand.cs:53
at NUnit.Framework.Internal.Commands.SetUpTearDownCommand.Execute (NUnit.Framework.Internal.TestExecutionContext) [0x00007] in /Developer/MonoTouch/Source/NUnitLite/NUnitLite-0.7.0/src/framework/Internal/Commands/SetUpTearDownCommand.cs:79
at NUnit.Framework.Internal.Commands.CommandRunner.Execute (NUnit.Framework.Internal.Commands.TestCommand) [0x00064] in /Developer/MonoTouch/Source/NUnitLite/NUnitLite-0.7.0/src/framework/Internal/Commands/CommandRunner.cs:43
at NUnit.Framework.Internal.Commands.CommandRunner.RunChildCommands (NUnit.Framework.Internal.Commands.TestSuiteCommand,NUnit.Framework.Internal.TestExecutionContext) [0x00033] in /Developer/MonoTouch/Source/NUnitLite/NUnitLite-0.7.0/src/framework/Internal/Commands/CommandRunner.cs:118
at NUnit.Framework.Internal.Commands.CommandRunner.ExecuteSuiteCommand (NUnit.Framework.Internal.Commands.TestSuiteCommand,NUnit.Framework.Internal.TestExecutionContext) [0x00029] in /Developer/MonoTouch/Source/NUnitLite/NUnitLite-0.7.0/src/framework/Internal/Commands/CommandRunner.cs:90
at NUnit.Framework.Internal.Commands.CommandRunner.Execute (NUnit.Framework.Internal.Commands.TestCommand) [0x00056] in /Developer/MonoTouch/Source/NUnitLite/NUnitLite-0.7.0/src/framework/Internal/Commands/CommandRunner.cs:35
at NUnit.Framework.Internal.Commands.CommandRunner.RunChildCommands (NUnit.Framework.Internal.Commands.TestSuiteCommand,NUnit.Framework.Internal.TestExecutionContext) [0x00033] in /Developer/MonoTouch/Source/NUnitLite/NUnitLite-0.7.0/src/framework/Internal/Commands/CommandRunner.cs:118
at NUnit.Framework.Internal.Commands.CommandRunner.ExecuteSuiteCommand (NUnit.Framework.Internal.Commands.TestSuiteCommand,NUnit.Framework.Internal.TestExecutionContext) [0x00029] in /Developer/MonoTouch/Source/NUnitLite/NUnitLite-0.7.0/src/framework/Internal/Commands/CommandRunner.cs:90
at NUnit.Framework.Internal.Commands.CommandRunner.Execute (NUnit.Framework.Internal.Commands.TestCommand) [0x00056] in /Developer/MonoTouch/Source/NUnitLite/NUnitLite-0.7.0/src/framework/Internal/Commands/CommandRunner.cs:35
at NUnit.Framework.Internal.Commands.CommandRunner.RunChildCommands (NUnit.Framework.Internal.Commands.TestSuiteCommand,NUnit.Framework.Internal.TestExecutionContext) [0x00033] in /Developer/MonoTouch/Source/NUnitLite/NUnitLite-0.7.0/src/framework/Internal/Commands/CommandRunner.cs:118
at NUnit.Framework.Internal.Commands.CommandRunner.ExecuteSuiteCommand (NUnit.Framework.Internal.Commands.TestSuiteCommand,NUnit.Framework.Internal.TestExecutionContext) [0x00029] in /Developer/MonoTouch/Source/NUnitLite/NUnitLite-0.7.0/src/framework/Internal/Commands/CommandRunner.cs:90
at NUnit.Framework.Internal.Commands.CommandRunner.Execute (NUnit.Framework.Internal.Commands.TestCommand) [0x00056] in /Developer/MonoTouch/Source/NUnitLite/NUnitLite-0.7.0/src/framework/Internal/Commands/CommandRunner.cs:35
at MonoTouch.NUnit.UI.TouchRunner.Run (NUnit.Framework.Internal.Test) [0x00022] in /Developer/MonoTouch/Source/Touch.Unit/NUnitLite/TouchRunner/TouchRunner.cs:444
at MonoTouch.NUnit.UI.TouchRunner.Run () [0x00011] in /Developer/MonoTouch/Source/Touch.Unit/NUnitLite/TouchRunner/TouchRunner.cs:148
at MonoTouch.Dialog.StringElement.Selected (MonoTouch.Dialog.DialogViewController,MonoTouch.UIKit.UITableView,MonoTouch.Foundation.NSIndexPath) [0x0000b] in /Developer/MonoTouch/Source/MonoTouch.Dialog/MonoTouch.Dialog/Elements.cs:689
at MonoTouch.Dialog.DialogViewController.Selected (MonoTouch.Foundation.NSIndexPath) [0x00029] in /Developer/MonoTouch/Source/MonoTouch.Dialog/MonoTouch.Dialog/DialogViewController.cs:518
at MonoTouch.Dialog.DialogViewController/Source.RowSelected (MonoTouch.UIKit.UITableView,MonoTouch.Foundation.NSIndexPath) [0x00019] in /Developer/MonoTouch/Source/MonoTouch.Dialog/MonoTouch.Dialog/DialogViewController.cs:364
at (wrapper runtime-invoke) <Module>.runtime_invoke_void__this___object_object (object,intptr,intptr,intptr) <IL 0x0005a, 0xffffffff>
at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication.UIApplicationMain (int,string[],intptr,intptr) <IL 0x0009f, 0xffffffff>
at MonoTouch.UIKit.UIApplication.Main (string[],string,string) [0x0004c] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:38
at DeviceTests.Application.Main (string[]) [0x00000] in /Users/aaron/Documents/Software Development/Hydrite Mobile Surveys/Branches/Hoss/DeviceTests/Main.cs:17
at (wrapper runtime-invoke) <Module>.runtime_invoke_void_object (object,intptr,intptr,intptr) <IL 0x00050, 0xffffffff>

Native stacktrace:

0 DeviceTests 0x000915ec mono_handle_native_sigsegv + 284
1 DeviceTests 0x00006378 mono_sigsegv_signal_handler + 248
2 libsystem_c.dylib 0x9278d86b _sigtramp + 43
3 ??? 0xffffffff 0x0 + 4294967295
4 libsqlite3.dylib 0x051e5f83 sqlite3ExprAlloc + 227
5 libsqlite3.dylib 0x051e5a7b selectExpander + 2859
6 libsqlite3.dylib 0x051e27f8 sqlite3WalkSelect + 104
7 libsqlite3.dylib 0x051e1b2c sqlite3SelectPrep + 76
8 libsqlite3.dylib 0x051ce468 sqlite3Select + 424
9 libsqlite3.dylib 0x051c2cfd yy_reduce + 8301
10 libsqlite3.dylib 0x051c0555 sqlite3Parser + 245
11 libsqlite3.dylib 0x051874cc sqlite3RunParser + 396
12 libsqlite3.dylib 0x0520611a sqlite3Prepare + 634
13 libsqlite3.dylib 0x0518666e sqlite3LockAndPrepare + 270
14 libsqlite3.dylib 0x05186225 sqlite3_prepare + 53
15 ??? 0x12f3d167 0x0 + 317968743
16 ??? 0x12f3c4a0 0x0 + 317965472
17 ??? 0x12f3bfe1 0x0 + 317964257
18 ??? 0x12f3bd14 0x0 + 317963540
19 ??? 0x12f3bc94 0x0 + 317963412
20 ??? 0x12f3b674 0x0 + 317961844
21 ??? 0x12f3b1c6 0x0 + 317960646
22 ??? 0x12f3b060 0x0 + 317960288
23 ??? 0x12f3a46c 0x0 + 317957228
24 ??? 0x12feef90 0x0 + 318697360
25 ??? 0x12feef4a 0x0 + 318697290
26 ??? 0x12feeee4 0x0 + 318697188
27 ??? 0x12ff04c8 0x0 + 318702792
28 ??? 0x12ff0308 0x0 + 318702344
29 ??? 0x12ff0444 0x0 + 318702660
30 ??? 0x12ff0444 0x0 + 318702660
31 ??? 0x12feea38 0x0 + 318695992
32 ??? 0x13bafb4c 0x0 + 331021132
33 ??? 0x12ff7efc 0x0 + 318734076
34 ??? 0x13b64d80 0x0 + 330714496
35 ??? 0x13bb5e88 0x0 + 331046536
36 ??? 0x0b7dfb98 0x0 + 192805784
37 DeviceTests 0x0000a732 mono_jit_runtime_invoke + 722
38 DeviceTests 0x0016c1ce mono_runtime_invoke + 126
39 DeviceTests 0x00172a18 mono_runtime_invoke_array + 1544
40 DeviceTests 0x00117742 ves_icall_InternalInvoke + 1090
41 ??? 0x12f2062d 0x0 + 317851181
42 ??? 0x12f20228 0x0 + 317850152
43 ??? 0x12f1feba 0x0 + 317849274
44 ??? 0x12f1fcdc 0x0 + 317848796
45 ??? 0x12f60c98 0x0 + 318114968
46 ??? 0x12f1f960 0x0 + 317847904
47 ??? 0x12f1d86a 0x0 + 317839466
48 ??? 0x12f1f0c4 0x0 + 317845700
49 ??? 0x12f1e738 0x0 + 317843256
50 ??? 0x12f1d834 0x0 + 317839412
51 ??? 0x12f1f0c4 0x0 + 317845700
52 ??? 0x12f1e738 0x0 + 317843256
53 ??? 0x12f1d834 0x0 + 317839412
54 ??? 0x12f1f0c4 0x0 + 317845700
55 ??? 0x12f1e738 0x0 + 317843256
56 ??? 0x12f1d834 0x0 + 317839412
57 ??? 0x12f1bd20 0x0 + 317832480
58 ??? 0x12ea8ebc 0x0 + 317361852
59 ??? 0x12ea8e16 0x0 + 317361686
60 ??? 0x12ea8dbf 0x0 + 317361599
61 ??? 0x12ea8b4e 0x0 + 317360974
62 ??? 0x12ea8ccd 0x0 + 317361357
63 DeviceTests 0x0000a732 mono_jit_runtime_invoke + 722
64 DeviceTests 0x0016c1ce mono_runtime_invoke + 126
65 DeviceTests 0x0020eb88 monotouch_trampoline + 3688
66 UIKit 0x027d18d5 -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] + 1194
67 UIKit 0x027d1b3d -[UITableView _userSelectRowAtPendingSelectionIndexPath:] + 201
68 Foundation 0x01957e83 __NSFireDelayedPerform + 380
69 CoreFoundation 0x012fe376 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 22
70 CoreFoundation 0x012fde06 __CFRunLoopDoTimer + 534
71 CoreFoundation 0x012e5a82 __CFRunLoopRun + 1810
72 CoreFoundation 0x012e4f44 CFRunLoopRunSpecific + 276
73 CoreFoundation 0x012e4e1b CFRunLoopRunInMode + 123
74 GraphicsServices 0x04d0c7e3 GSEventRunModal + 88
75 GraphicsServices 0x04d0c668 GSEventRun + 104
76 UIKit 0x0272265c UIApplicationMain + 1211
77 ??? 0x0b7e6dcd 0x0 + 192835021
78 ??? 0x0f3ebdf8 0x0 + 255770104
79 ??? 0x0f3eb190 0x0 + 255766928
80 ??? 0x0f3eb2e6 0x0 + 255767270
81 DeviceTests 0x0000a732 mono_jit_runtime_invoke + 722
82 DeviceTests 0x0016c1ce mono_runtime_invoke + 126
83 DeviceTests 0x00170354 mono_runtime_exec_main + 420
84 DeviceTests 0x00175745 mono_runtime_run_main + 725
85 DeviceTests 0x00067985 mono_jit_exec + 149
86 DeviceTests 0x00203e92 main + 1986
87 DeviceTests 0x00003735 start + 53

=================================================================
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries
used by your application.
=================================================================

Comments (9)

  1. MarcVit

    Thanks so much for bringing that up!! That just saved my life. It was in the docs, but they should’ve highlighted the importance of this implementation detail.

    • Right?? I spent nearly a week fighting with this when I was first starting out with MonoTouch. Not being a full-on C#/.NET guy, I didn’t know about IDisposable or the right “recipes” for ADO.NET. A little extra stress on the importance of disposing those objects would have helped greatly. I’m glad you weren’t held up for too long.

  2. movsho

    Thank you so very very much! I ran into it and after trying all sorts of voodoo tries, I ran into this article.
    Thanks! 🙂

  3. Aaron, is it possible to share a code snipper to handle multiple sqlite connections from multiple threads doing write and read operations? It would really save my day.

    • Hello there, thanks for reading! I did wrestle with this a bit. In the end, I settled on storing a single SQLiteConnection instance in a shared connection variable, and then putting a lock(connection) {} block around each piece of code that executes queries using the connection. There is a tiny bit of a performance hit by doing it this way, but it’s far better than having the application crash!

      Thanks again for reading!
      Aaron

  4. Jim

    I was pulling my hair out trying to figure this out…
    Thanks so much!

  5. Alejandro Winkler

    Thanks so much! Finally an answer.

  6. Ian

    Oh for gods sake… Thanks for this blog post, this fixed my issue.

  7. Fabio Lessa

    You are a life saver! Thank you.

Leave a Reply

Your email address will not be published. Required fields are marked *