WIP 8 .NET Plugin - Does not load libs

Hello,

I am studing the .NET 7.0 support of Rhino 8 WIP and I am having some very crazy issues with library loading.

Issue Summary
Trying to be as direct as possible: A plugin that is configured to support both WinForms and WPF presents issues when loading other NuGet packages. Sometimes it fails without error message, sometimes it throws an exception stating the dll is not found.

Versions
1- VIsual Studio 2022 17.7.6 (up to date as per the VS installer)
2- Rhino extension downloaded from the VS extension manager:


3- Rhino 8 BETA version:
image
4- Rhino NuGet package in the project (I updated using the NuGet Manager to the BETA version - the extension points to the WIP):
image

Steps to Reproduce Issue
1- Create new project Check Both Use Windows Forms and Use WPF. I need both checked because I want to host a WPF user control inside a rhino ETO panel.

2- If you try to build without changing anything, it will fail with the following error message:
error NETSDK1136: The target platform must be set to Windows (usually by including ‘-windows’ in the TargetFramework property) when using Windows Forms or WPF, or referencing projects or packages that do so.

Thus, I changed the project file with the following line (as @dale suggested in another post of mine):
<TargetFrameworks>net7.0-windows;net48</TargetFrameworks>

It builds and runs. If you call the default command, it makes the line as expected.

3- Add a 3rd party NuGet package. I will use the CsvHelper as example which I personally don’t expect to be so special:

Status: It still builds and the command adds the line.

4- Lets play with the RunCommand default function to just reference the added NuGet package.

    protected override Result RunCommand(RhinoDoc doc, RunMode mode)
    {
// ISSUE TESTER
      try
      {
        CsvReader reader = null; // <- ISSUE!!! COMMENT AND UNCOMMENT THIS LINE
        int a = 0; // Try adding a breakpoint here
        a++;
      }
      catch (Exception ex)
      {

        throw;
      }
// ISSUE TESTER

      
      // TODO: start here modifying the behaviour of your command.
      // ---
      RhinoApp.WriteLine("The {0} command will add a line right now.", EnglishName);

      Point3d pt0;
      using (GetPoint getPointAction = new GetPoint())
      {
        getPointAction.SetCommandPrompt("Please select the start point");
        if (getPointAction.Get() != GetResult.Point)
        {
          RhinoApp.WriteLine("No start point was selected.");
          return getPointAction.CommandResult();
        }
        pt0 = getPointAction.Point();
      }

      Point3d pt1;
      using (GetPoint getPointAction = new GetPoint())
      {
        getPointAction.SetCommandPrompt("Please select the end point");
        getPointAction.SetBasePoint(pt0, true);
        getPointAction.DynamicDraw +=
          (sender, e) => e.Display.DrawLine(pt0, e.CurrentPoint, System.Drawing.Color.DarkRed);
        if (getPointAction.Get() != GetResult.Point)
        {
          RhinoApp.WriteLine("No end point was selected.");
          return getPointAction.CommandResult();
        }
        pt1 = getPointAction.Point();
      }

      doc.Objects.AddLine(pt0, pt1);
      doc.Views.Redraw();
      RhinoApp.WriteLine("The {0} command added one line to the document.", EnglishName);

      // ---
      return Result.Success;
    }

Note that I just added a null reference to an object of the added library.

Add a breakpoint to the int a = 0; Line.

Status: It builds (DEBUG config).
When I hit F5 Rhino runs with the debugger.
I call the command and the weird behaviour happens.

if the CsvReader reader = null; is NOT commented, the command does nothing. There is this output in the Rhino console: Command: LibLoadPluginTestCommand but nothing else. The breakpoint at line int a = 0; is not hit.

Now, if the CsvReader reader = null; IS commented, the breakpoint at line int a = 0; is hit and the normal behaviour happens.


I am having the very same issue with other libraries. If seems that the plugins cannot load other libs. I suspect it is something related to adding -windows to the target platform for net7.0 - but this is required if we are to use WinForms.

The zipped test project is herein attached:
LibLoadPluginTest.zip (422.4 KB)

Thank you for your assistance.

CSVHelper may need 4.x?

What happens if you

  1. breakpoint the ‘throw’ line in that try/catch. Does it throw an exception, and if so which one and what are the details?
    separately
  2. put in a couple of outputs (rhino command line, messagebox, whatever) between each line. Try that in the debug and release builds: what gets executed, is an exception thrown, etc.

Hi @Nathan_Bossett

I don’t think this is a problem with the NuGet package because I am having issues also with different packages and the NuGet package in question does state that there is no dependency for .NET 7.0

But I found more information while digging into it. The main problem is that, well, allowing for both .NET Core 7 and Framework 4.8 is rather confusing.

I found the issue. I do not know that much to state if this is a bug relative to the project templates.

What happens is that, when you build, two versions are actually built (NET7.0 and NET48) and this is what you get in the output folders:
BIN
image

net48
image

net7.0-windows
image

Note that the example CsvHelper.dll DLL is not copied to the net7.0-windows folder.

Then, in visual studio, we can set which target framework to run and to debug.
image

It works when:
1- I copy manually the CsvHelper.dll file from the net48 to the net7.0-windows folder.
2- Set-up the visual studio Framework to net7.0-windows

So, the issue is that the net7.0-windows cannot find the CsvHelper.dll. Should it be copied to the net7.0-windows output folder?

Thus, this seems to be an issue with the project template (there is already the issue with it not adding the -windows when you set WinForms).

@curtisw, I noticed that you are involved in the extension projects in GitHub and that you added some videos explaining how to use it. Perhaps you could share some thoughts?

Thank you

Hey @scudelari,

With .NET 7, nuget packages aren’t copied locally by default and are resolved at runtime, unless you publish your project.

To copy all nuget dependencies to the output folder with a debug build you can add this to your .csproj:

<PropertyGroup>
  <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>

See documentation about it here.

Another one that would also work is setting <EnableDynamicLoading> to true.

I’ve added RH-77962 for myself to update the project templates to include this when it includes .NET 7 as a target.

Hope this helps!

2 Likes

Hi @curtisw
Thank you for the assistance. It worked (the dependencies are copied to the net7.0 output folder and Rhino loads everything correctly.
Since we are talking about this, I have the following warning when I compile my plugin project.
warning NETSDK1137: It is no longer necessary to use the Microsoft.NET.Sdk.WindowsDesktop SDK. Consider changing the Sdk attribute of the root Project element to ‘Microsoft.NET.Sdk’.

There is a MS reference on this:

Perhaps it would make sense to change as well the template of the .csproj so that it would have

<Project Sdk="Microsoft.NET.Sdk">

instead of the current

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">

Thanks,

Hey @scudelari, thanks for bringing this up. The Sdk attribute is still used as it is required when compiling for net48 to properly support WPF/WinForms in Visual Studio. If there is a way to remove it while still supporting .xaml and winforms designers I’d certainly switch over to that, but I haven’t found a way yet.

Hi @curtisw, No problem. I’m glad to help.

I am finding some clashes in the project setup.

The issue is that if I set my project to reference the RhinoCommon NuGet package, the RhinoCommon.dll will be copied to the output folder if the CopyLocalLockFileAssemblies is set to true. And, Rhino complains if the RhinoCommon file is found alongside with the plugin.

I tried using this filtering that I found on the internet, but it doesn’t seem to work…

  <Target Name="FilterCopyLocalItems" AfterTargets="ResolveLockFileCopyLocalProjectDeps">
    <ItemGroup>
      <ReferenceCopyLocalPaths Remove="@(ReferenceCopyLocalPaths)" Condition="'%(Filename)' == 'RhinoCommon'" />
    </ItemGroup>
  </Target>

The workaround is to reference the RhinoCommon.dll from the install folder, but I feel somehow that using the NuGet package would be cleaner (otherwise why having the RhinoCommon NuGet in the first place?)

Anyhow, I just wanted to signal this out as well.

Thanks,

I’m not seeing the same issue, the RhinoCommon nuget package should be automatically omitting them. Perhaps you are referencing an old version of the nuget package?

Regardless, if you reference RhinoCommon like so in your csproj it shouldn’t copy them over:

<PackageReference Include="RhinoCommon" Version="8.0.23304.9001" IncludeAssets="compile;build" />