【问题标题】:AccessViolationException when try to read memory from pointer in C# using Marshal class尝试使用 Marshal 类从 C# 中的指针读取内存时出现 AccessViolationException
【发布时间】:2016-07-15 11:27:44
【问题描述】:

我尝试创建一个程序,该程序将以编程方式设置 Windows 颜色管理,以将 f.lux 之类的效果存档为一个爱好项目。

**WINAPIWcsGetDefaultColorProfileSize的定义。 **

BOOL WINAPI WcsGetDefaultColorProfileSize(
  _In_      WCS_PROFILE_MANAGEMENT_SCOPE profileManagementScope,
  _In_opt_  PCWSTR pDeviceName,
  _In_      COLORPROFILETYPE cptColorProfileType,
  _In_      COLORPROFILESUBTYPE cpstColorProfileSubType,
  _In_      DWORD dwProfileID,
  _Out_     PDWORD pcbProfileName
);

**这是托管WcsGetDefaultColorProfileSize 的签名。 **

[DllImport("Mscms.dll", EntryPoint = "WcsGetDefaultColorProfileSize", CharSet = CharSet.Unicode)]
private static extern bool GetDefaultColorProfileSize(
  WCS_PROFILE_MANAGEMENT_SCOPE profileManagementScope,
  [MarshalAs(UnmanagedType.LPWStr), In] string pDeviceName,
  COLORPROFILETYPE cptColorProfileType,
  COLORPROFILESUBTYPE cpstColorProfileSubType,
  Int32 dwProfileID,
  out IntPtr pcbProfileSize
);

问题是当我尝试使用 Marshal 类从 WINAPI 获取作为指针返回的字符串时。像这样

// Need to get the size of ICC profile file first
GetDefaultColorProfileSize(
WCS_PROFILE_MANAGEMENT_SCOPE.WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER, 
monitor_name.DeviceKey, 
COLORPROFILETYPE.CPT_ICC, 
COLORPROFILESUBTYPE.CPST_RGB_WORKING_SPACE, 
1,
out SizePointer);
System.Diagnostics.Debug.WriteLine("Size is " + Marshal.ReadInt32(SizePointer));

GetDefaultColorProfileSize 返回 TRUE 但是当我尝试使用 Marshal.ReadInt32 访问它返回的地址(它返回这个地址 0x0000003e) 它抛出这个错误

System.AccessViolationException 试图读取或写入受保护的内存。这通常表明其他内存已损坏。

如何才能读取该指针处的内存?

我尝试按照 nvoigt 的建议首先分配内存

IntPtr SizePointer 更改为IntPtr SizePointer = Marshal.AllocHGlobal(4);

但它仍然抛出相同的异常

完整代码如下。

枚举显示设备

    [DllImport("User32.dll")]
    private static extern bool EnumDisplayDevices(
        string lpDevice, int iDevNum,
        ref DISPLAY_DEVICE lpDisplayDevice, int dwFlags);

    [StructLayout(LayoutKind.Sequential)]
    public struct DISPLAY_DEVICE
    {
        public int cb;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string DeviceName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceString;
        public int StateFlags;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceID;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string DeviceKey;

        public DISPLAY_DEVICE(int flags) {
            cb = 0;
            StateFlags = flags;
            DeviceName = new string((char)32, 32);
            DeviceString = new string((char)32, 128);
            DeviceID = new string((char)32, 128);
            DeviceKey = new string((char)32, 128);
            cb = Marshal.SizeOf(this);
        }
    }

窗口颜色系统

    enum WCS_PROFILE_MANAGEMENT_SCOPE
    {
        WCS_PROFILE_MANAGEMENT_SCOPE_SYSTEM_WIDE = 0,
        WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER
    }

    enum COLORPROFILETYPE
    {
        CPT_ICC = 0x0001,
        CPT_DMP,
        CPT_CAMP,
        CPT_GMMP
    }

    enum COLORPROFILESUBTYPE
    {
        CPST_NONE = 0x0000,
        CPST_RGB_WORKING_SPACE = 0x0001,
        CPST_PERCEPTUAL = 0x0002,
        CPST_ABSOLUTE_COLORIMETRIC = 0x0004,
        CPST_RELATIVE_COLORIMETRIC = 0x0008,
        CPST_SATURATION = 0x0010,
        CPST_CUSTOM_WORKING_SPACE = 0x0020
    }
    [DllImport("Mscms.dll", EntryPoint = "WcsGetDefaultColorProfileSize", CharSet = CharSet.Unicode)]
    private static extern bool GetDefaultColorProfileSize(
      WCS_PROFILE_MANAGEMENT_SCOPE profileManagementScope,
      [MarshalAs(UnmanagedType.LPWStr), In] string pDeviceName,
      COLORPROFILETYPE cptColorProfileType,
      COLORPROFILESUBTYPE cpstColorProfileSubType,
      Int32 dwProfileID,
      out IntPtr pcbProfileSize
    );
    [DllImport("Mscms.dll", EntryPoint = "WcsGetDefaultColorProfile" , CharSet =CharSet.Unicode, SetLastError =true)]
    private unsafe static extern bool GetDefaultColorProfile(WCS_PROFILE_MANAGEMENT_SCOPE profileManagementScope,
         [MarshalAs(UnmanagedType.LPWStr), In] string pDeviceName,
          COLORPROFILETYPE cptColorProfileType,
          COLORPROFILESUBTYPE cpstColorProfileSubType,
          Int32 dwProfileID,
          Int32 cbProfileName,
          out IntPtr pProfileName);

    [DllImport("Mscms.dll", EntryPoint = "WcsSetUsePerUserProfiles", CharSet = CharSet.Unicode)]
    private static extern bool SetUsePerUserProfiles([MarshalAs(UnmanagedType.LPWStr), In]string pDeviceName, Int32 dwDeviceClass, bool usePerUserProfiles);
    [DllImport("Mscms.dll", EntryPoint = "WcsGetUsePerUserProfiles", CharSet = CharSet.Unicode)]
    private static extern bool GetUsePerUserProfiles([MarshalAs(UnmanagedType.LPWStr), In]string pDeviceName, Int32 dwDeviceClass, out bool pUsePerUserProfiles);

实际代码

    public unsafe static void GetProfile() {
        DISPLAY_DEVICE lpDisplayDevice = new DISPLAY_DEVICE(0);     // OUT
        DISPLAY_DEVICE monitor_name = new DISPLAY_DEVICE(0);        // OUT

        int devNum = 0;
        while (EnumDisplayDevices(null, devNum, ref lpDisplayDevice, 0)) {
            int devNum2 = 0;
            while (EnumDisplayDevices(lpDisplayDevice.DeviceName, devNum2, ref monitor_name, 0)) {
              IntPtr SizePointer;
              // Need to get the size of ICC profile file first
                GetDefaultColorProfileSize(
  WCS_PROFILE_MANAGEMENT_SCOPE.WCS_PROFILE_MANAGEMENT_SCOPE_CURRENT_USER, 
                    monitor_name.DeviceKey, 
                    COLORPROFILETYPE.CPT_ICC, 
                    COLORPROFILESUBTYPE.CPST_RGB_WORKING_SPACE, 
                    1,
                    out SizePointer);
              System.Diagnostics.Debug.WriteLine("Size is " + Marshal.ReadInt32(SizePointer));
            // Try to get a location of ICC profile file.
             // Not Implement Yet
                break;
            }
            break;
        }
    }

【问题讨论】:

  • 我们需要更粗体的文字。
  • 很抱歉,我现在已将问题修复为更具可读性。

标签: c# winapi marshalling unmanaged


【解决方案1】:

您的SizePointer 是您从未使用过的局部变量。我不知道您期望会发生什么,但这行不通。您可能打算使用profileSize

以后,您应该先修复警告。它们很重要。


好的,现在您至少传递了正确的变量。你还没有将它初始化为任何东西。您正在将 NULL 指针传递给应该写在那里的方法。它将无法。我无法想象您实际上从该方法中得到了TRUE。从 NULL 指针读取显然会失败。您需要确保指针实际上指向内存中可以写入的部分。

您需要DWORD。当您使用ReadInt32 时,我假设您使用的是 32 位。

你需要先分配内存:

int size = 4; // 4 byte = 32 bit

IntPtr p = Marshal.AllocHGlobal(size);

以后别忘了释放它,这不是垃圾回收:

Marshal.FreeHGlobal(p);

【讨论】:

  • 很抱歉,我将代码复制到 StackOverflow 并剪切了一些不重要的代码行时出现了拼写错误。我在 VS2015 中运行的实际代码已更正。正如你已经提到的,我已经修正了错字。
  • 如果您发布虚假代码然后告诉我们我们回答了错误的问题,没有人愿意提供帮助,就好像这是我们的错一样。坦率地说,我们并不关心您的代码实际上是什么。我们只关心摆在我们面前的问题。如果您发布的代码与您面前的代码无关,那么这就是您的责任。请不要发布虚假代码。
  • @nvoigt 我试过了,但它仍然抛出异常。返回“TRUE”的WcsGetDefaultColorProfileSize 返回out IntPtr SizePointer 的指针始终为“0x0000003e”。当我在 VS 中打开内存窗口并搜索此地址时。它只显示?? ?? ?? ??.
  • @Suttisak 我不会评论我看不到的代码。如果您尝试过,请发布代码
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-28
  • 2016-07-23
  • 2013-10-12
  • 1970-01-01
  • 1970-01-01
  • 2017-04-17
相关资源
最近更新 更多