question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Exception "The data area passed to a system call is too small"

See original GitHub issue

Description

Encountered the below exception stack trace in our production WPF application.

Details are scant but I do know the PC was running Windows 10 Pro 2009, and the version of our software that was running targeted net6.0-windows runtime environment. I also have a list of processes that were currently running at the time of the exception. I’m not sure how to share that privately here; let me know if you need it and I’ll arrange a way.

It’s difficult to tell exactly what the state of our software was when this exception happened, but other than this exception all signs indicate the software was operating normally.

Here’s the stack trace:

Type: System.ComponentModel.Win32Exception
Message: The data area passed to a system call is too small.
Source: WindowsBase
Stack Trace:
at MS.Win32.UnsafeNativeMethods.GetWindowText(HandleRef hWnd, StringBuilder lpString, Int32 nMaxCount)
at System.Windows.Automation.Peers.WindowAutomationPeer.GetNameCore()
at System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree()
at System.Windows.ContextLayoutManager.fireAutomationEvents()
at System.Windows.ContextLayoutManager.UpdateLayout()
at System.Windows.ContextLayoutManager.UpdateLayoutCallback(Object arg)
at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
at System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)

Internet search results currently point to some issue with an out-of-date installation of SQL Server. I do not believe SQL Server is/was present on this machine.

Reproduction Steps

Sorry

Expected behavior

I expect no exception

Actual behavior

Above exception was thrown

Regression?

No response

Known Workarounds

No response

Configuration

Windows 10 Pro 2009 x64 WPF net6.0-windows target

Other information

No response

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:5
  • Comments:16 (3 by maintainers)

github_iconTop GitHub Comments

3reactions
matthew-a-thomascommented, Feb 25, 2022

Another workaround

I think the following will more reliably work around this issue.

In general, P/Invoke the SetWindowsHookEx function with these parameters:

  • idHook: WH_CALLWNDPROCRET (12)
  • lpfn: the function described below
  • hmod: IntPtr.Zero, or 0
  • dwThreadId: current thread’s native ID

The function signature for WH_CALLWNDPROCRET is described here: https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nc-winuser-hookproc

What you want to do in this function is inspect the CWPRETSTRUCT parameter to see if the message is a WM_GETTEXT message. If so, then just set last system error to zero (Marshal.SetLastSystemError(0)).

Here’s some example WPF code that uses the PInvoke.User32 and PInvoke.Kernel32 NuGet packages to make it easier to deal with the native calls and structures:

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
using PInvoke;

public partial class App
{
    protected override void OnStartup(StartupEventArgs e)
    {
        var threadId = PInvoke.Kernel32.GetCurrentThreadId();
        Verify(PInvoke.User32.SetWindowsHookEx(
            User32.WindowsHookType.WH_CALLWNDPROCRET,
            (code, param, lParam) =>
            {
                unsafe
                {
                    ref var info = ref Unsafe.AsRef<PInvoke.User32.CWPRETSTRUCT>((void*)lParam);
                    if (info.message == User32.WindowMessage.WM_GETTEXT)
                    {
                        Marshal.SetLastSystemError(0);
                    }
                    return PInvoke.User32.CallNextHookEx(
                        IntPtr.Zero,
                        code,
                        param,
                        lParam
                    );
                }
            },
            IntPtr.Zero,
            threadId
        ));

        base.OnStartup(e);
    }

    static T Verify<T>(T handle)
    where T : SafeHandle
    {
        if (!handle.IsInvalid)
            return handle;
        var error = Marshal.GetLastWin32Error();
        if (error == 0)
            return handle;
        throw new Win32Exception(error);
    }
}

This (obviously) requires your project to allow unsafe code.

With this in my WPF app I can now set system errors all I want in as many hooks for WM_GETTEXT message hooks as I want, and my app won’t throw this issue’s exception.

However this does not hook into windows created on other threads. If you start a new Dispatcher in another thread and show a Window there then you’ll need to run the above code on that thread, too.

2reactions
matthew-a-thomascommented, Feb 25, 2022

Reproduction

Turns out it’s easy enough to trigger this exception by simply having a window with an empty Title and then setting the system error to 122 within a message loop hook for the WM_GETTEXT message and finally returning zero:

// Within `MainWindow.xaml.cs` of a brand new WFP project

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;

public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();
        Title = string.Empty;
    }

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);
        var source = (HwndSource)PresentationSource.FromVisual(this)!;
        source.AddHook(ProblemHook);
    }

    IntPtr ProblemHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        if (msg == 0x000D) // WM_GETTEXT
        {
            Marshal.SetLastSystemError(122);
        }
        return IntPtr.Zero;
    }
}

I think it’s significant that the message doesn’t even have to be marked handled.

And it’s not necessary to conditionally set the error. It also throws the exception if SetLastSystemError(122) is outside the if-statement.

But with the above then you get this exception stack:

System.ComponentModel.Win32Exception (122): The data area passed to a system call is too small.
   at MS.Win32.UnsafeNativeMethods.GetWindowText(HandleRef hWnd, StringBuilder lpString, Int32 nMaxCount)
   at System.Windows.Automation.Peers.WindowAutomationPeer.GetNameCore()
   at System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree()
   at System.Windows.ContextLayoutManager.fireAutomationEvents()
   at System.Windows.ContextLayoutManager.UpdateLayout()
   at System.Windows.UIElement.UpdateLayout()
   at System.Windows.Interop.HwndSource.Process_WM_SIZE(UIElement rootUIElement, IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam)
   at System.Windows.Interop.HwndSource.LayoutFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)

The stack trace is different because the layout pass is apparently being invoked by a different code path. But you’ll notice that the same methods are being exercised at the top of the stack.

Findings

As you can see, the exception message is caused by Win32 error 122. After running all the relevant Microsoft DLLs through disassemblers and tracing through Microsoft’s source code online we can see that error 122 is not raised on any of the relevant code paths.

And I guess I should state the obvious: no we are not knowingly setting the system error to 122 in the message loops in our software.

Taking all these things into account, I’m deducing these things:

  • Since the call stack is different, in the wild this exception is being raised more selectively
  • This exception is being caused by a window hook somewhere setting error 122
  • It is not being caused directly by any of the methods in the exception call stacks

Path forward

Possible workaround

First, let me show a possible workaround I found. We’ll probably implement this in our software. All you have to do is be the first to add this band-aid hook. But this only works if the WM_GETTEXT message goes unhandled by all hooks prior to the band-aid hook. So this likely won’t fix the issue for us:

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;

public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();
        Title = string.Empty;
    }

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);
        var source = (HwndSource)PresentationSource.FromVisual(this)!;
        source.AddHook(BandAidHook); // Make sure this is hooked first. That ensures it runs last
        source.AddHook(ProblemHook);
    }

    IntPtr ProblemHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        if (msg == 0x000D) // WM_GETTEXT
        {
            Marshal.SetLastSystemError(122);
        }
        return IntPtr.Zero;
    }

    IntPtr BandAidHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        if (msg == 0x000D) // WM_GETTEXT
        {
            Marshal.SetLastSystemError(0);
        }
        return IntPtr.Zero;
    }
}

Looking long term

Let’s take a look at that WPF method at the very top of the stack:

/// <SecurityNote>
/// SecurityCritical due to a call to SetLastError and calls GetWindowText
/// </SecurityNote>
[SecurityCritical]
internal static int GetWindowText(HandleRef hWnd, [Out] StringBuilder lpString, int nMaxCount)
{
    int returnValue = NativeMethodsSetLastError.GetWindowText(hWnd, lpString, nMaxCount);
    if (returnValue == 0)
    {
        int win32Err = Marshal.GetLastWin32Error();
        if (win32Err != 0)
        {
            throw new Win32Exception(win32Err);
        }
    }
    return returnValue;
}

…and the NativeMethodsSetLastError.GetWindowText(...) method looks like this:

[DllImport(PresentationNativeDll, EntryPoint = "GetWindowTextWrapper", CharSet=CharSet.Auto, BestFitMapping = false, SetLastError = true)]
public static extern int GetWindowText(HandleRef hWnd, [Out] StringBuilder lpString, int nMaxCount);

I ran PresentationNative_v0400.dll -> GetWindowTextWrapper(...) through a disassembler and can see that all it’s doing is SetLastError(0) before calling GetWindowTextW, which states:

If the function succeeds, the return value is the length, in characters, of the copied string, not including the terminating null character. If the window has no title bar or text, if the title bar is empty, or if the window or control handle is invalid, the return value is zero. To get extended error information, call GetLastError.

To me it seems like a bad decision to make it impossible to distinguish between an empty title bar and an error condition. But I’m betting that nobody will be in any hurry to alter the behavior of GetWindowTextW.

So I would like to propose: UnsafeNativeMethodsCLR.GetWindowText(...) should not check for errors, but should instead just return an empty string when NativeMethodsSetLastError.GetWindowText(...) returns zero. Would the WPF folks like a PR for this?

Read more comments on GitHub >

github_iconTop Results From Across the Web

"The data area passed to a system call is too small" error ...
This issue occurs because of miscommunications between two filter drivers, specifically WCNFS (the Desktop Bridge) and the RsFxXXXX.sys driver (SQL Server ...
Read more >
How to fix "The data area passed to a system call is too small ...
Method 1. Change Website properties · Method 2. Reinstall problematic apps · Method 3. Check for OS updates · Method 4. Try the...
Read more >
"The data area passed to a system call is too small" error ...
Describes a problem that occurs when the full path of a driver file is longer than SMS can handle. To work around this...
Read more >
The data area passed to a system call is too small, Error ...
This error occurs due to miscommunication between two drivers, WCNFS (the Desktop Bridge) and the RsFxXXXX. sys driver (SQL Server FILESTREAM ...
Read more >
The data area passed to a system call is too small [FULL FIX]
How can I fix The data area passed to a system call is too small error? · 1. Change Web site properties ·...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found