jacobslusser / ScintillaNET

A Windows Forms control, wrapper, and bindings for the Scintilla text editor.
MIT License
967 stars 245 forks source link

How to call non-wrapped Scintilla functions with parameters and retreive results #503

Open cyber960 opened 3 years ago

cyber960 commented 3 years ago

Hello,

I'd like to do some character substitution for display purposes in the Scintilla control as outlined here: https://www.scintilla.org/ScintillaDoc.html#SCI_SETREPRESENTATION

I found theses constants in the ScintillaNet code: public const int SCI_SETREPRESENTATION = 2665; public const int SCI_GETREPRESENTATION = 2666; public const int SCI_CLEARREPRESENTATION = 2667;

It looks like to call these functions I would call DirectMessage() with the first argument as the function name in the constants defined above and the other two arguments are IntPtrs. How would I go about converting char* parameters to these IntPtr parameters that it expects and how would I retreive the results of these function calls that return results?

Thanks for your help!

cyber960 commented 3 years ago

I managed to get these functions working in case someone wants to merge in the changes. This Notepad++ issue shows why having these functions are useful for displaying whitespace characters that are not shown by default when setting ViewWhitespace = WhitespaceMode.VisibleAlways: https://github.com/notepad-plus-plus/notepad-plus-plus/issues/8284

public unsafe void SetRepresentation(string encodedString, string representationString)
{
    var bytesEncoded = Helpers.GetBytes(encodedString, Encoding, zeroTerminated: true);
    var bytesRepresentation = Helpers.GetBytes(representationString, Encoding, zeroTerminated: true);
    fixed (byte* bpEncoded = bytesEncoded)
    {
        fixed (byte* bpRepresentation = bytesRepresentation)
        {
            DirectMessage(NativeMethods.SCI_SETREPRESENTATION, new IntPtr(bpEncoded), new IntPtr(bpRepresentation));
        }
    }
}

public unsafe string GetRepresentation(string encodedString)
{
    var bytesEncoded = Helpers.GetBytes(encodedString, Encoding, zeroTerminated: true);

    fixed (byte* bpEncoded = bytesEncoded)
    {
        var length = DirectMessage(NativeMethods.SCI_GETREPRESENTATION, new IntPtr(bpEncoded), IntPtr.Zero).ToInt32();
        var bytesRepresentation = new byte[length + 1];
        fixed (byte* bpRepresentation = bytesRepresentation)
        {
            DirectMessage(NativeMethods.SCI_GETREPRESENTATION, new IntPtr(bpEncoded), new IntPtr(bpRepresentation));
            return Helpers.GetString(new IntPtr(bpRepresentation), length, Encoding);
        }
    }
}

public unsafe void ClearRepresentation(string encodedString)
{
    var bytesEncoded = Helpers.GetBytes(encodedString, Encoding, zeroTerminated: true);
    fixed (byte* bpEncoded = bytesEncoded)
    {
        DirectMessage(NativeMethods.SCI_CLEARREPRESENTATION, new IntPtr(bpEncoded), IntPtr.Zero);
    }
}
VPKSoft commented 3 years ago

Hi, Do you have a sample of the call, I tried using the example from the Scintilla documentation, but now wow-effect happened:

private void mnuTestMethod_Click(object sender, EventArgs e)
{
    scintilla.Text = "Ω Ω";
    scintilla.Text += "\u03C9 \u2126";
    scintilla.SetRepresentation("\xe2\x84\xa6", "U+2126 \xe2\x84\xa6");
}

image

I definitely wish to merge the code after I know how it should work to this NET Framework + NET Core + NET 5 package fork.

cyber960 commented 3 years ago

the w looking character is lowercase omega which is why their example is a bit confusing. You can try the sample code below, once without SetRepresentation and once with it.

string ohm = "\u2126";
string omega = "\u03C9".ToUpper();
scintilla1.Text = $"Ohm: {ohm}\r\nOmega: {omega}";

//scintilla1.SetRepresentation(ohm, "OHM");
//scintilla1.SetRepresentation(omega, "OMEGA");
VPKSoft commented 3 years ago

Thanks, the documentation was a bit confusing indeed - seems to work understanding how to use this: image

I'll document the methods and add them to the fork. Thanks again!

VPKSoft commented 3 years ago

NuGet released. Here is the latest commit.

cyber960 commented 3 years ago

@VPKSoft many thanks for maintaing an updated fork of ScintillaNet and thanks for the latest NuGet release. I was previously using the jacobslusser.ScintillaNET.Signed NuGet package as my project required the assembly to have a strong name. Would it be possible for you to release a NuGet with a strong name as well?

VPKSoft commented 3 years ago

I'll do the strong naming when you explain me how to do this - I thought the package signing would be enough 😅 image

cyber960 commented 3 years ago

To sign the assembly with a strong name key, you can either use the strong name key file from the main branch or create a new one: https://github.com/jacobslusser/ScintillaNET/blob/master/src/ScintillaNET.snk

In Visual Studio, right-click the ScintillaNET project, select 'Properties', go to the 'Signing' tab, check the checkbox 'Sign the assembly' and then set it to the 'ScintillaNET.snk' file.

In the main branch, jacobslusser created a separate build configuration for the signed assembly (in his ScintillaNET.csproj file you can see that he has 'Release|AnyCPU' and 'Signed|AnyCPU'). I am not sure why this was done since instead of just having a single configuration with the strong name. There doesn't appear to be any benefits to having an assembly that isn't strong named. See https://docs.microsoft.com/en-us/dotnet/standard/assembly/strong-named

VPKSoft commented 3 years ago

Have a try: unofficial.ScintillaNET.Signed - I haven't tested this yet. I did just that which was NOT recommended by this Microsoft article and gave the package a different name after a few broken pipeline runs 🙄 I'll see if I can configure the pipeline to sign the assembly, but that is going to take some more time.

VPKSoft commented 3 years ago

Ps. the *.snk file doesn't do the job, I seem to need a PFX containing the private key and passing the password to the pipeline. I have no idea how this should be done in with MSBuild: image

cyber960 commented 3 years ago

Thank you, the new NuGet with the signed assembly is working correctly.

Below are some articles I found with respect to strong names and .NET Core. Note that I am using .NET Framework, whereas the error you posted is for the .NET Core build. It seems that it is only relevant to apply strong names for .NET Framework. I'm not sure how the whole build process works but perhaps there is a way to use the .snk file only on the .NET Framework build.

From https://github.com/dotnet/runtime/blob/master/docs/project/strong-name-signing.md: All .NET Core assemblies are strong-named.

From https://docs.microsoft.com/en-us/dotnet/standard/assembly/strong-named For .NET Core, strong-named assemblies do not provide material benefits.

VPKSoft commented 3 years ago

Hi, I managed to get the official unofficial.ScintillaNET CI/CD pipeline to build a strong-named assembly. Can you verify this? 🙂 I also deprecated the specific-named NuGet at the same time.

cyber960 commented 3 years ago

Thanks! I have verified that the net40 dll in v3.8.3 is signed with a strong name and that it is working correctly