Jump to content
  • Advertisement
Sign in to follow this  
L. Spiro

C# Returning Buffer from C to C#

Recommended Posts

I don’t C# very often.

I have a C function that takes a byte array as input and should return a string it generates internally.

Just for starters I generate the string and discard it since I don’t know how to return it to C#.  In C I have this:

int __stdcall ConvertHodgmanKylotanjbadamsfrobRavyneApochPiQSeanMiddleditchrip_offspaceratLightness1024_to_String(const char * thoseGuys);

In C# I have this:
 

[DllImport("Important.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
private static extern unsafe Int32 ConvertHodgmanKylotanjbadamsfrobRavyneApochPiQSeanMiddleditchrip_offspaceratLightness1024_to_String(byte [] thoseGuys);

I step into ConvertHodgmanKylotanjbadamsfrobRavyneApochPiQSeanMiddleditchrip_offspaceratLightness1024_to_String() and on the C side it works fine.  It generates the output I want as a local so I can verify it and then discards it to avoid leaking.

I need it to return the generated string to C#.  Let’s say I want this to be the final C function:
 

int __stdcall ConvertHodgmanKylotanjbadamsfrobRavyneApochPiQSeanMiddleditchrip_offspaceratLightness1024_to_String(const char * thoseGuys, char * retVal, int &retLen);

How should I allocate it in C and how do I tell C# how to treat it as a parameter and how do I deallocate it after?


L. Spiro

Edited by L. Spiro

Share this post


Link to post
Share on other sites
Advertisement

If you are actually working with strings in particular, the C# side should use string for the C#->C param, then probably a StringBuilder with a predefined size for the C->C# "return value". You should probably make sure to select reasonable MarshalAs options anyway though.

Another way, that is possibly easier (but uglier) is to just use unsafe and IntPtr. Depending on your target that may cause problem due to the unsafe requirement. In this case, make sure to look up the fixed and unsafe keywords. If I understand things correctly, this will bypass most if not all marshalling and copying, but is less clean and "safe".

If you are working with arrays of primitives, you may need to tag the parameters with [In] and/or [Out] depending on the direction of the data. That also applies to ref parameters.

If you need to pass structs, make sure you use struct in C# and make sure to tag any non-primitive types (including arrays of primitive types) with reasonable MarshalAs values and make sure to tag the entire struct with [StructLayout(LayoutKind.Sequential)] (or whatever the explicit version is in which case you should tag each field with their absolute offset as well). Let it be known that I have seen unexpected or problematic behaviour when trying to pass structs with non-primitive members though. In one of those cases I had much better luck serializing the data to a byte array and pass it as an IntPtr instead of trying to get the marshalling to work correctly, but that particular case was a rather complicated struct in an API beyond my control with nested structs, strings and other fixed size arrays of nested structs and stuff, so YMMW.

[Edit]

I just realized I missed a part of your post where you wanted to allocate the return value in C. I'm not sure that's what you actually want given your C signature which looks more like you want to pass in a preallocated buffer (in which case you want to use StringBuilder) but if you actually want to allocate it in your C function, I would return it as a pointer and use it with IntPtr in C#, then define some form of FreeString() function in your C dll and pass that IntPtr back once you've manually copied the data into a C# array/struct/string/whatever. Which is how I would do it in C if I was designing an API, even if it was not strictly meant to be used from C#.

Share this post


Link to post
Share on other sites

I was idiot and made error in my C prototype.  The C function would have needed “char * &retVal” so it could return its own allocation to C#.

As you can guess, converting all those people to strings can give wildly different return sizes.  I expect ranges from a few bytes to a few megabytes.  ApochPiQ alone is probably 3 megabytes of a string.  So it is not something I can allocate ahead of time.

But I think the use of BSTR mentioned in the link ApochPiQ posted should work.  I am fine with returning a buffer as long as it can be cleaned up by C# properly.  So they call it “PInvoke” eh?  That should be a handy term to use in my future searches.


L. Spiro

Share this post


Link to post
Share on other sites

I've never used BSTR myself, but personally I think it looks rather scary. It will probably not work (or work strange) with other data types and I'd also be very wary of cross platform compatibility with this. On the other hand, if it's not for a long-term cross platform project then the easiest and simplest way is to be preferred I suppose. 

Something like this is how I would do it:

    static class PInvokeThing
    {
        [DllImport("Important.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
        private static unsafe extern int ConvertHodgmanKylotanjbadamsfrobRavyneApochPiQSeanMiddleditchrip_offspaceratLightness1024_to_String(string stuff, out byte* outPtr, out int outLen);

        [DllImport("Important.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
        private static unsafe extern int FreeBuffer(byte* ptr);


        public static string ConvertTheString(string input)
        {
            unsafe
            {
                byte* ptr;
                int len;
                var result = ConvertHodgmanKylotanjbadamsfrobRavyneApochPiQSeanMiddleditchrip_offspaceratLightness1024_to_String(input, out ptr, out len);

                // check result

                var barr = new byte[len];

                for (int i = 0; i < len; i++)
                    barr[i] = ptr[i];

                FreeBuffer(ptr); // probably check this too

                return Encoding.Default.GetString(barr);
            }
        }
    }

This was written mostly from my head and may contain minor mistakes, but the idea should be usable for most types of data etc.

Share this post


Link to post
Share on other sites

I did some test interaction functions from C# DLL into my C++ engine structure that lets the natie side create and manage those data while the managed side has read/write access to it so it is possible to pass for example an arrays native pointer from managed side to the native side for operating.

My Array.cs class looks like this

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace Bridge
{
    public interface IArrayInternal
    {
        IntPtr Handle { get; set; }
        Allocator.IAllocator GetAllocator();

        int GetLength();

        object GetValue(int index);
        void SetValue(int index, object value);

        /// <summary>
        /// Resizes this array. Any data inside might get lost in order
        /// ti achieve the targeted size
        /// </summary>
        void Resize(int size);
        void Dispose();
    }
    public class ByteArrayInternal : Interopt.NativeMngClass, IArrayInternal
    {
        public new IntPtr Handle
        {
            get { return ptr; }
            set { ptr = value; }
        }
        public virtual int GetLength()
        {
            if (ptr == IntPtr.Zero) return -1;
            else return (int)csGetArraySizeByte(ptr);
        }

        public ByteArrayInternal(IntPtr ptr, Allocator.IAllocator allocator = null)
            : base(ptr, allocator, true)
        { }
        public ByteArrayInternal(UInt32 size, Allocator.IAllocator allocator = null)
            : base(IntPtr.Zero, allocator, true)
        {
            if((ptr = csCreateArrayByte(GetAllocator().Handle)) != IntPtr.Zero)
                csArrayResizeByte(ptr, size);
        }

        public object GetValue(int index)
        {
            if (ptr == IntPtr.Zero) return 0;
            else return csGetArrayValueByte(ptr, (UInt32)index);
        }
        public void SetValue(int index, object value)
        {
            if (ptr != IntPtr.Zero)
                csSetArrayValueByte(ptr, (UInt32)index, (byte)value);
        }

        public virtual void Resize(int size)
        {
            if (ptr == IntPtr.Zero) return;
            else csArrayResizeByte(ptr, (UInt32)size);
        }

        public override void Dispose()
        {
            if (ptr != IntPtr.Zero) csDeleteArrayByte(ptr);
            ptr = IntPtr.Zero;

            Unregister();
        }

        [DllImport("CSharp.dll", EntryPoint = "csCreateArrayByte", CallingConvention = CallingConvention.Cdecl)]
        private static extern IntPtr csCreateArrayByte(IntPtr allocator);

        [DllImport("CSharp.dll", EntryPoint = "csGetArraySizeByte", CallingConvention = CallingConvention.Cdecl)]
        private static extern UInt32 csGetArraySizeByte(IntPtr instance);

        [DllImport("CSharp.dll", EntryPoint = "csGetArrayValueByte", CallingConvention = CallingConvention.Cdecl)]
        private static extern byte csGetArrayValueByte(IntPtr instance, UInt32 index);

        [DllImport("CSharp.dll", EntryPoint = "csSetArrayValueByte", CallingConvention = CallingConvention.Cdecl)]
        private static extern void csSetArrayValueByte(IntPtr instance, UInt32 index, byte value);

        [DllImport("CSharp.dll", EntryPoint = "csArrayResizeByte", CallingConvention = CallingConvention.Cdecl)]
        private static extern void csArrayResizeByte(IntPtr instance, UInt32 size);

        [DllImport("CSharp.dll", EntryPoint = "csDeleteArrayByte", CallingConvention = CallingConvention.Cdecl)]
        private static extern void csDeleteArrayByte(IntPtr instance);
    }

    public class Array<T> : Interopt.INativeClass, Interopt.INativeMngClass, IDisposable
    {
        protected IArrayInternal handler;

        public IntPtr Handle 
        {
            get 
            {
                if (handler == null) return IntPtr.Zero;
                else return handler.Handle; 
            }
            protected set
            {
                if (handler == null) return;
                else handler.Handle = value;
            }
        }
        public Allocator.IAllocator GetAllocator()
        {
            if (handler == null) return null;
            else return handler.GetAllocator();
        }

        public virtual int Length
        {
            get 
            {
                if (handler == null) return 0;
                else return handler.GetLength(); 
            }
        }

        public Array()
        { }
        public Array(IntPtr ptr, Allocator.IAllocator allocator = null)
        {
            switch (Type.GetTypeCode(typeof(T)))
            {
                case TypeCode.Byte: handler = new ByteArrayInternal(ptr, allocator); break;
                default: throw new ArgumentOutOfRangeException();
            }
        }
        public Array(UInt32 size, Allocator.IAllocator allocator = null)
        {
            switch (Type.GetTypeCode(typeof(T)))
            {
                case TypeCode.Byte: handler = new ByteArrayInternal(size, allocator); break;
                default: throw new ArgumentOutOfRangeException();
            }
        }
        ~Array()
        {
            Dispose();
        }

        public T this[int index]
        {
            get 
            {
                return (T)Convert.ChangeType(handler.GetValue(index), typeof(T)); 
            }
            set 
            {
                if (handler != null)
                    handler.SetValue(index, value); 
            }
        }

        public virtual void Resize(int size)
        {
            if(handler != null)
                handler.Resize(size);
        }
        public virtual void Dispose()
        {
            if (handler != null)
                handler.Dispose();
        }

        public override string ToString()
        {
            return string.Format("{0}[{1}]", typeof(T).Name, Length);
        }
    }
}

Used this schema for having some of my C# tools to access engine native functions

Share this post


Link to post
Share on other sites

PInvoke is one term, data marshalling is another. 

PInvoke.net is a good resource, but even so, the effort involved is still an annoyance. In working with C you still need to operate in terms of sources and sinks, and when the C side function is allocating, you'll need a corresponding C side function to release it.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

  • Advertisement
  • Advertisement
  • Popular Tags

  • Similar Content

    • By JoshuaFraser
      Hi and thanks for reading, I have an issue with this reactive crosshair script, everything works fine until I start changing the offset. Give the script a go and you will see what I mean, when I do SetOffset(0f); it doesnt always set back to the origional state, if anyone can spot a fix I'd be super appreciative!
      using System.Collections; using System.Collections.Generic; using UnityEngine; public class ReactiveCrosshair : MonoBehaviour { [SerializeField] GameObject c_limb_prefab; private float center_offset = 0f; private float current_offset = 0f; private float max_offset = .5f; private int number_of_limbs = 4; private float limb_length = .05f; private float limb_width = .005f; private List<GameObject> c_limbs = new List<GameObject>(); public void SetupCrosshair(){ for (int i = 0; i < number_of_limbs; i++) { GameObject line_go = (GameObject)Instantiate (c_limb_prefab); line_go.transform.SetParent (this.transform); Vector3 limb_pos = new Vector3 (0f,0f,0f); //line_go.transform.position = limb_pos; line_go.transform.localPosition = limb_pos; LineRenderer line = line_go.GetComponent<LineRenderer>(); line.startWidth = limb_width; line.positionCount = 2; line.SetPosition (0, line_go.transform.localPosition + new Vector3(center_offset, 0f, 0f)); line.SetPosition (1, line_go.transform.localPosition + new Vector3(center_offset + limb_length, 0f, 0f)); line.useWorldSpace = false; c_limbs.Add(line_go.gameObject); } if (c_limbs != null) { OrientLimbs (); SetOffset (0f); } } public void OrientLimbs(){ for (int i = 0; i < c_limbs.Count; i++) { float rotation_step = 360f / (float)c_limbs.Count; c_limbs [i].transform.RotateAround (c_limbs[i].transform.position, c_limbs[i].transform.forward, 90f + (rotation_step * (float)i)); } } public void SetOffset(float _current_spread){ float offset = Mathf.Lerp (0f, max_offset, _current_spread); for (int i = 0; i < number_of_limbs; i++) { if (offset > current_offset) { Vector3 pos = c_limbs [i].transform.position + (c_limbs [i].transform.TransformDirection (Vector3.right) * offset); c_limbs [i].transform.position = pos; } if (offset < current_offset) { Vector3 pos = c_limbs [i].transform.position - (c_limbs [i].transform.TransformDirection (Vector3.right) * offset); c_limbs [i].transform.position = pos; } } Debug.Log ("SetOffset() offset: " + offset.ToString () + " _current_spread: " + _current_spread.ToString() + " localPos: " + c_limbs[1].transform.localPosition); current_offset = offset; } }  
    • By Woody Stevens
      Hi,
       
      I am looking for a TCP or HTTP networking library similar to Lidgren (UDP).
       
      This is primarily for sending game map data and potentially other large messages from Server to Client.
       
      I do want to keep Lidgren for my chat messages, player position, small fast updates etc. I especially love the flow of data and the library usage in general, so any libraries of a similar style would be excellent. Preferably something open source, free and reliable.
      I also must be able to swap between localhost and an ip address with ease, like Lidgren, as I run a server for singleplayer/mp/lan.
       
      My game maps are similar to minecraft, but it is 2d and only one Z-level, so i'm sending a jagged array of Tile object data (currently only enum TileID.Grass) down the pipe to the Client. Problem is if i'm sending a large map 1024 x 1024 tiles down the to client that's quite a lot of data, and Lidgren is relatively slow to build the writes (before the message is even sent!). It is fine when i'm using smaller maps < 512 x 512 ( xTiles * yTiles ).

      I know about chunking and will look into implementing this later, whilst taking into account the user's position in the world to only send nearby chunks.
       
      An example of my code that can be slow:
      private void WriteWorld(NetOutgoingMessage outgoing) { try { var world = WorldManager.Instance.CurrentWorld; outgoing.Write(world.XTiles); outgoing.Write(world.YTiles); for (int x = 0; x < world.XTiles; x++) { for (int y = 0; y < world.YTiles; y++) { // Write Tile obj data outgoing.Write((int)world.Tiles[x][y]); // <-------- Slow here when xTiles and yTiles are each > 512 ! } } } catch (Exception ex) { // log send error } }  
      I'd love to hear from you guys, especially if any of you have come across a similar challenge.
    • By ethancodes
      I'm working on a system for my game that will allow the player to stack pick ups in a queue. As one pick up expires, the next automatically activates. I'm having an issue though where if I pick up the first one, it activates fine, but if i pick up a second directly after it, it overrides the first one, activates the second one, and then once it has run it's course, everything goes back to normal gameplay, no first pick up. I'm not sure why this is happening. Hopefully someone can spot what I'm doing wrong in my code.
      Here is the code for the pick up manager:
      // Update is called once per frame void Update () { if (pickUpQueue.Count != 0 && !pickUpActive) { pickUpActive = true; pickUpQueue[0].ActivatePickUp(); } DeactivatePickUp(); } void DeactivatePickUp () { if (pickUpQueue.Count != 0 && pickUpActive) { Destroy (pickUpQueue [0]); pickUpQueue.RemoveAt (0); pickUpActive = false; } } And here is the PickUp:
      public override void ActivatePickUp () { ball.GetComponent<Ball>().Speed = 2.0f; //increase ball speed... ball.GetComponent<Ball>().StartCoroutine(timer); //...set time that power up is active }  
      There is also a Base Pick Up:
      public void OnCollisionEnter2D (Collision2D collision) { Vector2 tweak = new Vector2 (Random.Range(0f, 0.2f),Random.Range(0f, 0.2f)); this.gameObject.GetComponent<Rigidbody2D>().velocity += tweak; //if the pickup makes contact with the paddle or ball.... if (collision.gameObject.tag == "Paddle" || collision.gameObject.tag == "Ball") { GameObject.FindObjectOfType<GameManager>().GetComponent<PickUpManager>().pickUpQueue.Add(this); Destroy(gameObject); //...and finally destroy power up object } } As a side note, I am trying to find a solution to this that will work for all of my pickups. Some pickups are ammo based, some are timed. 
    • By Hellados
      Hello guys, my name is Giorgi and i'm newbie game developer i'm learning Pixel art and after pixel art  i want learn C# and don't know how and where start i'm bad with programming language and know only HTML/CSS
    • By D34DPOOL
      Edit Your Profile D34DPOOL 0 Threads 0 Updates 0 Messages Network Mod DB GameFront Sign Out Add jobEdit jobDeleteC# Programmer for a Unity FPS at Anywhere   Programmers located Anywhere.
      Posted by D34DPOOL on May 20th, 2018
      Hello, my name is Mason, and I've been working on a Quake style arena shooter about destroying boxes on and off for about a year now. I have a proof of concept with all of the basic features, but as an artist with little programming skill I've reached the end of my abilities as a programmer haha. I need someone to help fix bugs, optomize code, and to implent new features into the game. As a programmer you will have creative freedom to suggest new features and modes to add into the game if you choose to, I'm usually very open to suggestions :).
      What is required:
      Skill using C#
      Experience with Unity
      Experience using UNET (since it is a multiplayer game), or the effort and ability to learn it
      Compensation:
      Since the game currently has no funding, we can split whatever revenue the game makes in the future. However if you would perfer I can create 2D and/or 3D assets for whatever you need in return for your time and work.
      It's a very open and chill enviornment, where you'll have relative creative freedom. I hope you are interested in joining the team, and have a good day!
       
      To apply email me at mangemason@yahoo.com
    • By Andrew Parkes
      I am a talented 2D/3D artist with 3 years animation working experience and a Degree in Illustration and Animation. I have won a world-wide art competition hosted by SFX magazine and am looking to develop a survival game. I have some knowledge of C sharp and have notes for a survival based game with flexible storyline and PVP. Looking for developers to team up with. I can create models, animations and artwork and I have beginner knowledge of C sharp with Unity. The idea is Inventory menu based gameplay and is inspired by games like DAYZ.
      Here is some early sci-fi concept art to give you an idea of the work level. Hope to work with like minded people and create something special. email me andrewparkesanim@gmail.com.
      Developers who share the same passion please contact me, or if you have a similar project and want me to join your team email me. 
      Many thanks, Andrew.

    • By mike44
      The reference assemblies for framework ".NETFramework,Version=v3.5" were not found. To resolve this, install the SDK or Targeting Pack for this framework version or retarget your application to a version of the framework for which you have the SDK or Targeting Pack installed. Note that assemblies will be resolved from the Global Assembly Cache (GAC) and will be used in place of reference assemblies. Therefore your assembly may not be correctly targeted for the framework you intend.

      Hi
      what to do with the above error in ms code/Unity3d Project on Ubuntu 18.04? I've installed it like:
      https://www.microsoft.com/net/learn/...ux/ubuntu18-04
      Many thanks
    • By Dave Haylett
      Hi.
      I have pulled in five NuGet packages for my Visual Studio 2017 project, however when I build the project, VS spits out 10 .DLL and .XML files in the root of the binary folder, to do with the packages. Can't I shove them into a \packages folder so the user doesn't see these ugly resources next to the .exe file?
      I've Googled moving the packages but the only responses seem to be around moving the installation folder of the NuGet packages on the local machine, as opposed to where VS builds them to.
    • By MoreLion
      hey all! We are looking for members for our Unity horror game! 
      Here’s the story:
      After a deadly virus plunges the world into chaos killing 85% of the human population there are now what they call “zones” these zones are watched very closely by the surviving government, people are checked every day for the virus, even if you touch the spit or any human waste or fluids of the victim who is infected, you will die. But one day, people in the west zone start to go missing, 1 woman goes outside the walls to uncover the mystery, is there more to the virus than meets the eye?, That is where your story starts.
      This game is not a long development game, I have loads other game ideas,
      I will also allow you to have a bit of creative freedom if you wish to add or share a idea!
      And no, it’s not a zombie game lol I feel like zombie games are too generic, in this game you will encounter terrifying beasts!
      There is some concept art one of our concept artists have made
      If interested email liondude12@gmail.com
    • By Victor Rodriguez
      Hi there! Is the first time that I'm posting here so I'm sorry if I'm doing it wrong ha. 
      So here it comes, my doubt is, I'm doing a game with different levels, each of these levels in one different scene. Each scene contains to cameras that you can change pressing a button. Everything works fine. 
      The only problem is that I would like it to look a bit more professional, and I would like that if you finish the level with camera2, the next level start the same way. I've been thinking about using dontdestroyonloadon both cameras, but obviously this cameras need to be attached to the player to make the movement work, what do you recommend? Sorry If I've explained it in a messy way, and feel free to dm me for anything. Thanks in advance! 
  • Advertisement
  • Popular Now

  • Forum Statistics

    • Total Topics
      631399
    • Total Posts
      2999854
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!