2

Just recently my app has been inconsistently throwing a variety of exceptions like these:

Unable to cast object of type 'System.Threading.Thread' to type 'System.Transactions.SafeIUnknown'. 

Unable to cast object of type 'System.Diagnostics.Process' to type 'System.Transactions.SafeIUnknown'.    

Unable to cast object of type 'System.Drawing.SolidBrush' to type 'System.Transactions.SafeIUnknown'.

Unable to cast object of type 'System.Threading.Thread' to type 'System.Xml.Linq.XNamespace'.    

(the last one might be a secondary effect of the others)

Stack traces of some of these are pasted below but note that all of them have these 3 final steps:

   at System.Transactions.Transaction.JitSafeGetContextTransaction(ContextData contextData)
   at System.Transactions.Transaction.FastGetTransaction(TransactionScope currentScope, ContextData contextData, Transaction& contextTransaction)
   at System.Transactions.Transaction.get_Current()

Observations - these exceptions:

  1. Occur frequently, but not all the time (50%?)
  2. Don't always produce the same exception when they do occur
  3. Have no obvious connection to the code in my app which leads to them
  4. Are reproducible on various PCs
  5. Can't be found exactly in any searches I've done

The app is a set of C# DLLs called via COM from older VB6 code. However that has been in place for years, and we haven't changed anything about how that is done recently.

It is not a multi-threaded app, so the casts involving System.Threading.Thread seem out of place. There was one third party module that used threads, but we took it out as an experiment: doing so did not resolve the problem but seemed to reduce the frequency of errors. This leads me to think its some kind of resource clobbering problem.


I am looking for advice such as:

  • If you've actually seen this pattern of exceptions before, what was the cause?

  • Recommendations for how to go about debugging the root issue


Example stack traces:

Unable to cast object of type 'System.Threading.Thread' to type 'System.Transactions.SafeIUnknown'.    
   at System.Transactions.Transaction.JitSafeGetContextTransaction(ContextData contextData)    
   at System.Transactions.Transaction.FastGetTransaction(TransactionScope currentScope, ContextData contextData, Transaction& contextTransaction)    
   at System.Transactions.Transaction.get_Current()    
   at System.Data.SQLite.SQLiteConnection.Open()    
   at Company.ABC.WrappedDbConnection.Open()    
   at Company.ABC.Program.DoSomething()

and

Unable to cast object of type 'System.Threading.Thread' to type 'System.Transactions.SafeIUnknown'.
   at System.Transactions.Transaction.JitSafeGetContextTransaction(ContextData contextData)
   at System.Transactions.Transaction.FastGetTransaction(TransactionScope currentScope, ContextData contextData, Transaction& contextTransaction)
   at System.Transactions.Transaction.get_Current()
   at System.Data.Common.ADP.IsSysTxEqualSysEsTransaction()
   at System.Data.Common.ADP.NeedManualEnlistment()
   at System.Data.OleDb.OleDbConnection.Open()
   at Company.ABC.WrappedDbConnection.Open()    
   at Company.ABC.Program.DoSomething()

and

System.InvalidCastException: Unable to cast object of type 'System.Drawing.SolidBrush' to type 'System.Transactions.SafeIUnknown'.
   at System.Transactions.Transaction.JitSafeGetContextTransaction(ContextData contextData)
   at System.Transactions.Transaction.FastGetTransaction(TransactionScope currentScope, ContextData contextData, Transaction& contextTransaction)
   at System.Transactions.Transaction.get_Current()
   at System.Data.Common.ADP.IsSysTxEqualSysEsTransaction()
   at System.Data.Common.ADP.NeedManualEnlistment()
   at System.Data.OleDb.OleDbConnection.Open()

   at Company.ABC.WrappedDbConnection.Open()    
   at Company.ABC.Program.DoSomething()

EDIT:

I tried looking into this using windbg (with SOS extensions for managed code).

It was able to break on the exception. Here is the stack trace from that point:

0:000> !DumpStack
OS Thread Id: 0x2d44 (0)
Current frame: KERNELBASE!RaiseException+0x62
ChildEBP RetAddr  Caller, Callee
0019b658 750325f2 KERNELBASE!RaiseException+0x62, calling ntdll!RtlRaiseException
0019b688 774a7310 ntdll!RtlFreeHeap+0x1e0, calling ntdll!RtlFreeHeap+0x560
0019b69c 70a9f09f clr+0xf09f, calling KERNEL32!TlsGetValue
0019b6ac 70b928f1 clr!GetMetaDataPublicInterfaceFromInternal+0x9741, calling KERNELBASE!RaiseException
0019b6ec 70aae56d clr!LogHelp_NoGuiOnAssert+0x2add, calling clr!LogHelp_NoGuiOnAssert+0x2a92
0019b6f8 70aae50f clr!LogHelp_NoGuiOnAssert+0x2a7f
0019b748 70bfc1a2 clr!CreateApplicationContext+0x1c2d2, calling clr!GetMetaDataPublicInterfaceFromInternal+0x95b6
0019b77c 70e3d369 clr!CreateHistoryReader+0x72069, calling clr!CreateApplicationContext+0x1c279
0019b7dc 70bc6a59 clr!PreBindAssemblyEx+0x10959, calling clr+0xf7b0
0019b80c 09364844 (MethodDesc 08e0d5b4 +0x4c System.Transactions.Transaction.JitSafeGetContextTransaction(System.Transactions.ContextData)), calling clr!LogHelp_TerminateOnAssert+0x7f00
0019b83c 09362f2f (MethodDesc 08e0d5e4 +0x9f System.Transactions.Transaction.FastGetTransaction(System.Transactions.TransactionScope, System.Transactions.ContextData, System.Transactions.Transaction ByRef)), calling (MethodDesc 08e0d5b4 +0 System.Transactions.Transaction.JitSafeGetContextTransaction(System.Transactions.ContextData))
0019b850 09362510 (MethodDesc 08e0d5fc +0x78 System.Transactions.Transaction.get_Current()), calling (MethodDesc 08e0d5e4 +0 System.Transactions.Transaction.FastGetTransaction(System.Transactions.TransactionScope, System.Transactions.ContextData, System.Transactions.Transaction ByRef))
0019b86c 08ddf67a (MethodDesc 08e04c6c +0x14b2 System.Data.SQLite.SQLiteConnection.Open()), calling (MethodDesc 08e0d5fc +0 System.Transactions.Transaction.get_Current())
0019ba70 08ddd8ea (MethodDesc 08e058b0 +0x22 Company.ABC.WrappedDbConnection.Open()), calling 0dfce43a

This syncs with the CLR exception stacks that are above.

The thing I was mainly looking to ascertain is whether there is heap "corruption". But apparently not:

0:000> !VerifyHeap
No heap corruption detected.

So I think that rules out any of our native code (or 3rd party) simply wreaking havoc. But still no actual understanding of this problem. It looks more like a bug in the database layer to me.


This has been submitted as an issue here: https://developercommunity2.visualstudio.com/t/Unable-to-cast-object-of-type-varies/1241378

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
  • This is very strange. Looks like a low-level issue with the framework or something like that. But usually these don't produce "plausible" exceptions. Source of JitSafeGetContextTransaction is here: https://referencesource.microsoft.com/#system.transactions/System/Transactions/Transaction.cs,136 seems weak references are wrong. Check your .NET setup is ok, or what 3rd parties live in your process. – Simon Mourier Oct 27 '20 at 19:48
  • @SimonMourier thanks for the reference. I don't think this is environmental since it occurs on multiple PCs. (re: .NET setup)\ – StayOnTarget Oct 29 '20 at 19:02
  • Maybe you have native 3rd parties loaded into your process that mess up with its memory? You can check all loaded dlls using a tool such as procexp from sysinternasl. – Simon Mourier Oct 29 '20 at 19:57
  • @SimonMourier I share that suspicion; VB6 and some DLLs are all native in the process, I'm working on that angle. – StayOnTarget Oct 29 '20 at 20:09
  • @SimonMourier well I found a workaround, but I don't really understand it. – StayOnTarget Oct 30 '20 at 15:04
  • I have recently noticed this same issue has appeared from nowhere. What version of .NET Framework are you using? – Paul Hunt Nov 02 '20 at 16:51
  • @PaulHunt it also appeared from nowhere for us - in code that had been working for years. The project is using .NET 472 – StayOnTarget Nov 02 '20 at 16:52
  • @UuDdLrLrSs How strange. Our project is v4.6.1, I was wondering if there had been an update of some kind recently and whether I should try upgrading to v4.6.2 but that doesn't sound like it will help. – Paul Hunt Nov 02 '20 at 16:54
  • @PaulHunt in your case is the exception also occurring in System.Transactions? – StayOnTarget Nov 02 '20 at 16:55
  • Yes, it is and it always appears to happen when Entity Framework is trying to open a connection. This is just one example from today..... The underlying provider failed on Open. ---> System.InvalidCastException: Unable to cast object of type 'Microsoft.Win32.SafeHandles.SafeBCryptAlgorithmHandle' to type 'System.Transactions.SafeIUnknown'. – Paul Hunt Nov 02 '20 at 16:58
  • @PaulHunt I'm just curious if you ever found a solution? – StayOnTarget Feb 09 '21 at 20:28

1 Answers1

0

I don't think this is a perfect "root cause" answer; but it is the workaround which seems to have succeeded in avoiding the exceptions.

The code in question is used to create DB connection, execute a query, then teardown the connection.

The exact change was to go from:

 public class DatabaseAccessXYZ
 {
    IDbConnection connection;

     void Execute<T>(string sql, T item)
     {
         using (connection = ConnectionManagerXYZ.GetConnection(Filespec))
         {
             connection.Open();
             connection.Execute(sql, item);
         }
         ...

to this instead:

 public class DatabaseAccessXYZ
 {
     void Execute<T>(string sql, T item)
     {
         using (var connection = ConnectionManagerXYZ.GetConnection(Filespec))
         {
             connection.Open();
             connection.Execute(sql, item);
         }
         ...

So the scope of the connection variable was changed. It was only used in this one place anyway, it did not need to be a class-level field. Now the exception no longer occurs.

As far as I understand it, this change just means that the CLR will probably garbage collect the object sooner. But it didn’t affect anything else – for instance, the connection is still closed at the same time (right after Execute is called when the using completes and the connection is disposed) which I confirmed in the debugger.

As noted in the question since there is no heap corruption I suspect that the underlying issue is a bug, maybe in System.Transactions. Databases of different types were in use (SQLite and MS Access / Jet) so I don't think the issue could be in the actual connection objects, which would have had to differ.

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81