Search code examples
c#dllconstantssendmessagenative-methods

Enums and constants, how does it all work?


I have been all over the internet for the last few days in search of the answer to my question, and with no luck.

So, I am attempting to understand the role of constants in a program and how they are referenced in classes.

I have been looking through the source code of Scintilla, and have seen that there are several values such as:

public const int SC_WRAP_NONE = 0;
public const int SC_WRAP_WORD = 1;
public const int SC_WRAP_CHAR = 2;
public const int SC_WRAP_WHITESPACE = 3;

These are located in the NativeMethods.cs file (here).

I have found documentation (here) that explains that some of these are enums. the aforementioned example is the enum for word wrap (wrap by word, character, or whitespace).

However, there are some constants defined like so:

public const int SCI_START = 2000;
public const int SCI_OPTIONAL_START = 3000;
public const int SCI_LEXER_START = 4000;
public const int SCI_ADDTEXT = 2001;
public const int SCI_ADDSTYLEDTEXT = 2002;
public const int SCI_INSERTTEXT = 2003;
public const int SCI_CHANGEINSERTION = 2672;
public const int SCI_CLEARALL = 2004;
public const int SCI_DELETERANGE = 2645;
public const int SCI_CLEARDOCUMENTSTYLE = 2005;
//
// ... list continues
//
public const int SCI_SETWRAPINDENTMODE = 2472;
public const int SCI_GETWRAPINDENTMODE = 2473;

If they are to be used as enums, I am wondering why they begin at 2000.

Not only that, but in the implementation within a class (taking the SCI_SETWRAPINDENTMODE and SCI_GETWRAPINDENTMODE constants as examples, the values are used like so:

public WrapIndentMode WrapIndentMode
{
    get
    {
        return (WrapIndentMode)DirectMessage(NativeMethods.SCI_GETWRAPINDENTMODE);
    }
    set
    {
        var wrapIndentMode = (int)value;
        DirectMessage(NativeMethods.SCI_SETWRAPINDENTMODE, new IntPtr(wrapIndentMode));
    }
}

These values are sent to Scintilla using the DirectMessage function (similar to Microsoft's SendMessage function). However, the thing I'm trying to figure out is how the application gets from recieving enum values to (in this case) wrapping the text. Is this something to do with the .dll files?

So my questions are:

  1. Why do some enums begin with such a large reference (>2000)?
  2. How does the application know what to do with the values in order to enact the desired output.
  3. Most importantly, how can I create my own functions that can be actioned by sending enum values to them, and how will a program know what to do with MY enum values?

If possible I'd like someone to just break this down step by step for me and explain the general process of what's going on here.

Currently I am under the impression that the enum value is sent to a .dll file where it calls a method, but I may be way off.


Solution

  • Keep in mind that the Scintilla edit control was originally designed as a traditional Windows control, no different from, say, the TextBox or RichTextBox control you'd find in the toolbox for a Winforms project. ScintillaNET is a wrapper for it, just like the .NET TextBox and RichTextBox classes are wrappers, making it easy to use the control from a managed language like C#. The GitHub depository you linked to only contains the wrapper, not the code for the control itself.

    It follows the traditional way to interact with such controls, you send it a message. The specific message number tells it what to do, the optional wparam and lparam message arguments may contain additional values. The declaration style you see is boilerplate for the way these messages and values were declared 25 years ago.

    The basic recipe for picking message numbers is "pick a number, any number, as long as it larger than 1024 (WM_USER) and is unique". The offset is required so they don't collide with the system messages, like WM_CREATE. The original author just decided that 2000 was a good place to start.

    The specific message number tells the control what to do and how to interpret the message arguments (wparam and lparam). You can only add a function by picking a message number for it and modifying the control to recognize the message and implement the function. The window procedure of the control (traditionally named WndProc) interprets the message, you can see it here. Keep in mind that you need C++ skills and a good understanding of the existing code base to bring that to a good end.