Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Runtime.InteropServices;

internal partial class Interop
{
internal static partial class Richedit
{
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct EDITSTREAM
{
public UIntPtr dwCookie;
public uint dwError;
public IntPtr pfnCallback;
Comment on lines +16 to +17
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm there may be different padding on 64 bit vs. 32 bit structs. @weltkante if you have any immediate things you notice

Yes, you are adding 4 byte undesired padding in 64bit between these fields. You can solve this without making two structs by forcing alignment to 4 byte instead of keeping it at native size. So adding a [StructLayout(LayoutKind.Sequential, Pack = 4)] should do the trick.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the record, in the header file the place where it deviates from standard packing rules is by declaring

#ifdef _WIN32
#include <pshpack4.h>
#elif !defined(RC_INVOKED)
#pragma pack(4)
#endif

This is which forces Pack=4 in C# terms for all structs in this header file. Only relevant if the struct contains pointers or doubles (i.e. anything larger than 4 byte)

It's unfortunate that the headers have these sprinkled around instead of directly on the declarations like C# enforces, you always have to search for them if you want to make sure you are using the right packing rules.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping it as "unresolved" for the ease of future searching

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I'm writing unmanaged struct wrappers I'll sometimes write unit tests that check sizeof-- particularly for less common or complicated structures. I manually check the sizeof in a C++ dll and hard code the values, but that could also be done in code by having an export that allows you to query.

I'd consider:

  1. Writing tests that check the size
  2. Testing interop in both x86 and x64
  3. Using a cross-compiled native dll to expose the C sizeof for structs (in addition to other native helpers for testing)

Copy link
Contributor

@RussKie RussKie Feb 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Writing tests that check the size

TBD.

  • Testing interop in both x86 and x64

I have raised #2879 for CI enhancements. [EDIT] Merged

  • Using a cross-compiled native dll to expose the C sizeof for structs (in addition to other native helpers for testing)

@hughbe you were working on #1932. Perhaps it can be leveraged for this purpose.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hughbe you were working on #1932. Perhaps it can be leveraged for this purpose.

This is not going to be merged for sometime as it is waiting on upstream arcade changes.

I have written a bunch of tests to ensure near 100% coverage as well as verifying the size on x86 and x64

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;

internal partial class Interop
{
internal static partial class Richedit
{
public delegate int EDITSTREAMCALLBACK(IntPtr dwCookie, IntPtr pbBuff, int cb, out int pcb);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;

internal partial class Interop
{
internal static partial class Richedit
{
[Flags]
public enum SF : uint
{
TEXT = 0x0001,
RTF = 0x0002,
RTFNOOBJS = 0x0003,
TEXTIZED = 0x0004,
UNICODE = 0x0010,
USECODEPAGE = 0x0020,
NCRFORNONASCII = 0x0040,
RTFVAL = 0x0700,
F_PWD = 0x0800,
F_KEEPDOCINFO = 0x1000,
F_PERSISTVIEWSCALE = 0x2000,
F_PLAINRTF = 0x4000,
F_SELECTION = 0x8000,
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -181,23 +181,6 @@ public class HH_FTS_QUERY
internal string pszWindow = null;
}

public delegate int EditStreamCallback(IntPtr dwCookie, IntPtr buf, int cb, out int transferred);

[StructLayout(LayoutKind.Sequential)]
public class EDITSTREAM
{
public IntPtr dwCookie = IntPtr.Zero;
public int dwError = 0;
public EditStreamCallback pfnCallback = null;
}

[StructLayout(LayoutKind.Sequential)]
public class EDITSTREAM64
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public byte[] contents = new byte[20];
}

public delegate IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

public delegate int ListViewCompareCallback(IntPtr lParam1, IntPtr lParam2, IntPtr lParamSort);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,6 @@ public static StringBuilder GetModuleFileNameLongPath(HandleRef hModule)
[DllImport(ExternDll.User32, CharSet = CharSet.Auto)]
public static extern int SendMessage(HandleRef hWnd, int msg, int wParam, [Out, MarshalAs(UnmanagedType.IUnknown)]out object editOle);

[DllImport(ExternDll.User32, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.EDITSTREAM lParam);

[DllImport(ExternDll.User32, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.EDITSTREAM64 lParam);

[DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern int GetDlgItemInt(IntPtr hWnd, int nIDDlgItem, bool[] err, bool signed);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Xunit;
using static Interop.Richedit;

namespace System.Windows.Forms.Tests.Interop.Richedit
{
public class EDITSTREAMTests : IClassFixture<ThreadExceptionFixture>
{
[WinFormsFact]
public unsafe void EditStream_Size_Get_ReturnsExpected()
{
if (IntPtr.Size == 4)
{
Assert.Equal(12, sizeof(EDITSTREAM));
}
else
{
Assert.Equal(20, sizeof(EDITSTREAM));
}
}
}
}
Loading