Interoperability with .NET

Hi @oheil, sure, by self-contained c# application i mean the usual .NET Core self-contained publish step that ships the .NET core runtime, i was wondering if we could also ship the Julia runtime along and have both languages packaged into this self-contained published .exe.

Example, when we publish any .NET core application it packages a bunch of System.XXX.dll like System.Collections.dll, i was wondering if we could also package the libjulia.dlls and it’s dependencies in order to have everything in one package, i think this would allow for very powerful applications.

Ah, ok. No, I am not this far with my application yet. When it comes to publishing everything using a installer my thoughts were on NSIS just because I used it before.
I will check this out when times come but I expect some month to come .

Ah ok, i was just wondering the applicability of this approach since PackageCompiler.jl is not very nice yet. I think my problem is the fact that distributing a Julia application is so hard right now, maybe merging it with C# could mitigate this.

I recently got some likes on this thread, so I opened it again to see what I did here.
The project I worked on is publicly available: https://github.com/oheil/doTimeTable/
and shows how Julia can be embedded in a complete C# application installer. Which means, installing the application is all the user needs to do (no preinstalling of Julia or something like that).
The link from C# to Julia is mainly in GUI/doTimeTable/Julia.cs. Julia is called in a separate thread.

9 Likes

Nice work oheil. I had to figure this out at a previous job - it was not fun.

Note: Repost to give greater detail

Here is my library on Julia & .NET Interoperability: GitHub - HyperSphereStudio/JULIAdotNET

It uses the C-Interface of both languages to translate between the languages very quickly compared to normal server/client based systems.

 Julia.Init();
  JLFun fun = Julia.Eval("t(x) = sqrt(x)");
  double result = (double) fun.Invoke(2);
  object dotNetObject = fun.Invoke(3).Value;
  Julia.Exit(0);  

It is very new but it has support for various built in Julia features such as a JLArray to allow manipulating jl_arrays neatly.

You can also work with .NET from Julia (Experimental at the moment). It utilizes .NET reflection

In C#:

public class TestClass{
    public long g;
    public TestClass(long g) => this.g = g;
}

Working from Julia:

sharpType = SharpType("TestClass")  #Retrieve Type
sharpCon = SharpConstructor(sharpType, 0)  #Get Constructor at Index 0
o = sharpCon(6) #Create Instance
sharpField = SharpField(sharpType, "g") #Get Field
println(sharpField(o)) #Get Field Value
9 Likes

nice job!

I was looking through the source and i noticed this - does this fail on non-windows? https://github.com/HyperSphereStudio/JULIA.Net/blob/25a8ad39b1802288dc07480de21ea5a48444960a/src/csharp/Julia.cs#L20

I am using the .net sdk on macosx.

I don’t know C# but if this works I might have a go and trying to write a F# typeprovider

Oh lol I forgot about that part. Any thoughts on multiplatform Julia Dir lookup? I provided a method to give your own dir but it would probaly be a good idea to have a default lookup. Also there is a kernel32 call which is not multiplatform.

I think those are the only two things that make it not multiplatform. Unfortunately I dont know anything about macOS. If anyone knows how to do the setdlldirectory / finding julia dir on macos, let me know

I think I wrote it in .Net framework aswell since I made this for another project. Ill port it over to .Net 5

1 Like

This looks really cool! Thanks for making it.

I actually just finished developing an interface between Julia and .NET for an internal project based on information from @oheil .
When I started that, I was a newcomer to both Julia and C#/VB.net so it seemed infeasible with the C interface.
So I did it using communications over pipes, like a client-server communication.
Though this takes time, (de-)serialization seems to be fine (i.e negligible to the user) in most cases, even with relatively large arrays (about tens of thousands of double precision numbers).

I do not know if you tackled this issue already but the main missing part for me in the documentation about julia’s C interface was how to use it properly to load and interact with modules and how to handle special types. I was completely lost i the whole packing / unpacking of pointers to structures…

Also, there are some base types that exist in Julia that do not in .NET, so some conversions need to be done explicitely. For instance I discovered that my .NET (maybe it is a matter of version) did not support Int8.

Keep up the work ! Cheers !

EDIT : See this answer from @Shuhua maybe its worth converging on this Correct process to use julia code from C# or VB .NET - #17 by Shuhua

3 Likes

I found almost no documentation, I just straight up read the Julia source code instead lol.

Regarding primitive types that are not cannot be converted, I personally have not run into any issues but I can have it promote them into larger containers if need be (Int8 → Int32 etc) but I havent had any issues so far.

I have just released the .NET 5.0 version of the project

Thanks for the link to the other thread, Ill see if at @Shuhua is willing to work together as I see he has done some work on natively converting C# arrays to Julia etc

Btw, Thoughts on implementing an abstract method interface for functions like what RuntimeMethod has and stuff? Can make it seem like a normal “method” object

Ok I think I made it multiplatform now. I have no way to test it on linux though.

Change → SetDllDirectory to Environment.CurrentDirectory
Change → “where.exe” → Windows ? “where.exe” : “which”

Does this look like it would work?

I will try it tonight.

which seems like the right command to me.

Once GitHub - JuliaLang/juliaup: Julia installer and version multiplexer becomes more widely deployed you can just execute juliaup api getconfig1 and get back a JSON with information about installed Julia versions. At the moment this only works on Windows and if the user installed Julia from the Windows store, but I am very, very close to releasing the Mac and Linux version of this as well. The VS Code extension already tries this method to find the Julia binary, and then falls back to other ways if that doesn’t work.

So, this is more a FYI, maybe not entirely ready yet to be used, but should be much more ready in a few weeks.

2 Likes

Sorry but I am not that familiar with .NET at the moment ! :wink:

Take a look at the DotNET.jl.
It provides a more friendly call syntax.

julia> T"System.String".new('6',Int32(3))
"666"

julia> List=T"System.Collections.Generic.List`1"
System.Collections.Generic.List`1[T]

julia> List.new[T"System.Int64"]()
System.Collections.Generic.List`1[System.Int64]("System.Collections.Generic.List`1[System.Int64]")
1 Like

Thanks, will do!

I will try to implement something like


@CSusing System 


strT = T"String"
my_str = strT("Test")
str_len = my_str.Length

For the using implementation, probaly have it store them all in a list for each “using JuliaInterface” context? Is it possible to create a variable when a module is “used”?

Then read the list in reverse order so last added using statement is checked first?

Added.

Here is the current Julia.NET .NET → Julia Interface:

  @netusing Test
   @netusing System
   val1 = T"ReflectionTestClass".TestStateField[]   #< Requires [] to actually get the field. If you dont put [] or () then it will just return the FieldInfo object
   val2= T"ReflectionTestClass".StaticMethod[]() #To call a method. If you dont put [] or () then it will just return the MethodInfo object
   val3 = T"ReflectionTestClass".StaticGenericMethod[T"Int64"]() "To call a generic method, put the generic types in []

item = T"ReflectionTestClass"".new[](3)     #To call a constructor.  If you dont put [] or () then it will just return the ConstructorInfo object
   val4 = item.InstanceMethod[]();   #To call a instance method
   val5 = item.g[]       #To Access a instance field
   myGenericItem = T"ReflectionGenericTestClass`1".new[T"Int64"](3)    #To Create a generic instance of an object, put the generic types in [].
   boxed5 = sharpbox(5)   #Will return the sharp object of the long value "5"
   shouldBe5 = shapunbox(boxed5) #Will unbox the sharp object and return to native julia value
   myClass = T"Test.ReflectionTestClass"   #<= Perform Assembly Search and Return the Sharp Type
   myClass2 = P"Test.ReflectionTestClass"   #<= Perform one time assembly search and store the sharp type in a internal array (Reccommended for fast lookups)
   myClass3 = G"Test.ReflectionTestClass"   #Get from internal array
   myClass4 = R"Test.ReflectionTestClass"   #Remove from internal array
   handle = pin(sharpbox(5)) #Pin the Sharp object in the Sharp GC
   free(handle) #Will also auto free. You can also treat it like stream and put it in do end block
1 Like

I tried it on OSX. It seemed to throw in this function.

Glanced at the source and it seems like your approach for Linux should work in any POSIX-like systems .net supports (OSX, FreeBSD: OSPlatform.OSX Property (System.Runtime.InteropServices) | Microsoft Learn)

https://github.com/HyperSphereStudio/JULIA.Net/blob/main/src/csharp/OperatingEnvironment.cs#L21

welcome Experts,
Recently we have developed GUI using Avalonia C# + Julia
Akkugif

waiting for your suggestion & Support :slight_smile:

GitHub link: https://github.com/PiResearchTech/Ayudha

Recently works well in Windows & Ubuntu
Thank you.

2 Likes