【问题标题】:How to properly align the fields of SHELLSTATE struct?如何正确对齐 SHELLSTATE 结构的字段?
【发布时间】:2016-05-18 01:35:55
【问题描述】:

有人可以帮我为 SHELLSTATE 结构编写正确的定义(在 C# 或 VB.NET 中)吗?

我自己的定义不能正常工作,有些成员不能按预期工作,例如fShowAllObjects工作正常,但fShowExtensions不行,我不能将字段设置为True,所以我认为是因为字段包装错误。

我不确定是否应该指定 StructLayoutAttribute.Pack 属性,或者我需要指定 LayoutKind.Explicit 然后为每个字段指定正确的字段偏移量,或者使用 @ 将 Boolean 字段编组为 1 字节987654331@,这些都是我尝试过但没有成功的事情,我需要帮助才能正确完成。

C#:

[StructLayout(LayoutKind.Sequential)]
public struct ShellState
{
    public bool ShowAllObjects;
    public bool ShowExtensions;
    public bool NoConfirmRecycle;
    public bool ShowSysFiles;
    public bool ShowCompColor;
    public bool DoubleClickInWebView;
    public bool DesktopHtml;
    public bool Win95Classic;
    public bool DontPrettyPath;
    public bool Unused1;
    public bool MapNetDrvBtn;
    public bool ShowInfoTip;
    public bool HideIcons;
    public bool WebView;
    public bool Unused2;
    public bool ShowSuperHidden;
    public bool NoNetCrawling;
    public int Unused3;
    public uint Unused4;
    public long ParamSort;
    public int SortDirection;
    public uint Unused5;
    public uint Unused6;
    public bool SepProcess;
    public bool WinXpStartPanelOn;
    public bool Unused7;
    public bool AutoCheckSelect;
    public bool IconsOnly;
    public bool ShowTypeOverlay;
    public bool ShowStatusBar;
    public uint Unused8;
}

VB.NET:

<StructLayout(LayoutKind.Sequential)>
Public Structure ShellState
    Public ShowAllObjects As Boolean
    Public ShowExtensions As Boolean
    Public NoConfirmRecycle As Boolean
    Public ShowSysFiles As Boolean
    Public ShowCompColor As Boolean
    Public DoubleClickInWebView As Boolean
    Public DesktopHtml As Boolean
    Public Win95Classic As Boolean
    Public DontPrettyPath As Boolean
    Public Unused1 As Boolean
    Public MapNetDrvBtn As Boolean
    Public ShowInfoTip As Boolean
    Public HideIcons As Boolean
    Public WebView As Boolean
    Public Unused2 As Boolean
    Public ShowSuperHidden As Boolean
    Public NoNetCrawling As Boolean
    Public Unused3 As Integer
    Public Unused4 As UInteger
    Public ParamSort As Long
    Public SortDirection As Integer
    Public Unused5 As UInteger
    Public Unused6 As UInteger
    Public SepProcess As Boolean
    Public WinXpStartPanelOn As Boolean
    Public Unused7 As Boolean
    Public AutoCheckSelect As Boolean
    Public IconsOnly As Boolean
    Public ShowTypeOverlay As Boolean
    Public ShowStatusBar As Boolean
    Public Unused8 As UInteger
End Structure

我还将提供 SHGetSetSettings 函数以进一步测试生成的结构修改:

<DllImport("Shell32.dll", SetLastError:=False)>
Public Shared Sub SHGetSetSettings(<[In]> <Out> ByRef refState As ShellState,
                  <MarshalAs(UnmanagedType.U4)> ByVal mask As ShellStateFlags,
                <MarshalAs(UnmanagedType.Bool)> ByVal applyState As Boolean)
End Sub

还有 SSF 枚举:

<Flags>
Public Enum ShellStateFlags As UInteger
    ShowAllObjects = &H1
    ShowFilenameExtensions = &H2
    ShowCompressedColor = &H8
    SortColumns = &H10
    ShowSystemFiles = &H20
    DoubleClickInWebView = &H80
    DesktopHtml = &H200
    Win95Classic = &H400
    DontPrettyPath = &H800
    MapNetDrvBtn = &H1000
    ShowInfoTip = &H2000
    HideIcons = &H4000
    NoConfirmRecycle = &H8000
    WebView = &H20000
    ShowSuperHiddenFiles = &H40000
    ExplorerSeparateProcess = &H80000
    NoNetCrawling = &H100000
    AutoChecboxkSelection = &H800000
    ShowIconsOnly = &H1000000
    ShowTypeOverlay = &H2000000
    ShowStatusBar = &H4000000
End Enum

【问题讨论】:

  • 我认为你的问题是那些布尔值是位域。看起来您必须实现一些包装器属性来旋转基础字段的位。见social.msdn.microsoft.com/Forums/en-US/…
  • 哇!这将比我第一眼想象的要难。感谢您的评论。
  • @David Heffernan 如果我记得很好,你向我解释了很多关于字段偏移(和联合)的事情,因为其他烦人且真正更大的结构,你还向我解释了如何确定字段大小VC++(或者也许我自己做了那件事,我不记得了)但我找不到你的 cmets 的问题(我真可怜,我自己的问题......我什至不记得结构名称) ,如果您阅读了本文并且如果您记得那个问题,请提及 url!,请确保我很欣赏您所说的“首先在 C++ 中尝试比较偏移量”。
  • 嗯,这很讨厌。所有这些位域。不要忘记 c++ long 是 32 位的。 C# long 是 64 位。

标签: c# .net vb.net winapi pinvoke


【解决方案1】:

这是一个丑陋的结构。通过实验,我确定这些字段是 4 字节对齐的,并且整体结构大小(MSVC 编译器)是 32 字节。鉴于此,以下结构应该有效。 (后注)

[StructLayout(LayoutKind.Sequential)]
public struct SHELLSTATE // int (4 byte) alignment
{
    public uint firstFlags; // includes alignment padding
    public uint dwWin95Unused;
    public uint uWin95Unused;
    public int lParamSort;
    public int iSortDirection;
    public uint version;
    public uint uNotUsed;
    public uint secondFlags; // includes spares at end to prevent access violation

    public bool fShowAllObjects => (this.firstFlags & 0x00000001) == 0x00000001;
    public bool fShowExtensions => (this.firstFlags & 0x00000002) == 0x00000002;
    public bool fNoConfirmRecycle => (this.firstFlags & 0x00000004) == 0x00000004;
    public bool fShowSysFiles => (this.firstFlags & 0x00000008) == 0x00000008;
    public bool fShowCompColor => (this.firstFlags & 0x00000010) == 0x00000010;
    public bool fDoubleClickInWebView => (this.firstFlags & 0x00000020) == 0x00000020;
    public bool fDesktopHTML => (this.firstFlags & 0x00000040) == 0x00000040;
    public bool fWin95Classic => (this.firstFlags & 0x00000080) == 0x00000080;
    public bool fDontPrettyPath => (this.firstFlags & 0x00000100) == 0x00000100;
    public bool fShowAttribCol => (this.firstFlags & 0x00000200) == 0x00000200;
    public bool fMapNetDrvBtn => (this.firstFlags & 0x00000400) == 0x00000400;
    public bool fShowInfoTip => (this.firstFlags & 0x00000800) == 0x00000800;
    public bool fHideIcons => (this.firstFlags & 0x00001000) == 0x00001000;
    public bool fWebView => (this.firstFlags & 0x00002000) == 0x00002000;
    public bool fFilter => (this.firstFlags & 0x00004000) == 0x00004000;
    public bool fShowSuperHidden => (this.firstFlags & 0x00008000) == 0x00008000;
    public bool fNoNetCrawling => (this.firstFlags & 0x00010000) == 0x00010000;
    public bool fSepProcess => (this.secondFlags & 0x00000001) == 0x00000001;
    public bool fStartPanelOn => (this.secondFlags & 0x00000002) == 0x00000002;
    public bool fShowStartPage => (this.secondFlags & 0x00000004) == 0x00000004;
    public bool fAutoCheckSelect => (this.secondFlags & 0x00000008) == 0x00000008;
    public bool fIconsOnly => (this.secondFlags & 0x00000010) == 0x00000010;
    public bool fShowTypeOverlay => (this.secondFlags & 0x00000020) == 0x00000020;
    public bool fShowStatusBar => (this.secondFlags & 0x00000040) == 0x00000040;

    public override string ToString() => $"SHELLSTATE Version: {this.version}";
}

自从我写了原始答案后,我使用一些 C++/CLI 重新检查了标志是否正确分解:

static IntPtr GetShellState()
{
    SHELLSTATE* state = (SHELLSTATE*)calloc(1, sizeof(SHELLSTATE));
    state->fShowAllObjects = TRUE;
    return IntPtr((void*)state);
}

static String^ SetNext(IntPtr value, int% flags)
{
    String^ result;

    SHELLSTATE* state = (SHELLSTATE*)value.ToPointer();
    if (state->fShowAllObjects)
    {
        state->fShowAllObjects = FALSE;
        state->fShowExtensions = TRUE;
        result = gcnew String("fShowExtensions");
        flags = 0;
    }
    else if (state->fShowExtensions)
    {
        // Removed other flag toggling
    return result;
}

static void FreeShellState(IntPtr value)
{
    if (value != IntPtr::Zero)
    {
        void* ptr = value.ToPointer();
        free(ptr);
    }
}

然后使用以下 C# 生成标志:

IntPtr s = Class1.GetShellState();
string itemName = "fShowAllObjects";
int flags = 0;

Func<IntPtr, int, uint> ReadFlags = (IntPtr ptr, int item) => (uint)Marshal.ReadInt32(ptr, item == 0 ? 0 : 28);

uint val = ReadFlags(s, flags);
System.Diagnostics.Debug.Print($"public bool {itemName} => (this.{(flags == 0 ? "first" : "second")}Flags & 0x{val.ToString("X8")}) == 0x{val.ToString("X8")};");

while (flags < 2)
{
    itemName = Class1.SetNext(s, ref flags);
    val = ReadFlags(s, flags);
    Debug.Print($"public bool {itemName} => (this.{(flags == 0 ? "first" : "second")}Flags & 0x{val.ToString("X8")}) == 0x{val.ToString("X8")};");
}

Class1.FreeShellState(s);

为了测试它,我使用了SHGetSetSettings P/Invoke:

[DllImport("Shell32.dll")]
public static extern void SHGetSetSettings(ref SHELLSTATE lpss, 
                                           UInt32 dwMask, 
                                           [MarshalAs(UnmanagedType.Bool)] bool bSet);

并调用函数:

SHELLSTATE state = new SHELLSTATE();
SHGetSetSettings(ref state, 0xFFFFFFFF, false);

【讨论】:

  • 感谢您的回答!。这些是有效的字段:fShowAllObjects, fShowExtensions, fShowCompColor, fShowInfoTip, fHideIcons, fShowSuperHidden,fSepProcess, fAutoCheckSelect, fIconsOnly, fShowStatusBar。这些是不起作用的字段:fNoConfirmRecycle, fShowSysFiles, fNoNetCrawling, fShowTypeOverlay,(它们始终具有相同的值)其余字段我没有测试它们,因为我对它们不感兴趣。
  • 我真的很想学习如何检查以了解如何对您为构建该结构的正确定义所做的事情进行必要的修改,但我不知道,所以我我会问你:你能否尝试修复结构以使fNoConfirmRecyclefShowSysFiles(至少是那些)工作?我尝试了不同的偏移值但没有成功,再次感谢!
  • @DavidHeffernan - 它最终并没有真​​正需要明确的布局,但我把它留在那里以明确每个成员的实际位置,因为问题是关于结构的布局. (是的,本可以对其中的每一个进行评论,但没有。是的,我知道LayoutKind.Sequential 会很好用。)
  • @ElektroStudios - 我会看看那些标志。此外,如果您想包含 VB 答案,请随时将其作为单独的答案发布,而不是将其编辑到我的中。
  • @ElektroStudios,我可以确认标记已正确分解。请参阅更新后的帖子。我不知道为什么 fConfirmRecyclefShowSysFiles 的值对你来说不正确,但它们似乎对我来说是正确的。
猜你喜欢
  • 2021-11-16
  • 2021-11-16
  • 2014-12-01
  • 2019-12-13
  • 2015-02-13
  • 1970-01-01
  • 2013-02-06
  • 1970-01-01
  • 2013-12-12
相关资源
最近更新 更多