CLR Run-Time Breaking Changes

 
Short Description Three overloads of Marshal.WriteInt16 that accept char as one of parameters write only one single byte.
Affected APIs public static void WriteInt16(IntPtr, char); public static void WriteInt16(IntPtr, int, char); public static void WriteInt16(object, int, char); Severity Low Compat Switch Available No

Description The affected APIs always wrote only one byte. This is wrong in many situations, where two bytes are needed. An example of the issue: If you write 'a', the memory at that offset will be cc61.

User Scenario Anybody who was using these APIs expecting two bytes to be written.

Work Around Many consumers of the APIs already seemed to work around the known bug with code such as the following: Marshal.WriteInt16(logFont, LogFontNameOffset, (short)'@');
 

 

 
Short Description TlbExp naming generation change
Affected APIs TlbExp.exe tool and type library export API TypeLibConverter.ConvertAssemblyToTypeLibrary Severity Low Compat Switch Available No

Description Current behavior TlbExp generates a type library for mscorlib.dll as a part of our build process. This type library is then used from COM clients. TlbExp does some complicated renaming for classes/interfaces when it finds the naming conflicts between namespaces (since type libraries don't have namespaces). Unfortunately, it may rename classes/interfaces even when one of the conflicting classes/interfaces is private or COM invisible. Why is this important? It is discovered that as soon as we add a new namespace with generic classes to mscorlib, we'll break COM clients. These generic classes have the same names as non-generic ones that already exist. The generic classes are not visible to COM but cause the old ones to be renamed anyway.

User Scenario VB6: VB6 project references a typelib by LIBID and version (and LCID). So as long as the v1.0 mscorlib.tlb is still on the machine, it won't be affected even on recompilation. If the machine only has a version 2.0 mscorlib.tlb, the reference would be automatically satisfied by that, and there's the potential of compile errors from changed names only if those types are being used. Note that references to typelibs should only be auto-upgraded if the major version is the same, so if version 2.0 is a 2.0 release and our typelibs have version 2.0, then referencing a version 2.0 typelib would have to be an explict action by the developer anyway (and if they wanted the old names they could install and reference the v1.0/v1.1 mscorlib.tlb). Of course, this same behavior applies to customer assemblies and typelibs as well. C++: Type libs are #imported by filename, so which mscorlib.tlb gets used (when there are multiple ones) can depend on the development environment, the source code, etc. So if recompilation picks up the version 2.0 mscorlib.tlb, compilation errors would occur only if those types are being used.

Work Around Use the new TlbExp to force the old behavior if applicable.
 

 

 
Short Description Trailing slash behavior changed for DirectoryInfo.Parent, and Name.
Affected APIs System.IO.DirectoryInfo.Parent System.IO.DirectoryInfo.Name Severity Low Compat Switch Available No

Description

In RTM, we had behavior on some of our IO classes which was simply inappropriate. One such case is DirectoryInfo.Parent. In RTM, if you called this API, and passed a path ending in a slash, then the path returned would simply give the same path, without the slash:

DirectoryInfo di = new DirectoryInfo(@"c:\temp\Dir\");
DirectoryInfo di2 = di.Parent();

In v1.0, the call to Parent would return "c:\temp\Dir" for the DirectoryInfo. However, this is not the way users expect this to behave: the backslash should simply have been ignored. We therefore changed this API to ignore a trailing backslash, so in v1.1, the above call will now return "c:\temp". As part of this change, DirectoryInfo.Name was changed. In v1.0, if a path which ended in a backslash, we would do no interpretation of the path at all, and simply return the fullname. In V1.1, we now determine that if you have specified 'c:\foo\bar', then the name simply refers to bar. This change in behavior is consistent with the change to Parent.


User Scenario Anyone relying on trailing slash behavior of DirectoryInfo, on the Parent, or the Name.

Work Around Remove trailing slashes from strings which are paths
 

 

 
Short Description NativeOverlapped constructor changed from taking an int to IntPtr
Affected APIs System.Threading.NativeOverlapped class Severity Low Compat Switch Available No

Description Some fields on the NativeOverlapped class needed to be IntPtr's instead of Int32's for 64 bit portability. We changed that. We believe this is the least cost fix. It's limited primarily to people implementing classes that support async IO. It's an advanced scenario, and people who hit this bug can probably figure it out easily.

User Scenario User implements a class like FileStream that supports async IO.

Work Around Change your code to use an IntPtr.
 

 

 
Short Description Greater precision introduced in Floating Point calculations
Affected APIs Any/All Floating Point manipulation Severity Low Compat Switch Available No

Description

In the CLR model, we assert that arguments, locals, and return values (things which you can't tell their size) can be expanded at will. When you say float, it means anything greater than or equal to a float. So we can sometimes give you what you asked for 'or better'. When we do this, we can spill 'extra' data, almost like a 'you only asked for 15 precision points, but congratulations! We gave you 18!'. If someone expected the floating point precision to always remain the exactly the same, they could be affected. In order to faciliate performance improvements and better scenarios, the CLR may rewrite (as in this case) parts of the register. For example, things that used to truncate because of spilling, no longer do. We make these kinds of changes all the time. We believe this is an appropriate change, and it is even called out specifically in the CLI specification, as something which can, and will occur with different iterations:


User Scenario Someone relies upon a floating point value to have exactly a certain precision.

Work Around None
 

 

 
Short Description Some DateTime Parsing which was acceptable but interpreted badly, is now no longer accepted
Affected APIs DateTime.Parse, or derivatives (such as CDate in VB) Severity Low Compat Switch Available No

Description We try to make DateTime.Parse accept a lot of input and make a reaosnable interpretation. However there are scenarios where it was interpreting data which was simply not reasonable. For example, the string "01-01-10/10/2001" would previously be successfully parsed, however this is clearly not a valid form for a DateTime. The scope of this change is limited, and will not affect any valid formats.

User Scenario Someone who relied on certain strings to be parsed, will now get an exception

Work Around None
 

 

 
Short Description ToString behavior on RegistryKey has been modified
Affected APIs RegistryKey.ToString() Severity Low Compat Switch Available No

Description RegistryKey.ToString previously returned the Handle to the Key (as a Hexadecimal string) at the end of the ToString() call. We no longer append this data to the end of the ToString().

User Scenario Anyone relying on the result of RegistryKey.ToString(). Note that we specifically inform customers to NOT rely on ToString behavior, for any APIs

Work Around None
 

 

 
Short Description Check the type of type (class vs. struct) specified in a TypeRef in metadata to ensure that is correct. A TypeInitializationException will be thrown if the TypeRef is incorrect.
Affected APIs None Severity Low Compat Switch Available No

Description

Affected code will look something like the following:TypeSpec

#4 (1b000004)
-------------------------------------------------------
TypeSpec : Class System.Byte

It should however, look like:

TypeSpec #4 (1b000004)
-------------------------------------------------------
TypeSpec : ValueClass System.Byte

The CLR accepted this incorrect metadata in v1.1. We have tightened down and do not accept it in v2.0.


User Scenario This would break you only if you were using a compiler of obfuscator that generated bad metadata.

Work Around None
 

 

 
Short Description WaitHandle.Wait* will throw when the wait completes due to an abandoned mutex. The mutex will continue to be held.
Affected APIs WaitHandle.WaitOne, WaitHandle.WaitAny, WaitHandle.WaitAll (all overrides) Severity Low Compat Switch Available No

Description When a wait function completes due to an abandoned mutex, it will throw instead of returning normally.

User Scenario Applications that encounter abandoned mutexes will receive an exception.

Work Around Catch the exception and either validate or make inaccessible the protected data.
 

 

 
Short Description The 2.0 runtime looks for and consumes the /cor command line switch on all applications.
Affected APIs None Severity Low Compat Switch Available No

Description The runtime looks for the /cor command line switch when any application that is built with the latest version of the runtime is launched, uses the switch for ClickOnce deployment, and then does not pass the switch on to the application. Any application that happens to use a /cor command line switch will start getting FileNotFound exceptions when rebuilt and executed with the latest version of the runtime.

User Scenario Any v1.x apps that used /cor for a command line switch will have to use a different switch when rebuilding or running on 2.0

Work Around Change the /cor command line switch to something else.
 

 

 
Short Description Throw AccessViolationException when an AV reaches managed code.
Affected APIs Any calls into unmanaged code through P/Invoke, any use of pointers in unsafe code. Severity Low Compat Switch Available No

Description Split NullReferenceException into NullReferenceException for "safe" errors, and AccessViolationException for "unsafe" ones.

User Scenario Applications that take an AV will receive an AccessViolationException instead of a NullReferenceException.

Work Around You should not catch AccessViolationException, you should fix the underlying AV. As a temporary measure, a config-file switch will be provided to revert this behavior.
 

 

 
Short Description Unhandled exceptions will always be fatal to a process.
Affected APIs N/A Severity Low Compat Switch Available No

Description Unhandled exceptions will always be fatal to the process.

User Scenario Applications that throw unhandled exceptions on threads other than the main thread (or ones that come into the runtime from the outside) will crash rather than continue running (potentially abnormally).

Work Around Put a catch block at the top of your non-main thread, threadpool workitem, or finalizer. (Or else fix the bug that led to the exception.) As a temporary measure, a config-file switch will be provided to revert back to the old behavior.
 

 

 
Short Description Adding unmanaged code permission link demand on some of the functions in the tracing classes.
Affected APIs System.Diagnostics.Trace, System.Diagnostics.TraceSource, System.Diagnostics.BooleanSwitch, System.Diagnostics.Debug, System.Diagnostics.SourceSwitch, System.Diagnostics.TraceSwitch Severity Low Compat Switch Available No

Description The change will allow the semi-trusted code to only write trace messages and it will prevent semi-trusted code from accessing listeners, correlation or modifying how the tracing is working.

User Scenario Semi-trusted code trying to call methods that affects how tracing is working.

Work Around It is not recommended but customers can create a proxy to expose the tracing functionality they want to semi-trusted code because we use link demand.
 

 

 
Short Description Disable inheriting from COM invisible classes and making derived classes COM visible.
Affected APIs There are no specific APIs that are affected. This is a change in internal CLR behavior. Severity Low Compat Switch Available No

Description Even when public non-sealed managed class (or the whole assembly) is marked as COM-invisible, somebody can inherit from such a class and make a subclass COM-visible. If such a subclass exposes a class interface of type AutoDual or type AutoDispatch (which is default), changes in the base class can break COM clients of a subclass. For example, if a new method is added on a base class (in a new version), a derived class with AutoDual interface will get different v-table layout and break native clients (since exposed v-table contain all public methods from base class as well as public methods of derived class). If a new overload is added on a base class, derived class with AutoDispatch interface will get its methods renamed (because of naming schema for overloaded methods) and again native clients will get broken. We don't believe that it is feasible to ask all developers of managed frameworks to maintain COM backward compatibility even in cases when their whole assemblies are marked as COM invisible and they clearly don't have any interesting COM clients. We also need to prevent people taking dependencies on behaviors we can't guarantee we'll preserve from version to version. This is only one step in that direction. It clearly unreasonable to put burden of preserving COM backward compatibility on every team that ships public non-sealed managed class.

User Scenario Anybody who was dependent on inheriting from COM invisible classes and making derived classes COM visible.

Work Around
  1. Make base classes that depend on COM clients COM visible. (preferred)
  2. Change derived class to use class interface of type None and explicitly forward calls to base class methods. (preferred)
  3. We would also add a config switch that would revert back to the old behavior. Note that wouldn't be guarantee that things will work either but there is a chance that they will if there were no changes in the base classes. If some changes are indeed made to the base class, clients could be broken by it as described above.
 

 

 
Short Description GetTypeFromCLSID returns the same type always, regardless of environment.
Affected APIs System.Type.GetTypeFromCLSID Severity Low Compat Switch Available No

Description

GetTypeFromCLSID() used to return a type object from tlbimp'ed assembly if there is a type with same GUID already loaded earlier. Such type used to yield 'false' when Type.IsComObject() is called. And, Type.GetMethodInfo() used to return whatever we could see in IL metadata assembly.

Now:

  • GetTypeFromCLSID() always returns a RuntimeType regardless of whether a type is already loaded with same GUID.
  • Type.IsComObject() returns true.
  • And, Type.GetMethodInfo() doesn't fetch any of the methods defined in tlb.

The reason we need to do this is to make things deterministic. In the V1 and V1.1 CLR, if the type has been loaded then we will wrap the created instance with the actual type, even if the Interop assembly for that type hasn't been registered. This can lead to unfortunate race conditions where, depending on what has currently been run in the AppDomain, the type will either be wrapped with an __ComObject or with the actual type. To remove these race conditions, we changed the behavior to only wrap the created COM object with the actual type if the Interop assembly is registered.


User Scenario This change would only break applications which are casting RCWs to specific types which, because of the way we import IAs, should not be very common. The other cases where it could possibly break is when reflecting on the type of the RCW; however we don't suspect this will be a common scenario.

Work Around Customer would need to register the Interop Assembly and then they will reliably get the COM object wrapped in the actual type
 

 

 
Short Description This breaking change request has two parts: 1) Always initialize main method as MTA unless user specified [STAThreadAttribute] on the main method. 2) Always initialize new threads as MTA unless user initialized it to STA before thread started.
Affected APIs There are no specific APIs that are affected. This is a change in internal CLR behavior. Severity Low Compat Switch Available No

Description

The problem with the V1 and V1.1 behavior is that depending on the app and the environment in which it is run (is it ngen'd, is it run off a network share, what are the security settings of the machine, etc), the CLR might need to start up COM to perform some of the startup code. When this happens, the CLR needs to start up COM before control enters the user's main and because we need to pick a default, we would CoInitialize it to MTA. What this would cause is that in some situations, your code below that does a CoInitialize in your Main method would fail. However under other circumstances it would pass. Changing the CLR to never use COM before the user's code is executed wasn't possible (it's not impossible, it is just way too much work and puts too many restrictions on the CLR to be viable) so we decided to make the behavior deterministic by always CoInitializing to MTA unless the [STAThreadAttribute] was specified on the main method.

Input from Christopher Brumme on this change:

"Whenever you set the apartment state of a running managed thread, you are in a race condition. That's because things like .cctors or security demands or assembly load notifications may have executed on that thread, perhaps causing marshaling of oleaut data—or any number of other things that could have caused a CoInitialize to happen. Even if the first line of your managed threadproc sets the apartment state, we have already executed an arbitrary amount of managed application code. And that arbitrary set of code changes whenever you upgrade the CLR, change the JIT inlining, fiddle with .cctor rules for NGEN vs. domain neutral, change security policy, etc.

If you are trying to set it to MTA, chances are you are okay. That's because we will default to MTA if we need to CoInitialize a thread. The only way someone would have set it to STA is if a .cctor or security infrastructure or hosting code had done this to you. And that would be pretty rude. Of course, if the CLR sets it to MTA and then the app sets it to MTA, this will be quietly accepted. So the only case that's likely to break is if the application selects STA but the CLR has already CoInitialized the running thread to MTA.

Anyway, the bottom line is that applications which set the apartment state of a running managed thread are perched on a knife edge. Long term, we need to move to a more stable and predictable world. The only question is the best way to do this. >We needed to change the behavior of 'main' on a managed EXE to be MTA if it is unspecified and we added a config setting for old C++ apps and for VB/C# apps that weren't built with Visual Studio.

The other change we added is for managed threads started with 't = new Thread(...); t.Start();'. If you didn't set the apartment state before you called Start, then it is now too late. We will have already placed you in the MTA. The same config setting to get the old behavior applies here too.

We realize that both of these changes will break apps. But those apps are going to break at some rate, as we disturb the race conditions anyway. We are providing the deterministic and reliable behavior now, know it will break some applications, rather than leaving in non-deterministic behavior that breaks applications with each new release."


User Scenarioo
  1. Anybody who was using Thread.CurrentThread.ApartmentState = ApartmentState.STA; as the first line in their Main method. For example, all WinForms applications generated with v1.1 C++ designer will break because of the such call in WinMain() that gets generated automatically. Note that they might be broken anyway (even without this change since they are dependent on any random changes in CLR initialization and on config changes).
  2. Anybody who was not initializing new thread as STA before they started it (but used to do it as first line of code that executes on that thread, for example).

Work Around This change 1) would only apply to apps that are compiled with v2.0(and after). Apps compiled with v1 and v1.1 would not see this but they might be broken on v2.0 anyway because they were dependent on the non-deterministic nature of apartment initialization. We would also provide config switch to revert back to old behavior for 2.0 apps (but this might save them per description above) or apps that use DLLs affected by 2).
 

 

 
Short Description UTF7Encoding need to override the Equality
Affected APIs UTF7Encoding.Equals and UTF7Encoding.GetHashCode Severity Low Compat Switch Available No

Description UTF7Encoding has boolean flag which control if the optional characters are allowed in encoding. so equality should consider this flag in the comparison.

User Scenario any app used to compare two UTF7Encoding objects may get different result than they get in v1.1.

Work Around No work around to consider the optional characters allowance flag except if the application keep track how UTF7Encoding object get created with this flag is true or false.
 

 

 
Short Description Change the Equality operation in SortKey class
Affected APIs SortKey.Equals Severity Low Compat Switch Available No

Description SortKey.Equals is not consistent with SortKey.Compare which is used to compare two SortKey objects. We need to make equality operation behave like SortKey.Compare. Currently SortKey.Equals is not that helpful for the user to compare two SortKey objects.

User Scenario Any client used to compare two SortKey objects may get different result than they get in v1.1.

Work Around use SortKey.Compare
 

 

 
Short Description StringInfo need to implement the Equality and GetHashCode.
Affected APIs StringInfo.Equals and StringInfo.GetHashCode Severity Low Compat Switch Available No

Description We need to implement semantics comparison to compare the associated string in StringInfo. In v1.1, StringInfo contains only static methods so it is very unlikely anybody will be broken because of this change.

User Scenario any app used to compare two StringInfo objects may get different result than they get in v1.1. v1.1 uses reference equality. Also storing StringInfo into hash table will be different than v1.1 too.

Work Around compare the result of the String property instead. (stringInfo1.String == stringInfo2.String).
 

 

 
Short Description UTF8Encoding needs to change Equals() implementation to consider throwOnInvalidBytes.
Affected APIs UTF8Encoding.Equals() Severity Low Compat Switch Available No

Description UTF8Encoding can be constructed with encoderShouldEmitUTF8Identifier and throwOnInvalidBytes option. The Equals() implementation only compares the encoderShouldEmitUTF8Identifier, but not throwOnInvalidBytes. This can lead application to re-use an UTF8Encoding expecting not throwing exception, but will get exception when encoding/decoding invalid char/byte sequence.

User Scenario If application uses UTF8Encoding.Equals() to compare two instances of UTF8Encoding using different throwOnInvalidBytes parameter in the ctor, v1.1 will return true, while v2.0 it will return false.

Work Around Since encoderShouldEmitUTF8Identifier is not exposed as public properties, app that replies on this behavior will have no workaround. However, it is unlikely that they will want to reply on this behavior.
 

 

 
Short Description Encoding.GetBytes() may not emit unpaired high or low surrogate characters for certain encodings (e.g. UTF-8 Encoding and UnicodeEncoding).
Affected APIs Encoding.GetBytes() Severity Low Compat Switch Available No

Description

For Unicode standard compliance, Encoding.GetBytes() will not emit bytes if there is an unpaired or out of order surrogate. This is most obvious if the caller call GetBytes() with one high or low surrogate. In this case, UTF8Encoding and UnicodeEncoding will not emit nothing.

The Unicode 4.0 requires that compliant applications not emit unpaired surrogates. In v1.1, GetBytes() will emit bytes for lone surrogates if the encoding supports it (such as UTF-8 and UnicodeEndcoding). However, this leads CLR not to be Standard compliance with Unicode 4.0.

The change can break application's assumption about that GetBytes() will emit leading high surrogates or mismatched surrogates. BinaryWriter.Write(char ch) is one example of being broken.


User Scenario If the application assumes that GetBytes() will emit high or surrogate if it is called with one surrogate (1/2 of the pair) at a time, or will emit a surrogate at the end of the character buffer, they may lose the ability to correctly generate surrogate pairs.

Work Around To fix this issue, applications have to either use the encoder or throw exception on unpaired surrogates.
 

 

 
Short Description Breaking Change: DisplayName of version 2.0 assemblies to contain processor architecture
Affected APIs Assembly.FullName, Type.AssemblyQualifiedName , the ResolveEventArgs.Name passed to AppDomain.AssemblyResolve. Severity Low Compat Switch Available No

Description The proposed change is to have the DisplayName of version 2.0 assemblies contain the processor architecture. This change is required because processor architecture is an inherent property of a version 2.0 assembly identity (just like other properties: name, version, culture). Omitting the processing architecture in the display name will lead to long-standing customer confusion because the identities reported by managed code do not accurately reflect their true identity. Note that only version 2.0 apps (and not version 1.1 apps) will be affected by this change, as only version 2.0 assemblies will have processor architecture in their identity.

User Scenario Any code that uses string comparison for comparing identities between refs and defs will be broken by this change. String comparison is not the correct way to compare identities. Note that only version 2.0 assemblies are affected as only version 2.0 assemblies have processor architecture in the display name. version 1.1 assemblies are not affected.

Work Around We will include processor architecture in the display name for version 2.0 assemblies only (not version 1.1). We will provide a managed API to do ref-def matching (This will be a static method on AssemblyName with a Boolean result specifying match success/failure). A cost of this change is everyone using the resolve hook now must call this new API; however this cost is justified as we are steering people in the right direction (of not using string comparison for comparing identities).
 

 

 
Short Description CompareInfo.GetSortKey should throw an exception when CompareOptions is out of range
Affected APIs System.Globalization.CompareInfo.GetSortKey Severity Low Compat Switch Available No

Description CompareInfo.GetSortKey should throw an exception when CompareOptions is out of range, previously it does nothing and essentially ignores the additional input.

User Scenario If an application was accidentally passing inputs in that are out of range they will now be notified of it via an exception rather then it being silently ignored

Work Around Change application to use inputs in the proper range.
 

 

 
Short Description Change SortKey.GetHashCode() to match SortKey.Equals() behavior
Affected APIs System.Globalization.CompareInfo.SortKey.GetHashCode Severity Low Compat Switch Available No

Description

Although breaking, we would need to go head and fix this to provide more consistent behavior, justification:

  1. With the current behavior, Equals and GetHashCode are not implemented the same way, if two objects are compared equal they should have the same hash code. The current implementation of GetHashCode is based on the name of the sortkey and so returns random results.
  2. The current behavior (based on instance? yuck!) has no true utility, where as the fixed behavior (creating a hashcode based on the string) does, since it could be used as an optimization for comparisons with a hashcode index and a sortkey index.
  3. There is no other good way to give the behavior since we cannot add a CultureInfo or CompareInfo to a sortkey (which already has one implied by its parent object from which it was created).

User Scenario See Description

Work Around none
 

 

 
Short Description RSACryptoServiceProvider and DSACryptoServiceProvider delay creation of a random key, causing exceptions to be thrown later than they were before.
Affected APIs RSACryptoServiceProvider and DSACryptoServiceProvider constructors Severity Low Compat Switch Available No

Description We now delay the creation of the random key in these classes until it is actually needed. This was done to increase performance and avoid unnecessary overhead when a random key was not needed (i.e., the class is created and a key is immediately imported). A side-effect of this, however, is that exceptions which were thrown in the constructor (such as for an invalid key size) are now thrown later or not at all. If someone were using random keys, relying on this behavior, and doing all error checking at construction time, this would break them.

User Scenario Users of these crypto classes

Work Around Change error-checking code (would have to recompile)
 

 

 
Short Description Kyrgyzstan tag is now KG, matching official ISO tag
Affected APIs Culture data, which shows up through APIs such as CultureInfo and ResourceManager.GetString. Severity Low Compat Switch Available No

Description v1.1 and v1.0 had the wrong tag for Kyrgyzstan, KZ. Fixed this in v2.0 so the tag for Kyrgyzstan is now KG. KG is the official ISO tag for kyrgyzstan. There is no country whose official ISO tag is KZ.

User Scenario Using the official ISO tag for Kyrgyzstan will now work. Using the incorrect tag will no longer work. If someone had created resources, tagged them as ky-kz, then did a ResourceManager.GetString("resid", "ky-KZ"), we will fail when trying to create the CultureInfo for ky-KZ, and will also therefore fail to find the resources tagged ky-KZ.

Work Around Update code and resources to use the new tag, ky-KG.
 

 

 
Short Description Globalization: DateTimeFormatInfo; 'U' format string for Datetime are missing
Affected APIs DateTimeFormatInfo Severity Low Compat Switch Available No

Description // Assume the user's current calendar is JapaneseCalendar.

CultureInfo ci = new CultureInfo("ja-JP"); // Create Japanese culture
ci.DateTimeFormat.Calendar = new GregorianCalendar(); // Switch to Gregorian calendar
Console.WriteLine(ci.DateTimeFormat.FullDateTimePattern;
// The Japanese format is printed out, while Gregorian format is expected.

User Scenario Japanese culture settings, switching to Gregorian calendar

Work Around none
 

 

 
Short Description DATA: Breaking Change - 4 cultures (ar-MA, nn-NO, kn-IN & div-MV) have misspelled day or month names.
Affected APIs none Severity Low Compat Switch Available No

Description Misspelled day/month names cause parsing errors

User Scenario Any of the following culture settings: ar-MA, nn-NO, kn-IN & div-MV

Work Around none
 

 

 
Short Description DATA: Breaking (rarely) month/day name changes
Affected APIs none Severity Low Compat Switch Available No

Description Misspelled day/month names cause parsing errors

User Scenario Any of the following cultures, zh-CN (chinese, china) uk-UA (Ukranian, Ukraine) bg-BG (Bulgarian (Bulgaria)) nn-NO (Norwegian, Nynorsk (Norway)), and Hebrew

Work Around none
 

 

 
Short Description Breaking Change: _Module in System::Runtime::InteropServices conflicts with global CComModule(ATL) object _Module
Affected APIs InteropServices._Module Severity Low Compat Switch Available No

Description This is a build time issue only and the compiler will emit a descriptive warning about the issue.

User Scenario Compiling a c++ app with /clr and adding a "using System.Runtime.InteropServices" will cause a conflict if the code uses either _Module

Work Around The workaround is to not add the "using System.Runtime.InteropServices" and instead qualify InteropServices._Module rather than _Module
 

 

 
Short Description DCR to make Boxed Value Types read-only
Affected APIs none Severity Low Compat Switch Available No

Description The breaking change is that in MultiDomain sharing you can now cause exceptions if you apply policy out of order. The apphack will ensure that in the case of applying policy to an existing domain that already has assemblies loaded, MultiDomain sharing will be treated like MultiDomainHost (which, as a permission hint, will have a perf penalty for these scenarios).

User Scenario See description

Work Around None
 

 

 
Short Description String comparison and sorting for sr-SP-Latin(Serbian) culture is incorrect
Affected APIs String.Compare Severity Low Compat Switch Available No

Description a known issue with the Windows XP, Windows Server 2003, and the .NET Framework's sorting tables.

User Scenario String comparison and sorting for sr-SP-Latin(Serbian) culture

Work Around none
 

 

 
Short Description Type.GetType(String typeName) throws if the type name contains the '&;' character
Affected APIs Type.GetType Severity Low Compat Switch Available No

Description the characters &;+*[] are now reserved characters for type names, using them will throw an exception

User Scenario using the characters &;+*[] in the type name parser

Work Around none
 

 

 
Short Description Canonicalization issue affecting Demand semantics in FileIOPermission
Affected APIs empty Severity Low Compat Switch Available No

Description Demand for a path name that is a subset of a different path name (e.g. "d:\foo" and "d:\foobar") is affected by security settings of the other, superset path

User Scenario user sets security on "d:\foo" but not "d:\foobar"

Work Around empty
 

 

 
Short Description BCL: StringBuilder constructor doesn't work as expected if capacity=0 and Maxcapacity 16/td>
Affected APIs System.Text.StringBuilder .ctor Severity Low Compat Switch Available No

Description This change is essential for ECMA compatibility. Achieving, and sustaining ECMA compatiblity is a fundamental requirement of the BCL. The scenario of appending a string to a stringbuilder which exceeds the maximum capacity specified for the stringbuilder is also, simply not an interesting scenario, especially when the affected scenario is constrained to situations where the capacity must be less than the default capacity.

User Scenario A user mistakenly appends more characters to a stringbuilder than they specific as the maximum capacity, AND the appended string is less than 16 characters.

Work Around Change the maximum capacity to valid value
 

 

 
Short Description Behavior change: % format produces different results under version 2.0 and EVE
Affected APIs IFormattable.ToString() Severity Low Compat Switch Available No

Description in V1.1, if you tried to format using "%." via IFormattable.ToString, the number would simply be ignored in V1.1, and "%" would be printed. However, the standard intent of . is to define a decimal place, and it is therefore more reasonable to expect it is a placeholder for the number. Therefore, this behavior has been changed to print (for an integer such as 5), "%500", which is far more robust, and better reflect the user expectation

User Scenario none

Work Around Change the original format string and remove the . if you don't want the value to be printed.
 

 

 
Short Description StreamReader used to ignore invalid UTF-* characters. Now it replaces them with "?"
Affected APIs System.IO.StreamReader Severity Low Compat Switch Available No

Description The default reader encoding is utf8. It can be possible to read a file which will end up having invalid characters in it for that format. Realistically, the best thing to have done would have been to throw, informing the user that those invalid characters are in the file. That would be too much of a breaking change however, so instead of simpy ilently swallowing those invalid characters (and thus, never letting the user know there's something bad going on) we now place a '?' character in their place, so the user can be aware of the issue

User Scenario A user who got lucky and expected a particular format when parsing information read from a file, and was relying on the inappropriate data from the file being ripped from the file. You would have to expect very specific lengths in the format to do this, something we would already strongly urge against, or expect that a specific section could be parsed into some specific value (such as an int).

Work Around Fix the invalid data in the file being read: there should be no invalid characters in files being read
 

 

 
Short Description Order of invocation of cctors has changed from V1.1 to V2.0 for beforefieldinit types in NGENed code.
Affected APIs None Severity Very Low Compat Switch Available No

Description

Cctor ordering for beforefieldinit types is not specified by ECMA. ECMA only specifies that the runtime will execute cctors on beforefieldinit types some time prior to any access to a type's static field. It does not offer any guarantees regarding exactly when and in which order these cctors should be executed.

Note: C# automatically marks types as beforefieldinit when static fields are initialized using "=" syntax like in "static int i = 5;". VB.NET programs are not affected since they use precise cctor semantics always.


User Scenario Any V1.0 or V1.1 compiled code that had dependency on cctor order execution and the cctors were on beforefieldinit types and the code was NGENed. (See sample code for an example of cctor order execution dependency)

Work Around a) Use V1.1 NGEN b) Use V1.1 or V2.0 JIT c) Change code to remove dependency on cctor ordering
 

 

 
Short Description Environment.UserDomainName now returns the correct value for name conflict scenarios
Affected APIs Environment.UserDomainName Severity Very Low Compat Switch Available No

Description In V1.1, if a domain user (e.g. 'SomeDomain\UserA') was logged into a machine (e.g. 'Test001')and the machine also had a local user account with the same name as the domain user (e.g. 'UserA'), then accessing the UserDomainName property would actually return the machine name, NOT the domain name. The same thing would happen if the domain user was logged in, and his/her name was the same as the name of the machine. This has been fixed, so the user's domain name is ALWAYS returned.

User Scenario A user has a local and domain name of the same name, and uses the user domain name property

Work Around None
 

 

 
Short Description Cache load failures in order to ensure that different app domains do not have different dependency loading success/failure characteristics in domain neutral sharing scenarios.
Affected APIs Deprecating Appdomain.AppendPrivatePath and Appdomain.ClearPrivatePath Severity Very Low Compat Switch Available No

Description

In v1.0 and v1.1, some pathways through the loader recorded a failure to load an assembly and would fail subsequent attempts to load that same assembly into the same AppDomain. However, most pathways through the loader would not cache this binding failure. This lack of caching enables certain scenarios, which some customers are doubtless taking advantage of.

In v2.0, the default behavior is to break these scenarios. Naturally the .NET Framework supports an opt-in mechanism to force the loader back to the v1.0 & v2.0 behavior via a config file. These scenarios are broken by the following changes made in v2.0:

  • All binding failures per assembly per AppDomain are cached. Subsequent attempts would unconditionally continue to fail. Transient errors like OutOfMemoryException are not considered binding failures and are subject to retry.
  • AppDomain.AppendPrivatePath and ClearPrivatePath are deprecated. These APIs allow the application to dynamically change where the .NET Framework looks for assemblies to satisfy binding requests. These APIs are subject to race conditions and cannot be reliably used.

User Scenario This change would break code that is dependent on changing the private path of the app-domain after the first assembly has been loaded. This is actually less common than one would think. ASP.NET thought that they relied on this and later learned that they did not.

Work Around None
 

 

 
Short Description Change Activator.CreateInstance and Type.InvokeMember to deterministically choose binding preferences, rathter than depending on declaraiton order.
Affected APIs Activator.CreateInstance, Type.InvokeMember, Severity High Compat Switch Available No

Description

The v1.x binder was arbitrarily assigning binding preferences between two methods. For example, in v1.x if you swap the declared order of M0 (or M2) the binder will bind to the other method.

using System;
using System.Reflection;

class C
{
public void M0(object arg) { Console.WriteLine(2); }
public void M0(ref int arg) { Console.WriteLine(1); }

public void M1(ref object arg) { Console.WriteLine(2); }
public void M1(ref int arg) { Console.WriteLine(1); }
}
class Repro
{
static void Main()
{
// Result depends on the order the M0 overloads were declared
typeof(C).InvokeMember("M0",
BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod,
null, new C(), new object[] { 100 }, null);

// Result depends on the order the M1 overloads were declared
typeof(C).InvokeMember("M1", BindingFlags.Public | BindingFlags.Instance |
BindingFlags.InvokeMethod,
null, new C(), new object[] { 100 }, null);
}
}

Now, in v2.0, we throw an AmbiguousMatchException and inform the developer about the ambiguous match rather than leave in the arbitrary, and prone-to-change, behavior of previous releases.


User Scenario .

Work Around .
 

 

 
Short Description Enterprise Library June release configuration tool fails when run against v2.0
Affected APIs mscorlib Severity Medium Compat Switch Available No

Description The Enterprise Library configuration tool throws an invalid cast exception when run against 2.0 when trying to use the Registry Storage Provider.

User Scenario Users implementing the Enterprise Library Configuration block will run into this exception

Work Around To fix this, they added the following to their code, but customers who compiled on v1.1 will still be broken by this:#if VS2005B2
SafeHandle handle = infoField.GetValue(configKey) as SafeHandle;
IntPtr keyHandle = handle.DangerousGetHandle();
#endif
 

 

 
Short Description NullReferenceException when calling GetEnumerator on a SynchronizedHashtable cast to ICollection
Affected APIs System.Collections.Hashtable.Synchronized, System.Collections.Hashtable.GetEnumerator Severity Medium Compat Switch Available No

Description SynchronizedHashtable is a wrapper around Hashtable. It inherits from Hashtable but routes all calls to the hastable it is wrapping after making the appropriate locks to synchronize access. If you cast the synchronized hastable to ICollection or IEnumerable and try to call ForEach on it, in v2.0 it will throw a NullReferenceException. In v1.1 it would not throw but it would return an enumerator with no elements, even if the hashtable in question was not empty. Prior to v2.0 the synch hashtable would allocate a array that it would never use. In v2.0, this redundant allocation was removed to save working-set size. However, there is a bug that if you cast the SynchronizedHastable to ICollection or IEnumerable, it does not forward to the wrapped object and gives you an empty enumerator to the outer one. Because the array is no longer allocated, in V2.0 this causes a NullReferenceException instead.

User Scenario Hashtable h = new Hashtable();
h.GetEnumerator();
(h as ICollection).GetEnumerator();
Hashtable h2 = Hashtable.Synchronized(h);
h2.GetEnumerator();
(h2 as ICollection).GetEnumerator();
In version 2.0, the last call throws NullReferenceException. In V1.1 it returns an empty enumerator.

Work Around To correct both problems, do not call the enumerator on a synchronized hastable that been casted or converted to IEnumerable or ICollection. Instead, enumerable on the strongly typed Hashtable object, or fetch the enumerator while it is still typed as Hashtable.
 

 

 
Short Description Releasing an unowned monitor throws an exception.
Affected APIs System.Monitor Severity Low Compat Switch Available No

Description In some circumstances in v1.1, we don't throw an exception if you tried to release an unowned monitor. This now throws an exception in v2.0.

User Scenario Imbalanced Monitor.Enter/Exits

Work Around Only release monitors that are owned.
 

 

 
Short Description WaitHandle.WaitAll throws an exception that seemingly cannot be caught if there's a null element in the array of wait handles; this is different from v1.1 behavior.
Affected APIs WaitHandle.WaitAll Severity Medium Compat Switch Available No

Description

If you pass in an array with one or more null entries into the API WaitHandle.WaitAll, in V1.1 this would throw an ArgumentNullException. In V2.0 this will throw a FatalExcecutionException, which cannot be caught and will tear down the process.

This happens because of a coding error. There is code to detect this situation and throw an exception, but some code earlier in the routine puts the array in a special wrapper that dereferences all the entries in it, which causes an AV in the VM, which tears down the process because that is the policy for failures of this type.


User Scenario If you pass in an array with one or more null entries into the API WaitHandle.WaitAll, in V1.1 this would throw an ArgumentNullException. In V2.0 this will throw a FatalExcecutionException, which cannot be caught and will tear down the process.

Work Around Don't leave any null values in the array passed to these APIs.
 

 

 
Short Description Applications dependent on the implementation of private fields could be broken if the type of the field changes for example from an IntPtr to a SafeHandle.
Affected APIs System.IntPtr() Severity Medium Compat Switch Available No

Description

Applications dependent on the implementation of private fields could be broken when the private field's type changes.

For instance, an app could use reflection to obtain a private field and try casting that field to an illegal type.

This has broken applications such as the Enterprise Library configuration tool, where the field type used to be safely cast to an IntPtr value, but since it has changed to a SafeHandle in v2.0 can no longer be cast this way.


User Scenario A user has code that casts a field that they obtained through reflection from one type to another. The field's type changes to one which cannot be cast the same way as the old type.

Work Around Do not rely on private implementation details whenever possible. We make no guarantees about maintaining compatibility with private members on types.
 

 

 
Short Description String.GetHashCode and Object.GetHashCode algorithms have changed.
Affected APIs String.GetHashCode, Object.GetHashCode, Hashtable Severity Medium Compat Switch Available No

Description Various performance tweaks have been made to the hashing algorithms of String and Object. One consequence of this is that code that depends on the exact value of the hash code, or the exact ordering of elements in a Hashtable, can be broken from version to version. Users should avoid depending on hash code values, as it makes code very fragile.

User Scenario A user populates a Hashtable, dumps the contents somewhere, and then has some code that depends on the exact ordering of the output. This is particular easy to do in things like tests where the output is diffed against a fixed file, so it can depend on exact Hashtable ordering.

Work Around The work around is to sort Hastable output after population if the order must be preserved over time, or otherwise don't depend on exact Hashcode values.
 

 

 
Short Description Stack Trace was misreporting nested classes in v1.1, this has been fixed in v2.0.
Affected APIs StackTrace() class, Environment.StackTrace, Exception.ToString() Severity Medium Compat Switch Available No

Description

The StackTrace dump in Environment and Exception had an error in V1.1 where it did not report nested type names correctly:

namespace A {
public class B {
public class C {
public string f() {
return new StackTrace().ToString
();
}
}
}
}

In V1.1 this reported "A.C.f()". In V2.0 the error was corrected to report "A.B.C.f()". Test code that baselines stack traces or code that cracked the text output of stack traces could have taken a dependency on the original bug.


User Scenario Test code that baselines stack traces or code that cracked the text output of stack traces could have taken a dependency on the original bug.

Work Around None.
 

 

 
Short Description Private typedefs may not have the same name in assemblies built for v2.0 as they did in v1.1.
Affected APIs Assembly.Load Severity Medium Compat Switch Available No

Description In v1.1, assemblies were allowed to have Private typedefs that had the same name. In v2.0 we disallowed this behavior. We still allow assemblies compiled for v1.1 or v1.0 to have private typedefs with the same name. However, assemblies compiled for v2.0 with this behavior will cause an exception when loaded.

User Scenario An app was obfuscated so that all Private typedefs had the name "$". If compiled for v2.0, this app will fail when the obfuscated assemblies are loaded.

Work Around An assembly compiled for v2.0 that needs obfuscation must be obfuscated so that Private typedef names differ. Already released apps with assemblies that were compiled for v1.1 will continue to work and need no workaround.
 

 

 
Short Description Loadfrom after Load bypasses Load context cache, and now loads from Cache.
Affected APIs Assembly.LoadFrom() Severity Medium Compat Switch Available No

Description In V1.1, assembly requests via System.Reflection.Assembly.LoadFrom() do not consult the Load context cache, just the LoadFrom cache. In the case that the assembly is already loaded in the Load context cache, the assembly is re-loaded from disk. This behavior actually does not make a lot of sense. This change results in a small performance increase for applications.

User Scenario A program loads an assembly via Assembly.Load() and then Assembly.LoadFrom(). In the past, the assembly would need to be re-loaded. In v2.0, the assembly first loaded via Assembly.Load() is used by calls to Assembly.LoadFrom()

Work Around There is no workaround for this change. It is expected to be a positive change for developers and existing programs.
 

 

 
Short Description Unhandled exceptions will always be fatal to a process
Affected APIs N/A Severity Medium Compat Switch Available No

Description Unhandled exceptions will always be fatal to the process. They weren't necessarily always fatal in V1.0/V1.1.

User Scenario Applications that throw unhandled exceptions on threads other than the main thread (or ones that come into the runtime from the outside) will crash rather than continue running (when they are potentially in an invalid state)

Work Around

Put a catch block at the top of your non-main thread, threadpool workitem, or finalizer. (Or else fix the bug that led to the exception.)

Alternatively, in the section of the application's config file, add the following:<legacyUnhandledExceptionPolicy enabled="1"/>

 

 

 
Short Description Order of invocation of class .ctors is changed from V1.1 to V2.0 for beforefieldinit types in NGEN
Affected APIs No APIs, this is internal CLR change Severity Medium Compat Switch Available No

Description

Class .ctor ordering for beforefieldinit types is not specified by ECMA. ECMA only specifies that runtimes will execute clas .ctors on beforefieldinit types some time prior to any access to a type's static field. It does not offer any guarantees regarding when exactly this might happen and in which order exactly. This of course never stopped anybody to (unknowingly) take a dependency on unspecified behavior if the behavior is consistent on the same runtime version.

Note: C# automatically marks types this way when static fields are initialized using "=" syntax like in "static int i = 5;". VB.NET programs are not affected since they use precise class .ctor semantics always.

An FxCop rule has been added to help detect the invalid pattern.


User Scenario Any V1.0 or V1.1 compiled code that had dependency on a class .ctor order execution, these class .ctors are on beforefieldinit types, and uses NGEN

Work Around Use V1.1 NGEN; or use the V1.1 or V2.0 JIT (the JIT is unaffected by this change)
 

 

 
Short Description A LockCookie for one ReaderWriterLock can be used to restore a different ReaderWriterLock
Affected APIs System.Threading.LockCookie Severity Medium Compat Switch Available No

Description

A thread can save the state of ReaderWriterLock into a LockCookie, and restore the state in the future using the saved LockCookie. A LockCookie can be serialized to a buffer. The buffer can be modified and then deserialized to a LockCookie. Now a thread can use the new LockCookie to restore the state of ReaderWriterLock. A LockCookie for one ReaderWriterLock can be used to restore a different ReaderWriterLock.

In addition, a LockCookie contains thread information about the thread to which a lock belongs. Serializing to a buffer, changing the thread id, and deserializing could open possible desynchronization attacks against arbitrarily chosen threads, provided that their identifiers were known or could be guessed


User Scenario There are no valid usages where a user would use a LockCookie from one lock to interact with another.

Work Around None
 

 

 
Short Description The values inside 2 enums (PerformanceCounterPermissionAccess and AttributeTargets) have changed.
Affected APIs performancecounter Severity Medium Compat Switch Available No

Description

In 1.1 the three values in the enum System.Diagnostics.PerformanceCounterPermissionAccess were Browse:6, Instrument:2, Admin:14. These have been changed to browse:3, Instrument:1, Admin:7

Additionally, the value of System.AttributeTargets.All has changed from 16383, to 32767


User Scenario Initialize PerformanceCounterPermission with PerformanceCounterPermissionAccess (only used in one of 4 constructors for PerformanceCounterPermissionAccess)

Work Around none
 

 

 
Short Description V1.1 stores raw handles in the WaitHandle.Handle property, while V2.0 wraps all raw handles in a SafeWaitHandle.
Affected APIs System.Threading Severity Medium Compat Switch Available No

Description

IntPtr oldHandle = waitHandle.Handle; Win32.CloseHandle( oldHandle ); waitHandle.Handle = Win32.CreateEvent( );

From V1.1 to V2.0 the WaitHandle class has changed. It now points to a SafeWaitHandle, not to the raw handle.

Getting the handle and closing it is bad. The SafeWaitHandle still tracks the old handle and it will try to close it at finalize time. This means that the handle can be attempted to be closed twice.


User Scenario Threadpool code tries to do a DangerousRelease on a SafeWaitHandle that has already been disposed.

Work Around Either avoid closing the handle manually in the finalizer for the class (the runtime will take care of that anyway), or unregister the WaitHandle from the ThreadPool before closing the handle. The first option is preferable.
 

 

Short Description Code that calls virtual functions non-virtually in partial trust fails with a System.Security.VerificationException when run against .NET Framework 2.0.
Affected APIs None

Description

The .NET Framework 1.1 C# compiler in certain cases generates code that calls virtual methods non-virtually. One of these cases is where there is a switch statement based on strings where the number of cases is greater than 8. In .NET Framework 2.0 a change was made to the verification rules for security purposes to disallow calling virtual methods non-virtually (in most cases). Therefore when code that was compiled using the .NET Framework 1.1 compiler is executed against .NET Framework 2.0, and the compiled MSIL binary contains a normal CALL (instead of a CALLVIRT) to a virtual method, and the assembly is running in partial trust, the MSIL needs to be verified, and it fails verification because of the new rule. The failure shows up as a System.Security.VerificationException.


Work Around

The next broad release of the .NET Framework (2.0 SP1 or Orcas) has a workaround for this issue. The workaround is to modify the machine.config file for the machine where the application is running by replacing the default "<runtime />" with:

<runtime>
    <legacyVirtualMethodCallVerification enabled="1" />
</runtime>