ArgumentOutOfRange exception when using WPF controls in a Project 2010 add-in.

You may see "ArgumentOutOfRange" exception for no apparent reason when using WPF controls in Microsoft Project 2010 add-in.

Specifically, you will see exception with a call stack similar to below :-

System.ArgumentOutOfRangeException: The parameter value must be between '0' and '3579139.40666667'.
Parameter name: paragraphWidth
   at MS.Internal.TextFormatting.TextFormatterImp.VerifyTextFormattingArguments(TextSource textSource, Int32 firstCharIndex, Double paragraphWidth, TextParagraphProperties paragraphProperties, TextRunCache textRunCache)
   at MS.Internal.TextFormatting.TextFormatterImp.PrepareFormatSettings(TextSource textSource, Int32 firstCharIndex, Double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak, TextRunCache textRunCache, Boolean useOptimalBreak, Boolean isSingleLineFormatting, TextFormattingMode textFormattingMode)
   at MS.Internal.TextFormatting.TextFormatterImp.FormatLineInternal(TextSource textSource, Int32 firstCharIndex, Int32 lineLength, Double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak, TextRunCache textRunCache)
   at MS.Internal.TextFormatting.TextFormatterImp.FormatLine(TextSource textSource, Int32 firstCharIndex, Double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak previousLineBreak, TextRunCache textRunCache)
   at System.Windows.Controls.TextBlock.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.StackPanel.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at MS.Internal.Helper.MeasureElementWithSingleChild(UIElement element, Size constraint)
   at System.Windows.Controls.ContentPresenter.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Border.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Control.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.DockPanel.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Documents.AdornerDecorator.MeasureOverride(Size constraint)
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Interop.HwndSource.SetLayoutSize()
   at System.Windows.Interop.HwndSource.set_RootVisualInternal(Visual value)
   at System.Windows.Interop.HwndSource.set_RootVisual(Visual value)
   at System.Windows.Forms.Integration.ElementHost.OnHandleCreated(EventArgs e)
   at System.Windows.Forms.Control.WmCreate(Message& m)
   at System.Windows.Forms.Control.WndProc(Message& m)

WPF is running into this exception because it is not checking/initializing the Floating Point Unit's (FPU) precision that it expects, and somehow the FPU precision was initialized to 24-bit (rather than 53-bit, which WPF expects) by MS Project. Ideally, WPF (or any program that expects FPU precision to be something) should set FPU precision to the desired precision (53-bit in this case) before it goes about FPU related operations. This issue is fixed in .NET Framework 4.5.

To resolve this issue in older versions of WPF, set the FPU precision to 53-bit by setting the FPCW (Floating Point Control World) before any of the WPF control are loaded. Following P/Invoke code demonstrates this:

//Function and other declarations

[DllImport("msvcrt.dll", CallingConvention =CallingConvention.Cdecl)]
public static extern int _controlfp(int newControl,int mask);
const int _RC_NEAR = 0x00000000;
const int _PC_53 = 0x00010000;
const int _EM_INVALID = 0x00000010;
const int _EM_UNDERFLOW = 0x00000002;
const int _EM_ZERODIVIDE = 0x00000008;
const int _EM_OVERFLOW = 0x00000004;
const int _EM_INEXACT = 0x00000001;
const int _EM_DENORMAL = 0x00080000;
const int _CW_DEFAULT = (_RC_NEAR + _PC_53 +_EM_INVALID + _EM_ZERODIVIDE +_EM_OVERFLOW + _EM_UNDERFLOW +_EM_INEXACT + _EM_DENORMAL);

 
//Call the function, as shown below at appropriate places in the code (for e.g. in your add-in startup):
int value = _controlfp(_CW_DEFAULT, 0xfffff);