Using .NET with UnrealScript - c#

UDK uses .NET. So maybe it is possible somehow to use .NET from UnrealScript?
It is really great to use C# with UnrealScript.
One certainly could build C++ layer to interact between .NET and UnrealScript which will use dllimport, but it is not the subject for this question.

So there seems to be no way to access .NET libraries directly from UnrealScript, but one can combine [DllExport] extension for C# and UnrealScript interop system to interact with .NET without intermediate C++ wrapper.
Lets look at simple example of exchanging with int, string, structure and filling UnrealScript String in C#.
1 Create C# class
using System;
using System.Runtime.InteropServices;
using RGiesecke.DllExport;
namespace UDKManagedTestDLL
{
struct TestStruct
{
public int Value;
}
public static class UnmanagedExports
{
// Get string from C#
// returned strings are copied by UnrealScript interop system so one
// shouldn't worry about allocation\deallocation problem
[DllExport("GetString", CallingConvention = CallingConvention.StdCall]
[return: MarshalAs(UnmanagedType.LPWStr)]
static string GetString()
{
return "Hello UnrealScript from C#!";
}
//This function takes int, squares it and return a structure
[DllExport("GetStructure", CallingConvention = CallingConvention.StdCall]
static TestStructure GetStructure(int x)
{
return new TestStructure{Value=x*x};
}
//This function fills UnrealScript string
//(!) warning (!) the string should be initialized (memory allocated) in UnrealScript
// see example of usage below
[DllExport("FillString", CallingConvention = CallingConvention.StdCall]
static void FillString([MarshalAs(UnmanagedType.LPWStr)] StringBuilder str)
{
str.Clear(); //set position to the beginning of the string
str.Append("ha ha ha");
}
}
}
2 Compile the C# code, say, as UDKManagedTest.dll and place to [\Binaries\Win32\UserCode] (or Win64)
3 On UnrealScript side one should place declarations of the functions:
class TestManagedDLL extends Object
DLLBind(UDKManagedTest);
struct TestStruct
{
int Value;
}
dllimport final function string GetString();
dllimport final function TestStruct GetStructure();
dllimport final function FillString(out string str);
DefaultProperties
{
}
Then one can use the functions.
The only trick is filling UDK string like it is shown in FillString method. Since we pass string as fixed length buffer, this string MUST be initialized. The length of initialized string MUST be more or equal to length C# will work with.
Further reading could be found here.

Related

Return function value from dll c++ to c# [duplicate]

I have a C++ function that produces a list of rectangles that are interesting. I want to be able to get that list out of the C++ library and back into the C# application that is calling it.
So far, I'm encoding the rectangles like so:
struct ImagePatch{
int xmin, xmax, ymin, ymax;
}
and then encoding some vectors:
void MyFunc(..., std::vector<int>& rectanglePoints){
std::vector<ImagePatch> patches; //this is filled with rectangles
for(i = 0; i < patches.size(); i++){
rectanglePoints.push_back(patches[i].xmin);
rectanglePoints.push_back(patches[i].xmax);
rectanglePoints.push_back(patches[i].ymin);
rectanglePoints.push_back(patches[i].ymax);
}
}
The header for interacting with C# looks like (and works for a bunch of other functions):
extern "C" {
__declspec(dllexport) void __cdecl MyFunc(..., std::vector<int>& rectanglePoints);
}
Are there some keywords or other things I can do to get that set of rectangles out? I found this article for marshalling objects in C#, but it seems way too complicated and way too underexplained. Is a vector of integers the right way to do this, or is there some other trick or approach?
The STL is a C++ specific library, so you cant directly get it across as one object to C#.
The one thing that is guaranteed about std::vector is that &v[0] points to the first element and all the elements lie linearly in memory (in other words, its just like a C array in terms of memory layout)
So marshal as array of int... which shouldn't be hard - There are lot of examples on the web.
Added
Assuming you only pass the data from C++ to C# :
C# cannot handle a C++ vector object, so do not try passing it by reference : Instead your C++ code must return a pointer to an array of ints...
If you are not going to be using this function from multiple threads, you can use static storage :
int *getRects(bool bClear)
{
static vector<int> v; // This variable persists across invocations
if(bClear)
{
v.swap(vector<int>());
}
else
{
v.clear();
// Fill v with data as you wish
}
return v.size() ? &v[0] : NULL;
}
call getRects(true) if the returned data is significant in size, so you release the memory in v.
For simplicity, instead of passing out the size of the vector data too, just put a sentinel value at the end (like say -1) so the C# code can detect where the data ends.
Yes. You can. Actually, not just std::vector, std::string, std::wstring, any standard C++ class or your own classes can be marshaled or instantiated and called from C#/.NET.
Wrapping a std::vector<any_type> in C# is indeed possible with just regular P/Invoke Interop, it is complicated though. even a std::map of any type can be done in C#/.NET.
public class SampleClass : IDisposable
{
[DllImport("YourDll.dll", EntryPoint="ConstructorOfYourClass", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void SampleClassConstructor(IntPtr thisObject);
[DllImport("YourDll.dll", EntryPoint="DestructorOfYourClass", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void SampleClassDestructor(IntPtr thisObject);
[DllImport("YourDll.dll", EntryPoint="DoSomething", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void DoSomething(IntPtr thisObject);
[DllImport("YourDll.dll", EntryPoint="DoSomethingElse", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void DoSomething(IntPtr thisObject, int x);
IntPtr ptr;
public SampleClass(int sizeOfYourCppClass)
{
this.ptr = Marshal.AllocHGlobal(sizeOfYourCppClass);
SampleClassConstructor(this.ptr);
}
public void DoSomething()
{
DoSomething(this.ptr);
}
public void DoSomethingElse(int x)
{
DoSomethingElse(this.ptr, x);
}
public void Dispose()
{
if (this.ptr != IntPtr.Zero)
{
// The following 2 calls equals to "delete object" in C++
// Calling the destructor of the C++ class will free the memory allocated by the native c++ class.
SampleClassDestructor(this.ptr);
// Free the memory allocated from .NET.
Marshal.FreeHGlobal(this.ptr);
this.ptr = IntPtr.Zero;
}
}
}
Please see the below link,
C#/.NET PInvoke Interop SDK
(I am the author of the SDK tool)
Once you have the C# wrapper class for your C++ class ready, it is easy to implement ICustomMarshaler so that you can marshal the C++ object from .NET.
http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.icustommarshaler.aspx
I'm pretty sure you can't do this. You have to be able to translate the C++ code directly to a C# class, so you would at least have to replicate the internals of the vector class to marshall it correctly. I'm also pretty sure you won't be able to move references across the boundary, you'll have to use IntPtr (raw pointers). The approach that i know works is to marshall a raw array of the structs.

Getting address of a struct

Here is my code:
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
using System;
using System.IO;
public class CarMove : MonoBehaviour
{
public unsafe struct TrafficRoadSystem { };
public unsafe struct CarsSimulation { };
public unsafe struct CarPostion
{
double position_x;
double position_y;
double position_z;
};
// Use this for initialization
[DllImport("Assets/traffic.dll", CharSet = CharSet.Ansi)]
public unsafe static extern int traffic_import_osm([MarshalAs(UnmanagedType.LPStr)] string osm_pbf_path, TrafficRoadSystem** out_road_system);
[DllImport("Assets/traffic.dll")]
public unsafe static extern int create_simulation(TrafficRoadSystem* road_system, CarsSimulation** out_simulation, double speed, int cars_count);
[DllImport("Assets/traffic.dll")]
public static extern void update_simulation(ref CarsSimulation simulation, double time);
[DllImport("Assets/traffic.dll")]
public static extern CarPostion get_car_postion(ref CarsSimulation simulation, int car_number);
unsafe CarsSimulation* carSimulation;
unsafe TrafficRoadSystem* trafficRoadSystem;
void Start()
{
unsafe
{
string osmPath = "Assets/Resources/map.osm.pbf";
int results;
results = traffic_import_osm(osmPath, &trafficRoadSystem);
}
}
// Update is called once per frame
void Update()
{
}
}
This is from dll C library. Function int traffic_import_osm() works on TrafficRoadSystem* trafficRoadSystem; object as reference and I want to have in access to object in void Update(). This works well in one function but I cannot get address of a class variable and I get error
Error CS0212 You can only take the address of an unfixed expression inside of a fixed statement initializer
in line results = traffic_import_osm(osmPath, &trafficRoadSystem);
I try to use this solution
https://msdn.microsoft.com/en-us/library/29ak9b70(v=vs.90).aspx
I wrote this:
TrafficRoadSystem trafficRoadSystem;
void Start()
{
unsafe
{
string osmPath = "Assets/Resources/map.osm.pbf";
CarMove carMove = new CarMove();
int results;
fixed( TrafficRoadSystem* ptr = &carMove.trafficRoadSystem)
{
results = traffic_import_osm(osmPath, &ptr);
}
}
}
and I get error CS0459 Cannot take the address of a read-only local variable in line
results = traffic_import_osm(osmPath, &ptr);
Making a C or C++ plugin in Unity requires extensive knowledge of these languages. This means you should spend time learning pointers before attempting to use raw pointer in Unity. Because even if you get it to compile, you may also run into hard to fix crashes.
You have:
unsafe TrafficRoadSystem* trafficRoadSystem;
and want to pass it to the function below:
public unsafe static extern int traffic_import_osm(..., TrafficRoadSystem** out_road_system);
1.The trafficRoadSystem variable is a pointer and you will need to make another pointer that points to trafficRoadSystem. This is called "pointers to pointer"
TrafficRoadSystem** addressOfTrafficRoadSystem = &trafficRoadSystem;
Notice the double "**".
2.You must use fixed keyword to do what I mentione in #1.
fixed (TrafficRoadSystem** addressOfTrafficRoadSystem = &trafficRoadSystem)
{
}
3.You can pass that pointer to pointer address to the traffic_import_osm function.
The whole new Start function:
void Start()
{
unsafe
{
fixed (TrafficRoadSystem** addressOfTrafficRoadSystem = &trafficRoadSystem)
{
string osmPath = "Assets/Resources/map.osm.pbf";
int results;
results = traffic_import_osm(osmPath, addressOfTrafficRoadSystem);
}
}
}
Unrelated possible future issues:
1.I noticed that you did CarMove carMove = new CarMove(); in your updated question. Don't use the new keyword here because CarMove inherits from MonoBehaviour. See this answer for what to do.
2.I also noticed you used "Assets/Resources/map.osm.pbf";. This path will be invalid when you build your project. Consider using the StreamingAssets folder instead of the Resources folder which only works with the Resources API. You use Application.streamingAssetsPath with the StreamingAssets folder.
An alternative to using raw Pointers is to use Marshalling. Marshalling allows you to take C# data structures and transform them to C-understandable data without the need of any unsafe-keywords.
Mono has a document describing how to properly interact with native libraries using PInvoke (which is the search term you're looking for).
Here, here and here you can also find an introduction into PInvoke and Marshalling.
Basically you replace the pointer signatures with the IntPtr-type, which is a managed version of a pointer. To transform your managed struct variable you'll first need to allocate a memory region and use it for your struct
IntPtr ptr;
TrafficRoadSystem _struct;
try {
ptr = Marshal.AllocHGlobal(Marshal.SizeOf(_struct));
Marshal.StructureToPtr(_struct, ptr, false);
} finally {
Marshal.FreeHGlobal(ptr);
}
I don't know how to properly deal with double pointers though..

Passing a class containing a StringBuilder from C# to ada

I'm working in Unity C#, trying to integrate with a dll written in ada. I need to pass a class containing strings and doubles to the dll for it to modify and return, but I get the following error:
"Type System.Text.StringBuilder which is passed to unmanaged code must have a StructLayout attribute."
Now, these classes already have a StructLayoutAttribute. And I can't put a StructLayout attribute on a Stringbuilder, Unity tells me it only works on classes and structs. Which makes me really confused about the error message, because it seems like it's asking me for something that's impossible?
public class controller : MonoBehavior{
myClass student = new myClass();
[DllImport ("my_dll", EntryPoint="myFunction#12")]
public static extern void myFunction(myClass c);
void Start(){
myFunction (student);
}
}
[StructLayoutAttribute(LayoutKind.Sequential, size=2)]
public class myClass {
public double height = 0.0;
public System.Text.StringBuilder name = new System.Text.StringBuilder(16);
}
IIRC C# marshals StringBuilder as char* by default when using PlatformInvoke features. This is handy for interfacing with C call convention DLLs. Some Ada compilers (especially the GCC one) might allow Ada libraries to be optimised for C call conventions. The "almost magical" nature of this may in fact be true. :)
My suggestion: surround your C# call to the Ada functions with an unsafe{} block and fixed() your char array, then pass it to your Ada function, then drop out of the fixed block again after it returns.
I don't have a working copy of Visual Studio so I can't verify this.
(Edited to fix incorrect terminology)
Something about this definitely feels off. If your Ada DLL knows how to handle a System.Text.StringBuilder then that seems almost magical. You generally need to pass primitive types around when dealing with native libraries. Also, this Ada DLL is possibly not a native dll, which is what Mono is expecting to work with. See Interop With Native Libraries for more info on marshaling data.
Now lets assume that your Ada DLL is actually a native DLL. And it somehow knows what to do with a StringBuilder. You can possibly pass a void pointer to the memory address via IntPtr. But I highly doubt this would work. Most likely what you want to do is pass the actual string through:
public class controller : MonoBehavior{
myClass student = new myClass();
[DllImport ("my_dll", EntryPoint="myFunction#12")]
public static extern void myFunction(myClass c);
void Start(){
student.name = "John Doe";
myFunction (student);
}
}
[StructLayoutAttribute(LayoutKind.Sequential, size=2)]
public class myClass {
//The following MarshalAs may be unnecessary.
[MarshalAs (UnmanagedType.LPStr)]
public string name;
public double height = 0.0;
}
LPStr is a 'long pointer to a string'. The assumption is your Ada DLL will know what to do with that. If that doesn't work, then you can remove the MarshalAs and see if it likes it better that way.
This C function will take an input str that must be less than ~250 characters and returns a modified string. You'll see that it mallocs a string the size of sprintf's modified string and then strcopy's it. There is no corresponding free() because it will be freed by mono as mentioned here.
char* myFunction(char* input, double height)
{
char buffer[256];
int n = sprintf(buffer, "%s -- %f", input, height);
char* ret = (char*)malloc(n+1); // +1 for the newline
strcopy(ret, buffer);
return ret;
}

AccessViolationException when calling C function from dll on C#

I'm trying to use PInvoke in order to call an unmanaged function from a C dll. Due to the fact that the source of the dll can't be released to developers, some of them have called the function in Delphi using the following declaration. We use SAT.dll with a CDECL calling convention.
function AssociarAssinatura( numeroSessao : Longint; codigoDeAtivacao: PChar;
CNPJvalue : PChar; assinaturaCNPJs : PChar ) : PChar ;
cdecl; External 'SAT.DLL';
Based on that structure, I made the following Console Application in C# in order to test the same function from the same DLL. I made some research and found out that the equivalent to Longint in delphi is int in C# and the equivalent of PChar is a pointer to a string (But I used C#'s string).
class Program
{
[DllImport("SAT.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern string AssociarAssinatura(int numeroSessao,
string codigoDeAtivacao, string CNPJvalue, string assinaturaCNPJs);
static void Main(string[] args)
{
Console.WriteLine("Comienza");
int numeroSessao = 111111;
string codigoDeAtivacao = "123123123";
string cnpJvalue = "2222222222222211111111111111";
string assinaturaCnpJs = "lrafwegmqcgvpzpbdmcmcgdvf";
string resposta = AssociarAssinatura(numeroSessao, codigoDeAtivacao,
cnpJvalue, assinaturaCnpJs);
Console.WriteLine(resposta);
}
}
When I call the function, an AccesViolationException is thrown. The code of AssociarAssinatura has some inner prints that show that the code from the function is indeed running well. Due to this I guess the problem is related when the function is returning it's value. My best guess is that somehow I'm having issues with the calling convention. Any thoughts?
Your problem here is most likely related to your PChar type in Delphi. In C#, strings are Unicode by default, and when calling your func, there will actually be a conversion from a PChar to PWideChar, which means a new block of memory will be allocated to hold this new PWideChar. This interop and difference between how strings are handled in .NET and in Delphi is more than likely causing your AccessViolationException.
You can use the MarshalAs attribute to explicitly tell .NET how to handle the specific type:
[DllImport("SAT.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern string AssociarAssinatura(int numeroSessao,
[MarshalAs(UnmanagedType.LPStr)] string codigoDeAtivacao, [MarshalAs(UnmanagedType.LPStr)] string CNPJvalue, [MarshalAs(UnmanagedType.LPStr)] string assinaturaCNPJs);
Which will explicitly specify how the string is handled. After that, your code should be fine.

Returning a string from PInvoke? [duplicate]

This question already has answers here:
Marshal "char *" in C#
(2 answers)
Closed 4 years ago.
I am using PInvoke for interoperability between Native Code (C++) and Managed Code (C#).
I just write a simple function which gets a string from C++ code. My code looks like
C# Code:
[DllImport("MyDll.dll")]
private static extern string GetSomeText();
public static string GetAllValidProjects() {
string s = GetSomeText();
return s;
}
C++ Code
char* GetSomeText() {
std::string stri= "Some Text Here";
char * pchr = (char *)stri.c_str();
return pchr;
}
All works fine at C++ end, i.e the variable pchr contains "Some Text Here" but at C# the string s contains noting in it. I don't know what I am doing wrong. Any help would be appreciated
First of all, as others have pointed out, your C++ is broken even before trying interop. You are returning a pointer to stri's buffer. But because stri is destroyed as soon as the function returns, the return value is not valid.
What's more, even if you fixed this, you need to do more. It won't work allocating memory in your C++ code which you would need the C# code to deallocate.
There are a few options to do it right.
Your C# code can ask the C++ code how long the string is. Then a C# StringBuilder is created and allocated to the appropriate size. Next the StringBuilder object is passed to the C++ code and its default marshalling is as a LPWSTR. In this approach the C# code allocates the string and your C++ code receives a C string to which it must copy the buffer.
Alternatively you can return a BSTR from the C++ which allows allocation in the native C++ code and deallocation in the C# code.
The BSTR approach is probably how I would do it. It looks like this:
C++
#include <comutil.h>
BSTR GetSomeText()
{
return ::SysAllocString(L"Greetings from the native world!");
}
C#
[DllImport(#"test.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string GetSomeText();
Update
Hans Passant added a couple of useful observations in the comments. First of all, most P/Invoke interop is done against an existing interface which cannot be changed and you do not have the luxury of picking your preferred interop interfacing approach. It would appear that is not the case here, so which approach should be chosen?
Option 1 is to allocate the buffer in the managed code, after having first asked the native code how much space is needed. Perhaps it is enough to use a fixed size buffer that both parties agree on.
Where option 1 falls down is when assembling the string is expensive and you don't want to do it twice (e.g. once to return its length, and once again for the contents). This is where option 2, the BSTR comes into play.
Hans pointed out one drawback of the BSTR, namely that it carries a UTF-16 payload but your source data may well char*, which is a "bit of a hassle".
To overcome the hassle you can wrap up the conversion from char* to BSTR like this:
BSTR ANSItoBSTR(char* input)
{
BSTR result = NULL;
int lenA = lstrlenA(input);
int lenW = ::MultiByteToWideChar(CP_ACP, 0, input, lenA, NULL, 0);
if (lenW > 0)
{
result = ::SysAllocStringLen(0, lenW);
::MultiByteToWideChar(CP_ACP, 0, input, lenA, result, lenW);
}
return result;
}
That's the hardest one out of the way, and now it's easy to add other wrappers to convert to BSTR from LPWSTR, std::string, std::wrstring etc.
Another way to get a string from C++. In case the you cannot modify your c++ dll. You can declare the DllImport with IntPtr instead of string. When the function is invoked, you can marshal the Ptr back to String.
[DllImport("MyDll.dll")]
private static extern IntPtr GetSomeText();
public static string GetAllValidProjects()
{
string s = Marshal.PtrToStringAnsi(GetSomeText());
return s;
}
Note : as mention in the previous post. "Some Text Here" is allocated on the stack so as soon as the function returns, the stack will unwire. Therefore the data is potential be overridden. Hence you shall use Marshal.PtrToStringAnsi right after the call. Don't hold to the IntPtr.
char* GetSomeText()
{
std::string stri= "Some Text Here";
char * pchr = (char *)stri.c_str();
return pchr;
}
It is because
std::string stri= "Some Text Here";
is a stack object and it is getting destroyed after GetSomeText() call. After the call pchr pointer which you are returning is invalid.
You may need to allocate space for the text dynamically to be able to access it latter.
Change your C++ function definition to:
char* GetSomeText()
{
std::string stri = "Some Text Here";
return strcpy(new char[stri.size()], stri.c_str());
}
Something like above. You got the idea...
GetSomeText() is returning pointer of char is wont work if u declare string variable to store it.
try
[DllImport("MyDll.dll")]
private static extern string GetSomeText();
public static string GetAllValidProjects()
{
string s = new string(GetSomeText());
return s;
}
there's a constructor that takes char*

Categories