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..
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With