AccessViolationException when try to read memory from pointer in C# using Marshal class

-2

I try to create a program that will programatically set Windows Color Management to archive an effect like f.lux as a hobby project.

**The definition of WINAPI WcsGetDefaultColorProfileSize. **

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
);

**This is the signature of managed 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
);

The problem is when I try to get the string which is return as a pointer from WINAPI using Marshal class. Like this

// 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));

the GetDefaultColorProfileSize return TRUE but when I try to access the address it return (It return this address 0x0000003e) using Marshal.ReadInt32 It throw this error

System.AccessViolationException Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

How can I able to read the memory at that pointer?

I try to allocate the memory first as suggesting by nvoigt

Changing from IntPtr SizePointer to IntPtr SizePointer = Marshal.AllocHGlobal(4);

But it still throw the same exception

The full code is below here.

Enumnerate Display Devices

    [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);
        }
    }

Window Color System

    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);

The Actual Code

    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
asked on Stack Overflow Mar 28, 2016 by Suttisak • edited Mar 28, 2016 by Suttisak

1 Answer

1

Your SizePointer is a local variable that you never used. I don't know what you expect to happen, but this is not going to work. You probably meant to use profileSize.

In the future, you should fix your warnings first. They are important.


Okay, now you are at least passing the correct variable. You still have not initialized it to anything. You are passing a NULL pointer to a method that is supposed to write there. It won't be able to. I cannot imagine you actually got a TRUE back from that method. And reading from a NULL pointer will obviously fail. You need to make sure that pointer actually points to a part in memory that can be written to.

You need a DWORD. As you are using ReadInt32, I'll assume you are on 32bit.

You need to allocate memory first:

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

IntPtr p = Marshal.AllocHGlobal(size);

Don't forget to free it later, this is not garbage collected:

Marshal.FreeHGlobal(p);
answered on Stack Overflow Mar 28, 2016 by nvoigt • edited May 23, 2017 by Community

User contributions licensed under CC BY-SA 3.0