0

In my WndProc I receive a WM_SETTINGCHANGE message, and I want to see what category the caller sent in the LParam field of the incoming message. Try as I might (and after searching everywhere), I cannot find anything that shows how to get the original string into C# where I can see it. The best I got was the code below, but it causes a system access violation attempting to read protected memory at the GetLParam call.

I get the feeling that the method for accessing LParam varies with every different WM_* message type.

    var mystr = new COPYDATASTRUCT();
    var mytype = mystr.GetType();
    mystr = (COPYDATASTRUCT) wMsg.GetLParam(mytype)!; // sys access violation here
    var textReceived = mystr.lpData;
    // System.AccessViolationException: 'Attempted to read or write protected memory

How can I dereference or copy the LParam so that I can see what category the WM_SETTINGCHANGE is referring to? Thank you.

UPDATE: Here is the solution that I was looking for, thanks to @Remy below.

 case WM_SETTINGCHANGE:
        // this arrives TWICE on a user dark mode change
        var text = Marshal.PtrToStringAuto(wMsg.LParam);
        var msg = @$"WM_SETTINGCHANGE received: W:{wMsg.WParam}" + $" - L: {text}";
        MessageBox.Show(msg);
Kevin
  • 1,548
  • 2
  • 19
  • 34
  • 2
    I haven't tried this myself, but you might be able to use System.Runtime.InteropServices.Marshal.PtrToStringUni or similar to retrieve the string from that lparam. – Ed Dore May 04 '23 at 03:53
  • The only thing I can see is a complete misunderstanding of WM_SETTINGCHANGE. Why that code was written and what problem it is supposed to solve is quite invisible. If you want to use COPYDATASTRUCT then you must send a WM_COPYDATA message. – Hans Passant May 04 '23 at 13:47
  • @HansPassant Yes, I know the code was for WM_COPYDATA, and I totally agree with you there. But SO always wants to see some code, so I posted that. Nothing else worked. I tried various Marshal.* and c-style* ptr dereferences, but nothing worked. At least I know the COPYDATA example works for WM_COPYDATA messages. As for the complete misunderstanding, maybe partially there. The doc says that the sender of the broadcast message can/should put in a category string like "Environment." I want to see what string is in there, so I need to dereference the LParam ptr. – Kevin May 04 '23 at 16:12

1 Answers1

0

I get the feeling that the method for accessing LParam varies with every different WM_* message type.

Yes, it does. So, you have to read the documentation for each individual message. For instance:

Per the WM_COPYDATA documentation:

lParam

A pointer to a COPYDATASTRUCT structure that contains the data to be passed.

Per the WM_SETTINGCHANGE documentation:

lParam is a pointer to a [C-style null-termimated] string that indicates the area containing the system parameter that was changed. This parameter does not usually indicate which specific system parameter changed. (Note that some applications send this message with lParam set to NULL.) In general, when you receive this message, you should check and reload any system parameter settings that are used by your application.

In C#, you can use PtrToStringAnsi(), PtrToStringUni(), or PtrToStringAuto() function (depending on how you are creating your window) to access the string value from the lParam of WM_SETTINGCHANGE.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770