desjarlais / Scintilla.NET

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

Unable to add ScintillaNET control from Toolbox for v5.3.2.4 and newer in WinForms (.NET Framework) #68

Closed abc983ft closed 1 year ago

abc983ft commented 1 year ago

Describe the bug When you try to add the ScintillaNet control to a Form in a WinForms app (.NET Framework 4.5+) you get an exception instead of the control being added.

Error Message Failed to create component 'Scintilla'. The error message follows:

'System.ComponentModel.Win32Exception (0x80004005): Could not load the Scintilla module at the path 'C:\Users\\AppData\Local\Microsoft\VisualStudio\17.0_877463f4\ProjectAssemblies\pa0jc1fh.tmw01....\build\x64\Scintilla.dll'. ---> System.ComponentModel.Win32Exception (0x80004005): The specified module could not be found

at ScintillaNET.Scintilla.get_CreateParams()

at System.Windows.Forms.Control..ctor(Boolean autoInstallSyncContext)

at ScintillaNET.Scintilla..ctor()'

To Reproduce

Expected behavior The control is added to the form without any exception. This is the case with version 5.3.2.3 and older

Environment:

VPKSoft commented 1 year ago

Hi, This is "duplicate" of issue: #52 and I don't know how to resolve the designer error for .NET Framework. This is caused due the PR: #50 .

lgaudouen commented 1 year ago

Be reassured, the problem does not come from the version of the NET framework nor even from the version of Scintilla.NET (5.3.2.4 or .5 or .7) but simply from Scintilla.NET in taking into account the management of differences between .NET and .NET Framework in integrating Nuget packages. If you develop with Visual Studio a .NET application (7 for example ...) activating Windows Forms you will not have the problem that you encounter (as I encountered ...) but you will have another one, that control of the .NET framework version. If you develop a .NET Framework application with Visual Studio, you encounter a problem common to all dlls which must call other dlls (this is why some programmers put the secondary dlls embedded in their dll). In the case of Scintilla.NET with a Net Framework application, the Scintilla control is added in the references but this control must know where the two dlls it uses are (scintilla.dll (a control) and lexilla.dll) except to crash. To avoid this crash, the scintilla control of Scintilla.NET (file scintilla.cs) has planned to find where the two satellite dlls are when creating an instance in a LocateNativeDllDirectory() function in the event that the application is in design mode. Moreover, we can read there "Directory exists when application is built, but not if Scintilla is dragged on in the Winforms designer" just before the code which must deal with this case:

             // In the Designer, look for the native dlls in the nuget package
             return Path. Combine(managedLocation, "..", "..", "build", platform);

And that's where it gets stuck. Indeed, finding satellite dll locations is more complex than that. When you add a package, a "packages.config" file is added if necessary in the project directory and it will contain the list of added packages:

<?xml version="1.0" encoding="utf-8"?>
<packages>
   <package id="Scintilla.NET" version="5.3.2.7" targetFramework="net481" />
</packages>

and a "packages" directory is added which will contain the description of the packages with a sub-directory for each package. This package directory contains in particular the famous "build" directory with two directories "x86" and "x64" which contain the highly sought after satellite dlls scintilla.dll and lexilla.dll. All that remains is to retrieve the version of Scintilla.NET (x86 or X64) to know the base directory of the satellite dlls.

lgaudouen commented 1 year ago

...but all of the above is for documentary purposes. Indeed, as you have seen, the Scintilla.NET dll used to add this control to a window of a Net Framework project by VS (2022) is located in a subdirectory of the C:\ directory. Users\LG7.000\AppData\Local\Microsoft\VisualStudio\17.0_332d903b\ProjectAssemblie (for VS 2022 17.7.2 project) with an exotic name. It is therefore not possible to calculate in the Scintilla.NET dll the name of the directory of the two satellite dlls from the name of the file of the main dll. To work around or fix this problem: 1 - embed the satellite dlls in the main dll: this existed but this solution was removed in January 2023 (see https://github.com/VPKSoft/Scintilla.NET/pull/50). 2 - As mentioned above, a directory (packages") located in the same directory as the solution (the xxx.sln file) contains the directories describing the packages referenced by the solution projects. Assuming that the package ID is the same as that of the "AssemblyProduct" custom attribute of the Scintilla.NET dll assembly, Starting from the fact that to add the control in a form, it is necessary to activate the instance of VS 2022 adhoc, it turns out that the active directory (obtained by Environment.CurrentDirectory() or Directory.GetCurrentDirectory() giving the same result) is that of the solution. So, about "solution", we should have the solution to the problem in Scintilla.Net with the following code:

#if NET45
            // In the Designer, look for the native dlls in the nuget package
            string workingDirectory = Environment.CurrentDirectory;
            string pathSolution = Environment.CurrentDirectory;
            // get the package name
            Assembly thisAssembly = Assembly.GetExecutingAssembly();
            object[] assemblyAttrs = thisAssembly.GetCustomAttributes(true);
            // get the product name and the version
            string thisVersion = thisAssembly.GetName().Version.ToString();
            string thisProduct = "";
            object[] attributes = assembly.GetCustomAttributes(typeof(AssemblyProductAttribute), true);
            AssemblyProductAttribute productAttr = null;
            // If we didn't get anything, return null.
            if ((attributes != null)
                productAttr = (AssemblyProductAttribute)attributes[0] as AssemblyProductAttribute;
            if (productAttr != null)
                thisProduct = productAttr.Product;
            string packageName = thisProduct + "." + thisVersion;
            return Path.Combine(pathSolution, "packages", packageName, "build", platform);
#else
        throw new InvalidOperationException(@$("Unable to get the satellite scintilla Dll");
#endif

to replace the code in the file scintilla.cs:

            // In the Designer, look for the native dlls in the nuget package
            return Path.Combine(managedLocation, "..", "..", "build", platform);

If you are interested, I could DIY you a modified Scintilla.NET.5.3.2.7 version and how to replace it in your current project. Of course, to be professional, the proposed code should be improved to handle the disagreements problems encountered.

VPKSoft commented 1 year ago

The revert option would be fine otherwise but then it would dismiss the changes in #50? Those were made in my understanding to allow to control to work in a "more secure environment". E.g. extracting the files might be a suspicious process to some virus checker. So if the #50 changes are not reverted I would welcome a PR or some help to make the package compatible with the .NET Framework UI Designer.

lgaudouen commented 1 year ago

The proposed solution I just sent seems to be fine since it does not use embedded satellite dlls and changing the LocateNativeDllDirectory function would only affect the .NetFramework 4.5 version in the package (unless other corrections are needed in other parts of the Scintilla.Net code) since the problem does not arise with the .Net versions of the dll. I will do further more thorough testing of my proposal (unfortunately with VS 2022 only, I just threw away my VS2019). I will also test with SharpDevelop (version 5.1.0) and MonoDevelop. In the event that these tests are negative, there would be no other solution than to return to embedded satellites. Indeed, there is of course the solution to use envDTE (tested) but it would be a priori linked to VS and its version and I don't know what happens with SharpDevelop's envDTE and, to my knowledge, MonoDevelop has no envDTE service. I will let you know in the next few days my conclusions on the use of my proposed modification of Scintilla.NET. Sincerely.

lgaudouen commented 1 year ago

Regarding the tests with SharpDevelop, the question is solved: in the latest version, the packages manager uses Nuget's V2 protocol and I haven't found a solution to make it work. Moreover: "On September 18, 2017, Daniel Grunwald of the ICSharpCode team announced the project is "dead" for reasons related to the fast pace of changes to C# and .NET, including .NET Core, and suggested SharpDevelop users switch to either MonoDevelop or Visual Studio Code IDEs, each being recommended as a suitable open source replacement that is the target of regular updates and other maintenance." https://en.wikipedia.org/wiki/SharpDevelop

As for MonoDevelop, apart from its full and cumbersome installation, I haven't found a packages manager feature to import a nuget package into the project. Moreover,: "In October of 2021 it was announced in the issue tracker that the project would be archived because it's no longer maintained." https://en.wikipedia.org/wiki/MonoDevelop and: "After the acquisition of the independent developer Xamarin by Microsoft the MonoDevelop project was deprecated.Mar 12, 2022" And also (https://github.com/mono/monodevelop): "NOTICE This project has not been built nor maintained since January 2020 and has been archived If you are interested in working on the project, even when archived you can still create a fork of it.".

As for the tests with VS 2022 and the .NET Framework version, the modifications that I proposed (slightly modified) allow you to add a Scintilla.NET control in a form of a Windows Forms application (.NET Framework).

Attached is the code for the static Scintilla() and private static string LocateNativeDllDirectory() functions from scintilla.cs file. modifications for .NETFramework 4.5.txt To quickly test or allow a programmer to move forward with his project without waiting for a 5.3.2.8 version of Scintilla.NET, here is the dll to replace in the packages\Scintilla.NET.5.3.2.7 \lib\net45 subdirectory of his solution. Scintilla.NET.zip or a package to install locally and use normally (see https://www.c-sharpcorner.com/article/how-to-create-and-test-nuget-package-locally-in-visual-studio-2022 /): Scintilla.NET.5.3.2.7 (modified).zip

VPKSoft commented 1 year ago

Hi, This looks great - works on NET Framework 4.5 via the local NuGet: image

But doesn't support other NET Framework versions (should the compiler directive #if NET45 be "wider" ?), e.g. #if NETFRAMEWORK.

I also tested with NET 7 WinForms application and this seems to work fine. Thank you very much @lgaudouen! I'll test the source code in the weekend...

lgaudouen commented 1 year ago

For my tests, I used VS 2022 17.7.2 and the latest .NET versions: used  NET versions

I haven't installed .NET 8.0 yet but will soon (.NET 8 will be officially released in November 2023, currently only a Preview version)

To get the modified package, I used the code obtained from https://github.com/VPKSoft/Scintilla.NET/archive/refs/tags/v.5.3.2.7.zip (page https://github.com/VPKSoft/Scintilla.NET/releases/tag/v.5.3.2.7). The only modification I made is the one I described above ("modifications for .NETFramework 4.5.txt" file). I created a solution with projects following the "Windows Forms App" (for .NET versions 7.0, 6.0, 5.0 and 3.1) and "Windows Forms App (.NET Framework)" VS templates. For all .NET projects, the "Windows Forms" option is activated (this is normal!). In Design mode, adding a Scintilla.NET control is possible for .NET Framework projects (for the modified package attached in my previous post) and .NET 7.0 and 6.0 for the package published on nugget.org and the package modified previously transmitted. For .NET 5.0 and 3.1 projects, adding the control is not possible because it does not appear in the toolbox list and it is not possible to modify it (in the VS menu this command is disabled). The reason is that .NET 5.0 and .NET 3.1 are no longer supported and a NETSDK1138 anomaly is detected (see https://learn.microsoft.com/en-us/dotnet/core/tools/sdk-errors/netsdk1138?f1url=%3FappId%3DDev16IDEF1%26l%3DFR-FR%26k%3Dk(NETSDK1138)%26rd%3Dtrue and https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core). However, to test the .NET 5.0 and .NET 3.1 versions of Scintilla.NET, I have:

If it works like that, you would still have to be particularly vicious to create an application with a framework that is no longer supported and whose design does not allow you to add controls other than native Microsoft ones!

With VS 2022, we can see that the problem of Scintilla.NET does not come from the NET45 symbol but I see that you have done tests for .NET 5.0 and .NET 3.1 using Visual Studio Code. Microsoft VSC plugins may be a problem with unsupported platforms. Soon a version 5.3.2.8?

ps: I have the latest versions (x86 and x64) of scintilla and lexilla (Scintilla: 5.3.6, Lexilla: 5.2.6) at your disposal if you wish, which you could integrate into a new version of the package. scintilla-lexilla versions.zip

VPKSoft commented 1 year ago

Hi, Here is the PR for the fix: #72. This is slightly modified for the solution you posted here @lgaudouen. Also the version follows the Scintilla DLL version, so it is 5.3.6. I'll leave it hanging there for a day or so if any changes are requested but in my tests this seemed to work in both NET Framework and .NET.

lgaudouen commented 1 year ago

Could I have the slight modifications that you intend to make to my proposal contained in "modifications for .NETFramework 4.5.txt" included in the modified 5.3.2.7 package transmitted that you were therefore able to test? You write "version follows the Scintilla DLL version, so it is 5.3.6." : where is the source of the previous version? This could help me in the tests and various proposals for modification or correction already identified in Scintilla.Net 5.3.2.7 and ScintillaLexers.NET but which I have not yet informed you of. Thanks in advance. As we could write by completing an American formula, "one is never too young to teach nor too old to learn"...

VPKSoft commented 1 year ago

You write "version follows the Scintilla DLL version, so it is 5.3.6." : where is the source of the previous version?

Not all Scintilla versions have been released with Scintilla.NET, also not nearly all of the Scintilla control functionality is implemented. E.g. the new version certainly don't get new features from the Scintilla's changes. I'm kind of hoping those will be implemented by the community. I really don't have the time to implement everything by my self - OSS is a community effort. For the versioning I'm open to suggestion for changes, e.g. semantic versioning or something similar. Here is the zipped NuGet package: Scintilla.NET.5.3.6-NuGet.zip

lgaudouen commented 1 year ago

BAD NEWS! The solution I have advanced is not one: based on the CurrentDirectory, it does not work in any case or rather it works only in certain cases and under certain conditions. I hope to find a solution without going through the IDE or going through embedded dll.

VPKSoft commented 1 year ago

Too bad - well lets hope there is a simple solution to be found! I'll keep the PR open, but won't merge it yet.

lgaudouen commented 1 year ago

GOOD NEWS. Actually the issue I was having is due to VS 2022 17.7.2 which seems to not handle solutions well with different .NET framework and version projects. As for managing projects that don't use the same package, that's another matter...

Given your patch, of which I do not understand the motivation or the content, and despite this and as you will be able to read later, I have continued to improve what I had proposed to you. As for your fix, I must tell you my misunderstanding of everything I read and see:

Moreover, you gave the Scintilla.NET package the name ("5.3.6") of the version of scintilla.dll, which led you to modify the method of calculating its name in the dll whose name is version includes 4 fragments ("5.3.6.0"). Surely it's a good idea, except with this logic I don't see how nugget.org, github and VS2022 are going to manage when it's going to be necessary to fix bugs or improve Scintilla.NET without changing version builds of scintilla.dll and scintilla.dll.

Since you seem to fear the distinction between .NET and .NETFramework using the NET45 standard symbol used by version 5.3.2.7, I propose a new version of the static Scintilla() and private static string LocateNativeDllDirectory() functions without symbol conditioning, a clearer and more readable code and more tested. But it surely has the weakness of having a simple code and I wouldn't see the interest of complicating it if it works.

As you can see, I desynchronized the package name from the dll names of scintilla.dll and lexilla.dll otherwise I wouldn't have gotten away with it. The LocateNativeDllDirectory function has been simplified and without the NET45 symbol. It deals with the issue of the independence of the package name and the version of the generated dlls (except that as you surely know, in the project status, the version number of the package, for example, 6.0.2 for a package version 6.0.2-alpha.1 is used to give assemblies a 6.0.2.0 version by default, but you can modify the assembly numbers and file version (see https://learn.microsoft.com/en-us/nuget/concepts/package-versioning)

I join you in one zip file:

fix1.zip

Do not worry about the version number of the package that I transmit to you: in accordance with the rules (see https://learn.microsoft.com/en-us/nuget/concepts/package-versioning), the corrected version of the problem with the .NETFramework should be 5.2.8 after the present 5.2.7 (just a patch). In this beta package, don't worry, I won't steal from you: I replaced the VPK logo with my own just to make it easier to recognize in my tests (and maybe yours) the different versions of packages I've been brought to create for development and multiple testing before making it work with VS 2022 (toolbox not updating, even empty, crash of .NET designer and/or that of NETFramework, ...): I hope that the VS 2022 version 17.8 (in preview 3 since August 8th) will have solved these problems that require each package change to save everything, close all instances of VS 2022 and restart. But, curiously, resuming my tests after 4 hours spent in my garden, without any change, I do not encounter the problems that I had encountered for 48 hours with the back and forth of versions of the package. Strange! I just installed the last version of the day (17.7.3) of VS 2022 and, with my tests, I did not encounter the biggest problems (some remain).

If the modifications made seem to resolve the .NET Framework and .NET issues, design mode and runtime, I still have a few questions to deal with: 1 - the simplest: it seems to me that the Text property should be "[Browsable(false)]", modifying the value of this property in designmode seems to me to be of no interest when switching to runtime and disturbing in design mode. 2 - The question of the two Lexer and LexerName properties: currently, it is possible to choose the value of the Lexer property (displaying, for example, "Cpp" and to enter another value for the LexerName property (for example, "css" For a developer, it would seem safer and easier to distinguish the Lexer property of the Scintilla.NET control from the obsolete scintilla functions (by replacing them internally with lexername). 3 - The issue of appearance in design mode related to the CaretLineVisible, CaretLineVisibleAlways, CaretLineBackColor and CaretLineBackColorAlpha properties. When inserting a Scintilla.NET control, the name of the control appears clearly and the values of the CaretLineVisible, CaretLineVisibleAlways properties are set to False by default. If you close the form designer and reopen it, the CaretLineVisible property automatically changes to True.

These questions and others (notably those concerning margins and others collections) could lead to the realization of a real Scintilla.NET control designer. It wouldn't be simple (I've made several complex ones but only with .NETFramework) and we'd get out of a "simple" scintilla.dll wrapper, but we could have a nice product for developers who wouldn't have to constantly dive into the scintilla documentation ( a bit like Scintilla.Lexers is starting to do to Scintilla.NET).

As for taking into account the functions of scintilla by Scintilla.NET, I was preparing a private note on my reflections on this subject. You tell us that you really don't have the time to implement everything by yourself. For my part, retired, I've found for ten years that I have less time than before (you will see ...). But as I need it for my development tools, I will look closely at this question.

VPKSoft commented 1 year ago

I applied your changes to the PR #72 with version number of 5.2.8.

  1. Can do.
  2. I have been thinking approaches to this as the lexer index got deprecated in the Scintilla 5-series. First I thought that making the Lexer property obsolete be the obvious choice so it would follow the development of the native Scintilla control. But now I'm thinking the Enum would be more of a .NET approach as string constants are so easy to mistype, etc. and the enum would be internally translated to the Lexer name as it now is, only the enum requires extending to 100+ programming languages 🙂
  3. Needs a new issue and investigating #74.

I'll leave the PR open for a few days before merging and publishing...

lgaudouen commented 1 year ago

By testing Scintilla.Lexers.NET, I discovered that the behavior of VS 2022 (17.7.3) is different depending on whether the package is in a stable version (for example "5.3.2.7") or not (for example "6.0.1-alpha.8"). With a draft version, a public static void ClearStyle(Scintilla scintilla) function should be written public static void ClearStyle(ScintillaNET.Scintilla scintilla) to not cause a CS0118 error. This problem comes from the version name syntax for draft versions: I should have used "6.0.1-alpha8" instead of "6.0.1-alpha.8". But surely you knew that.

thehill383 commented 1 year ago

I am still experiencing an problem VS 2022 17.7.3 Scintilla.NET 5.3.2.8 .NET Framework 4.8

When dragging Scintilla from the toolbox i get

'System.InvalidOperationException: Unable to locate the Scintilla.NET satellite assemblies : directory 'C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\packages\Scintilla.NET.5.3.2.8\build\x64' not found at ScintillaNET.Scintilla.LocateNativeDllDirectory() at ScintillaNET.Scintilla..cctor()'

before realising there was still an issue i had published to the clickonce app and got the following which looks pretty similar System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.TypeInitializationException: The type initializer for 'ScintillaNET.Scintilla' threw an exception. ---> System.InvalidOperationException: Unable to locate the Scintilla.NET satellite assemblies : directory 'C:\Users\nhill\AppData\Local\Apps\2.0\20PXGJ54.VQ6\4VXEHVRE.HNR\ffsi..tion_6afae331d96cfc69_0001.0000_b5f850ca718670e1\packages\Scintilla.NET.5.3.2.8\build\x64' not found at ScintillaNET.Scintilla.LocateNativeDllDirectory() at ScintillaNET.Scintilla..cctor()

lgaudouen commented 1 year ago

I actually have the same problem as you with the package installed from nuget.org. I downloaded the Scintilla.NET source from Github and generated the package. With this package I have no problem. But, there is a difference between the two versions of Scintilla.NET.dll: that of the published nuget (which does not work) has a size of 226,816 bytes and that of the package that I regenerated (which works) has a size of 227,840 bytes: the published nuget therefore does not seem to correspond to the published source and those 24 bytes of difference make the difference...

VPKSoft commented 1 year ago

Here you go, a new release, worked at least on my test with WinForms designer. I probably messed up something with the versioning on the previous release.

lgaudouen commented 1 year ago

It seemed to me that it had to work... And it works now ! Furthermore, there are 2 files version.txt and versions.txt (with the same data) for the x86 subdirectory and only one (version.txt) for x64. It's my fault: I thought it was "smart" (?) to put it in the plural since with the separation of Scintilla and Lexilla there were two dlls whose versions are different and evolve independently.

thehill383 commented 1 year ago

Here you go, a new release, worked at least on my test with WinForms designer. I probably messed up something with the versioning on the previous release.

@VPKSoft brilliant thank you, i can confirm 5.3.2.9 is working fine now.

my project had become corrupted and there were references in there to 5.3.2.4 that wouldn't update. I ended up uninstalling and then reinstalling. I think that fixed it, might have had to do a bit of minor editing in the .csproj file

thehill383 commented 1 year ago

@lgaudouen sorry, forgot to say thanks to you for the work that you put into getting this resolved.

thehill383 commented 1 year ago

@lgaudouen i have realised that 5.3.2.9 still isn't working, when a solution is published via ClickOnce what do you think is the best course of action, open a new issue or try and continue one of the previous issues

lgaudouen commented 1 year ago

I have just received the email corresponding to your last post published on issue 68 (14 days ago!...) about the problem of installing an application with ClickOnce and you ask me for an opinion. Unfortunately, I currently have no opinion on the subject, having no expertise in ClickOnce. Fortunately, ClickOnce has interested me for several years and I am ready to invest my (little...) time on this issue. Unfortunately, I have no information about this "malfunction" of the Scintilla.NET 5.3.2.9 nuget. Perhaps you could provide additional information on the problem encountered (exceptions, ..) and why not a very (very ...) simple test solution which you are certain to encounter a problem when using ClickOnce.