如何正确对齐 SHELLSTATE 结构的字段?
How to properly align the fields of SHELLSTATE struct?
谁能帮我写出 SHELLSTATE 结构的正确定义(用 C# 或 VB.NET)?
我自己的定义不能正常工作,有些成员没有按预期工作,例如 fShowAllObjects
工作正常,但 fShowExtensions
没有,我无法将字段设置为True
,所以我认为是因为字段打包错误。
我不确定是否应该指定 StructLayoutAttribute.Pack
属性,或者我需要指定 LayoutKind.Explicit
然后为每个字段指定正确的字段偏移量,或者封送 Boolean
字段到 1 字节 <MarshalAs(UnmanagedType.I1)>
,这些是我尝试但没有成功的事情,我需要帮助才能正确完成。
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
这是一个丑陋的结构。从实验中我确定这些字段是 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);
谁能帮我写出 SHELLSTATE 结构的正确定义(用 C# 或 VB.NET)?
我自己的定义不能正常工作,有些成员没有按预期工作,例如 fShowAllObjects
工作正常,但 fShowExtensions
没有,我无法将字段设置为True
,所以我认为是因为字段打包错误。
我不确定是否应该指定 StructLayoutAttribute.Pack
属性,或者我需要指定 LayoutKind.Explicit
然后为每个字段指定正确的字段偏移量,或者封送 Boolean
字段到 1 字节 <MarshalAs(UnmanagedType.I1)>
,这些是我尝试但没有成功的事情,我需要帮助才能正确完成。
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
这是一个丑陋的结构。从实验中我确定这些字段是 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);