Working with native dlls

as suggested by @tonfilm in the chat I, or let’s say chatgpt, created a .net wrapper for a native lib i want to call. I(!) have set the dll name to the one I’m using, without any path. Here’s the wrapper:

namespace TetGenNativeWrapper
{
    using System;
    using System.Runtime.InteropServices;

    // Define a class that wraps the native DLL functions
    public class NativeDllWrapper
    {
        // Declare the native functions as static extern methods with [DllImport] attribute
        // Use the appropriate calling convention, parameter types, and marshaling options
        [DllImport("TetGen2vvvv.dll")]
        public static extern int tetCalculate(String behaviour, double[] vertXYZ, double[] vertAttr, int[] vertMarker, int[] numPoly, int[] numVertices, int[] vertIndex, int[] numFHoles, double[] fHoleXYZ, int[] facetMarker, double[] HoleXYZ, double[] RegionXYZ, double[] RegionAttrib, double[] RegionVolConst, int[] binSizes, String fileName);
    }

// Define a class that exposes the native DLL functions to other .NET assemblies
    // You can use the same name and signature as the native functions, or provide a more idiomatic .NET API
    public class NativeDllAdapter
    {
        // You can use the same name and signature as the native functions
        // Just call the wrapper methods internally
        public static int NativeFunction1(String behaviour, double[] vertXYZ, double[] vertAttr, int[] vertMarker, int[] numPoly, int[] numVertices, int[] vertIndex, int[] numFHoles, double[] fHoleXYZ, int[] facetMarker, double[] HoleXYZ, double[] RegionXYZ, double[] RegionAttrib, double[] RegionVolConst, int[] binSizes, String fileName)
        {
            return NativeDllWrapper.tetCalculate(behaviour, vertXYZ, vertAttr, vertMarker, numPoly, numVertices, vertIndex, numFHoles, fHoleXYZ, facetMarker, HoleXYZ, RegionXYZ,RegionAttrib,RegionVolConst,binSizes, fileName);
        }
    }

}

my file structure looks like this:
VL.TetGen.vl
/runtimes/win-64/native/TetGen2vvvv.dll
/lib/Net 6.0/TetGenNativeWrapper.dll

and here’s the error I get after referencing TetGenNativeWrapper.dll and calling the function:

TetGen2vvvv.dll doesn’t have any further dependencies…

help :|

did you follow the instructions to create a pack?

https://thegraybook.vvvv.org/reference/extending/creating.html

The help sebescudie and I gave in the chat assumes that you will set up a vl pack. If not, you need to make sure the folder is in the search path, like so:

https://thegraybook.vvvv.org/reference/libraries/referencing.html#unmanagednative-dependencies

I did. I didn’t have the nuspec file yet and my folder names were not exactly the same as the vl file - i fixed this, but it still can’t find the dll, so something else is off…

I have attached the project and the wrapper solution; the c++ code is on github.
For reference here’s also my old code of loading the dll in the beta plugin (it also does a few things to load the dll correctly, taken from your OpenVR implementation, which I assume can be ignored, left it here just in case):

// dll loading code copied from tonfilm.s VVVV.OpenVR
        private class UnsafeNativeMethods
        {
            [DllImport("kernel32.dll", SetLastError = true)]
            public static extern bool SetDllDirectory(string lpPathName);

            [DllImport("kernel32.dll", SetLastError = true)]
            public static extern int GetDllDirectory(int bufsize, StringBuilder buf);

            [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
            public static extern IntPtr LoadLibrary(string librayName);
        }

        public static string CoreAssemblyNativeDir
        {
            get
            {
                //get the full location of the assembly with DaoTests in it
                string fullPath = Assembly.GetAssembly(typeof(C3dTetrahedralizeNode)).Location;
                var subfolder = Environment.Is64BitProcess ? "x64" : "x86";

                //get the folder that's in
                return Path.Combine(Path.GetDirectoryName(fullPath), subfolder);
            }
        }

        public static void LoadDllFile(string dllfolder, string libname)
        {
            var currentpath = new StringBuilder(255);
            var length = UnsafeNativeMethods.GetDllDirectory(currentpath.Length, currentpath);

            // use new path
            var success = UnsafeNativeMethods.SetDllDirectory(dllfolder);

            if (success)
            {
                var handle = UnsafeNativeMethods.LoadLibrary(libname);
                success = handle.ToInt64() > 0;
            }

            // restore old path
            UnsafeNativeMethods.SetDllDirectory(currentpath.ToString());
        }
        static C3dTetrahedralizeNode()
         {
            LoadDllFile(CoreAssemblyNativeDir, "Tetgen2vvvv.dll");
            //add dependencies folder to path when used as a standalone plugin
            var platform = IntPtr.Size == 4 ? "x86" : "x64";
            var pathToThisAssembly = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
//            var pathToBinFolder = Path.Combine(pathToThisAssembly, "dependencies");
            var pathToBinFolder = Path.Combine(pathToThisAssembly, "dependencies", platform);
            var envPath = Environment.GetEnvironmentVariable("PATH");
            envPath = string.Format("{0};{1}", envPath, pathToBinFolder);
            Environment.SetEnvironmentVariable("PATH", envPath);

            
        }


        /// dll import
        [System.Runtime.InteropServices.DllImport("TetGen2vvvv.dll")]
        private static extern IntPtr tetCalculate(String behaviour, double[] vertXYZ, double[] vertAttr, int[] vertMarker, int[] numPoly, int[] numVertices, int[] vertIndex, int[] numFHoles, double[] fHoleXYZ, int[] facetMarker, double[] HoleXYZ, double[] RegionXYZ, double[] RegionAttrib, double[] RegionVolConst, int[] binSizes, String fileName);

thank you

VL.TetGen.zip (693.3 KB)

It would be much easier to follow you and give advice if you had a VL.TetGen repo on your github. Only this way we can exactly replicate what you see on your end.

here you go:

Any reason to not include the sources of the c# wrapper in this repo instead of the \lib\net6.0\TetGenNativeWrapper.dll?

I think you can use https://github.com/lucasg/Dependencies to check where exactly your wrapper DLL expects the native one to be located and/or what else might be missing.

3 Likes

No reason, just not clear on the process yet. You suggest to write the wrapper as explained here? Writing nodes using C# | vvvv gamma documentation
I simply tried to do what tebjan suggested in the chat:

You cannot reference native dlls in a vl doc. You can only reference .NET dlls. But a .NET dll can use interop to call native dlls. The native one then needs to be in the runtime folder in order to get found by the .NET dll when it’s loaded by vvvv.

aha, will try that, thank you!

@joreg not sure if that’s what you meant, but I’ve tried creating my wrapper by adding C# via gamma UI, same result…

I’ve tried dependency walker on my wrapper dll as suggested by @bjoern, and indeed it’s unable to find the native dll. Unfortunately it also won’t tell me where it’s looking for it.

Or what we see here is supposed to be the whole path and I do have to specify something else than just the dll name as path in the wrapper…when I put the TetGen2vvvv.dll next to the vvvv.exe it seems to work (i still get an error thrown by external component, but it seems to be loading it).

thank you

Likely it has to be under windows/system32 or maybe, in the root of gamma directory that you use

it is not about how you create the code for the interop dll but that it should be in the repo so that we can fully reproduce your situation.

@joreg lol, ok, now I got you. The full code is literally what’s in my first post, so I didn’t follow…I have now added the c# file via the gamma UI and pushed it to the repo.

another side question which just came up here: how do I set a c# node to category “3D.Something…”? I’d like to be this in 3D.Geometry, but this doesn’t seem to work as namespace, eg Geometry works for me though…

and: would you now include the source of the wrapper in the pack/nuget, or still build it? Somewhere I read I shouldn’t reference csproj. files in nugets…? Can I just add the .cs?

yes, next to the vvvv exe it seems to load it, but this way it’s cumbersome to ship…works for other projects too though, so something else is not correct…

thanks!

ok, please see my PR. like this at least i see the tetCalculate node in the application of VL.TetGen.vl. does that help?

yes definitely include it, but the way my PR does (ie, as a project that has to be manually built) and not by referencing the .csproj which indeed wouldn’t work when deploying this as a nuget.

in c# namespaces must not start with a number. here is where forwarding comes in that would allow you to make naming changes like this because VL does allow categories to start with numbers.

ok, first of all: thanks, this has been the first ever PR for me on github :D
I could see the tetCalculate node before just fine, the problem seems to be in finding the native dll:

this part I don’t get - you have also added /lib to gitignore and I shouldn’t link the .csproj - I assume we don’t want the library users to have to build the wrapper themselves?

aha. so with this wrapper method, tetCalculate, I will patch the process to be used by the end user, for which I can set the namespace in VL. The wrapper method itself I’d like to still show up eg in the Internal category in 3D.Geometry, so I create a forward node, correct?

thank you

is this still the problem for you with my PR? i didn’t see that problem.

right, endusers will just install the nuget and be good to go, because one part of the nuget packaging process will be building the wrapper dll into /lib. but anyone working on the source repo will have to build the wrapper manually.

yes.

yes. when you run the if region, it turns pink for me with the above error. It’s your PR with the wrapper project built using dotnet build cmd

aha, got it, thanks

hm… the error i get is: “Critical: External component has thrown an exception.” sounding like it actually executed but failed.

so i wonder if you have some old binaries around that are interfering or you’re still running a somehow different setup.

let’s see:

  • delete your VL.TetGen and do a fresh clone of your repo to make sure we don’t have any zombie files in your local repo from previous attempts
  • build the VL.TetGen\src\NativeWrapper.csproj project which will build some files into VL.TetGen\lib\net6.0
  • start vvvv using --package-repositories "C:\vl-libs" (assuming your \VL.TetGen is in C:\vl-libs)
  • open VL.TetGen.vl

that’s the one I’m hoping to get :)

I did everything as instructed, now I don’t get an error at all. I assume I should also see the error you see, since we’re not feeding any valid data to it…?

Tried creating a quick process node with tetCalculate in it and load VL.TetGen.vl as dependency, but the process node won’t show up in the node browser…

I’m on windows 11, tried with 5.2 and 5.3.0423,

sorry, i forgot one thing. the reason you don’t see anything is that you’re working in a readonly pack, ie. any change or bang you make in that document is not doing anything.

so you have to also start vvvv with argument: --editable-packages VL.TetGen

I’m using launcher, the full “Extra args” im giving now are
--package-repositories "C:\vl-libs" --editable-packages VL.TetGen
(I have actually changed my dir to vl-libs, to make sure I’m doing the exact same thing…)

Now, when I run the if region, vvvv just silently dies. I’ve attached VS and got this error on crash

I don’t get the error you get, but I’d assume that this error is ok and I’m actually talking to the native dll now- I’ll first have to build up a patch around tetCalculate to confirm it’s working as expected…will be back here otherwise :)

What do your instructions above mean for my dev workflow though - to work on TetGen pack I’ll have to run it with these extra args everytime, or was this just to confirm something? Should I proceed like this for all packs accessing native dlls- I do have a few of them planned…?

thank you