Difficulty With C++ Dll in C# Unity 3D Uses

I am having trouble marshalling data between a C# Unity 2D application and an existing C++ DLL.

```
**// ============================================================================
// “General Output” Function (GPO)
// ============================================================================
bool _GetFPGAOCount(unsigned char ucCount);
bool _SetFPGAOIdx(unsigned long uIdx, bool bFlag);
bool _SetFPGAOAll(bool bFlag);
bool _SetFPGAOutput(unsigned long ulData1,unsigned long ulData2);
bool _GetFPGAOIdx(unsigned long uIdx, bool bFlag);
bool _GetFPGAOutput(unsigned long
ulData1,unsigned long
ulData2);

// ============================================================================
// “General Input” Function (GPI)
// ============================================================================
bool _GetFPGAICount(unsigned char ucCount);
bool _GetFPGAIIdx(unsigned long uIdx, bool bFlag);
bool _GetFPGAInput(unsigned long
ulData1,unsigned long
ulData2);
// Set debounce time
bool _SetFPGAIHWDebTime(unsigned long uIdxGroup, unsigned long time);**
** **My Unity C# Example** **csharp
**[DllImport(pluginName, EntryPoint = “_SetFPGAOAll”, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static extern bool SetFPGAOAll(bool bFlag);

public void OnBnClickedButtonSetAll()
{
SetFPGAOAll(true);
}**
```
Please need any help for converting should be welcome thanks

You either have to export the C++ methods as extern "C" or you have to look up how the C++ mangled name looks like with some dll inspection tools. The problem is most likely that, if you export those methods as normal C++ methods their names are mangled. Though as you can read further down that page there is not really a solid standard between C++ compilers. So if you want to keep the mangled names for some reason, you have to look up the exact names in your compiled DLL and specify the entrypoint manually (as you’re currently already doing, with extern “C” that’s usually not necessary given the method import in C# has the same name).

The name mangling usually encodes the signature of the method into the name itself. So all parameter types as well as the return type is usually encoded into the name. An extern “C” export will keep the name as it is.

2 Likes

Already Have a complete Dll Working in C++ GUI example But not Working when i call it in Unity

#ifndef _FPGAWINXPLIB_H
#define _FPGAWINXPLIB_H

#ifdef FPGAWINXPLIB_EXPORTS
#define FPGAWINXPLIB_API __declspec(dllexport)
#else
#define FPGAWINXPLIB_API __declspec(dllimport)
#endif

//FPGA Information Structure
typedef struct
{
    char szManufactureID[5+1];            // 00000~00004    Manufacture ID
    char szCustomerID[8+1];                // 00005~0000C    Customer ID
    char szModuleName[7+1];                // 0000D~00013    Module Name
    unsigned int szVersionNumber[3];    // 00014~00016    Version Number
    char szBuildtime[8+1];                // 00017~0001E    Buildtime
    int szGPIOdir[5];                    // 0001F~00023    GPIO output define
    unsigned int szAPIVersion[3];        // API Version
    char szFPGADriverVerion[12];        // FPGA Driver
}    FPGAInfo, *pFPGAInfo;

enum _MEMORY_TYPE
{
    RAM,
    FLASH,
    // 2011/01/13 Add "EEPROM" Function
    EEPROM,
    // 2011/09/29 Add "ExpandROM" Function
    EXPANDROM,
    TYPEMAX
};

// ============================================================================
// "FPGA Initial and Uninitial Function (These functions just for Window XP.)
// ============================================================================
extern "C" FPGAWINXPLIB_API BOOL _FPGAInit(DWORD * dwError, PVOID funcIntHandler);
extern "C" FPGAWINXPLIB_API BOOL _FPGAUnInit();
// ============================================================================
// "General Output" Function (GPO)
// ============================================================================
extern "C" FPGAWINXPLIB_API BOOL _GetFPGAOCount(unsigned char *ucCount);                        // Valid output port counts.
extern "C" FPGAWINXPLIB_API BOOL _SetFPGAOIdx(unsigned long uIdx, bool bFlag);                    // Set bFlag to uIdx position of output port.
extern "C" FPGAWINXPLIB_API BOOL _SetFPGAOAll(bool bFlag);                                        // Set bFlag to all position of output port.
extern "C" FPGAWINXPLIB_API BOOL _SetFPGAOutput(unsigned long ulData1,unsigned long ulData2);    // Set ulData1 to 0~31 position of output port and set ulData2 to 32~63 position.  
extern "C" FPGAWINXPLIB_API BOOL _GetFPGAOIdx(unsigned long uIdx, bool *bFlag);                    // Get bFlag from uIdx position of output port.
extern "C" FPGAWINXPLIB_API BOOL _GetFPGAOutput(unsigned long* ulData1,unsigned long* ulData2);    // Get ulData1 from 0~31 position of output port and get ulData2 from 32~63 position.

// ============================================================================
// "General Input" Function (GPI)
// ============================================================================
extern "C" FPGAWINXPLIB_API BOOL _GetFPGAICount(unsigned char *ucCount);                        // Valid input port counts.
extern "C" FPGAWINXPLIB_API BOOL _GetFPGAIIdx(unsigned long uIdx, bool *bFlag);                    // Get bFlag from uIdx position of input port.
extern "C" FPGAWINXPLIB_API BOOL _GetFPGAInput(unsigned long* ulData1,unsigned long* ulData2);    // Get ulData1 from 0~31 position of input port and get ulData2 from 32~63 position.
// Set debounce time
extern "C" FPGAWINXPLIB_API BOOL _SetFPGAIHWDebTime(unsigned long uIdxGroup, unsigned long time);

So always start with the docs:

https://docs.unity3d.com/Manual/NativePlugins.html

Note especially how they have extremely simple DllImport decorators, whereas yours above has a whole pile of extra C# chuff, none of which I have ever seen used before in Unity.

Find out why:

  • DLL didn’t make it into the build?

  • can’t mount the DLL?

  • can’t find that method?

  • can’t invoke the method that it found?

etc.

Every one of those is going to require completely different and completely google-able steps for you to solve it.

1 Like

Did you get any errors in Console window when you tried to call OnBnClickedButtonSetAll?
or any errors when you import native dll into Unity?

No Error at all everything seen to be correct. But no action in my platform were my sample should work…
I does not intend to build this Api when i asked for a c# version of the Plugin The contructor answer me EOL.
So Any Help for this plugin should be welcome thanks …

7599121–942943–Export functions for FPGA INPUT.pdf (159 KB)

Careful with bools. The default marshalling for booleans is unsigned 32 bit integer, while bool in C++ is one byte, so you are either saved by alignment or boom. Now where you use BOOL in the other sample, check what it’s size.

2 Likes

Can you precise Because this is an API that i’m trying only to use. I have attached a document with samples comands
but i dont understand why it is not working

Aurimas-Cernius

You haven’t precisely explained the problem either. Is dll found and loaded or not? Does it fail to find functions from dll, or does it call functions in a dll, but they don’t work in a correct way? Are parameters marshaled incorrectly?

The DLL is founded do functions and return true as answer But nothing Happen in my Platform. But the sample work correct

7600387--943177--Capture.png

Look, we don’t have your DLL or the potential FPGA hardware behind that library. So we can’t really help you much. The documentation you provided is very basic and doesn’t really go much into detail. However if you have trouble defining proper external definitions for that interface, here’s how I would interpret the document and header you shared:

    public static class FPGAInterface
    {
        const string dllName = "YourDllName.dll";
        [DllImport(dllName)]
        private static extern byte _GetFPGAOCount(ref byte ucCount);
        [DllImport(dllName)]
        private static extern byte _SetFPGAOIdx(uint uIdx, byte bFlag);
        [DllImport(dllName)]
        private static extern byte _SetFPGAOAll(byte bFlag);
        [DllImport(dllName)]
        private static extern byte _SetFPGAOutput(uint ulData1, uint ulData2);
        [DllImport(dllName)]
        private static extern byte _GetFPGAOIdx(uint uIdx, ref byte bFlag);
        [DllImport(dllName)]
        private static extern byte _GetFPGAOutput(ref uint ulData1, ref uint ulData2);

        [DllImport(dllName)]
        private static extern byte _GetFPGAICount(ref byte ucCount);
        [DllImport(dllName)]
        private static extern byte _GetFPGAIIdx(uint uIdx, ref byte bFlag);
        [DllImport(dllName)]
        private static extern byte _GetFPGAInput(ref uint ulData1, ref uint ulData2);
        [DllImport(dllName)]
        private static extern byte _SetFPGAIHWDebTime(uint uIdxGroup, uint time);

        public static bool GetFPGAOCount(ref byte ucCount) => _GetFPGAOCount(ref ucCount) != 0;
        public static bool SetFPGAOIdx(uint uIdx, bool bFlag) => _SetFPGAOIdx(uIdx, bFlag ? 1 : 0) != 0;
        public static bool SetFPGAOAll(bool bFlag) => _SetFPGAOAll(bFlag ? 1 : 0) != 0;
        public static bool SetFPGAOutput(ulong ulData) => _SetFPGAOutput((uint)(ulData & 0xFFFFFFFF), (uint)((ulData>>32) & 0xFFFFFFFF)) != 0;
        public static bool GetFPGAOIdx(uint uIdx, ref bool bFlag)
        {
            byte flag = 0;
            if (_GetFPGAOIdx(uIdx, ref flag) == 0)
                return false;
            bFlag = flag != 0;
            return true;
        }
        public static bool GetFPGAOutput(ref ulong ulData)
        {
            uint d1 = 0, d2 = 0;
            if (_GetFPGAOutput(ref d1, ref d2) == 0)
                return false;
            ulData = (ulong)d1 | (ulong)d2 << 32;
            return true;
        }

        public static bool GetFPGAICount(ref byte ucCount) => _GetFPGAICount(ref ucCount) != 0;
        public static bool GetFPGAIIdx(uint uIdx, ref bool bFlag)
        {
            byte flag = 0;
            if (_GetFPGAIIdx(uIdx, ref flag) == 0)
                return false;
            bFlag = flag != 0;
            return true;
        }
        public static bool GetFPGAInput(ref ulong ulData)
        {
            uint d1 = 0, d2 = 0;
            if (_GetFPGAInput(ref d1, ref d2) == 0)
                return false;
            ulData = (ulong)d1 | (ulong)d2 << 32;
            return true;
        }
        public static bool SetFPGAIHWDebTime(uint uIdxGroup, uint time) => _SetFPGAIHWDebTime(uIdxGroup, time) != 0;

    }

I seperated the actual external declaration from the interface in C#. So all the external methods are declared private and essentially wrapped by the public methods. At this point we can convert the byte return type into an actual bool in C#. Likewise I converted the other bool types and also the seperate 32 bit read and write methods now work with a 64 bit ulong in C#. Though if you need them as two seperate 32 bit uints, that’s an easy change.

Currently I kept the bool return type for all methods and the actual data is pass via parameters as we don’t know what it means for a method to “fail”. If that can happen often, it’s better this way. However if it should usually never fail, it might be better to throw an exception in case the method fails.

Interface with exceptions

    public static class FPGAInterfaceEx
    {

        public class EFPGAException : System.Exception
        {
            public EFPGAException(string aMessage) : base(aMessage) { }
        }
        const string dllName = "YourDllName.dll";
        [DllImport(dllName)]
        private static extern byte _GetFPGAOCount(ref byte ucCount);
        [DllImport(dllName)]
        private static extern byte _SetFPGAOIdx(uint uIdx, byte bFlag);
        [DllImport(dllName)]
        private static extern byte _SetFPGAOAll(byte bFlag);
        [DllImport(dllName)]
        private static extern byte _SetFPGAOutput(uint ulData1, uint ulData2);
        [DllImport(dllName)]
        private static extern byte _GetFPGAOIdx(uint uIdx, ref byte bFlag);
        [DllImport(dllName)]
        private static extern byte _GetFPGAOutput(ref uint ulData1, ref uint ulData2);

        [DllImport(dllName)]
        private static extern byte _GetFPGAICount(ref byte ucCount);
        [DllImport(dllName)]
        private static extern byte _GetFPGAIIdx(uint uIdx, ref byte bFlag);
        [DllImport(dllName)]
        private static extern byte _GetFPGAInput(ref uint ulData1, ref uint ulData2);
        [DllImport(dllName)]
        private static extern byte _SetFPGAIHWDebTime(uint uIdxGroup, uint time);

        public static byte GetFPGAOCount()
        {
            byte count = 0;
            if (_GetFPGAOCount(ref count) == 0)
                throw new EFPGAException($"{nameof(GetFPGAICount)} failed");
            return count;
        }
        public static void SetFPGAOIdx(uint uIdx, bool bFlag)
        {
            if (_SetFPGAOIdx(uIdx, bFlag ? 1 : 0) == 0)
                throw new EFPGAException($"{nameof(SetFPGAOIdx)} failed");
        }
        public static void SetFPGAOAll(bool bFlag)
        {
            if (_SetFPGAOAll(bFlag ? 1 : 0) == 0)
                throw new EFPGAException($"{nameof(SetFPGAOAll)} failed");
        }
        public static void SetFPGAOutput(ulong ulData)
        {
            if (_SetFPGAOutput((uint)(ulData & 0xFFFFFFFF), (uint)((ulData >> 32) & 0xFFFFFFFF)) == 0)
                throw new EFPGAException($"{nameof(SetFPGAOutput)} failed");
        }
        public static bool GetFPGAOIdx(uint uIdx)
        {
            byte flag = 0;
            if (_GetFPGAOIdx(uIdx, ref flag) == 0)
                throw new EFPGAException($"{nameof(GetFPGAOIdx)} failed");
            return flag != 0;
        }
        public static ulong GetFPGAOutput()
        {
            uint d1 = 0, d2 = 0;
            if (_GetFPGAOutput(ref d1, ref d2) == 0)
                throw new EFPGAException($"{nameof(GetFPGAOutput)} failed");
            return (ulong)d1 | (ulong)d2 << 32;
        }

        public static byte GetFPGAICount()
        {
            byte count = 0;
            if (_GetFPGAICount(ref count) == 0)
                throw new EFPGAException($"{nameof(GetFPGAICount)} failed");
            return count;
        }
        public static bool GetFPGAIIdx(uint uIdx)
        {
            byte flag = 0;
            if (_GetFPGAIIdx(uIdx, ref flag) == 0)
                throw new EFPGAException($"{nameof(GetFPGAIIdx)} failed");
            return flag != 0;
        }
        public static ulong GetFPGAInput()
        {
            uint d1 = 0, d2 = 0;
            if (_GetFPGAInput(ref d1, ref d2) == 0)
                throw new EFPGAException($"{nameof(GetFPGAInput)} failed");
            return (ulong)d1 | (ulong)d2 << 32;
        }
        public static void SetFPGAIHWDebTime(uint uIdxGroup, uint time)
        {
            if (_SetFPGAIHWDebTime(uIdxGroup, time) == 0)
                throw new EFPGAException($"{nameof(SetFPGAIHWDebTime)} failed");
        }

Of course I can not test any of this. Also your post number #4 contained those Init and Uninit methods which seems to be relevant for WinXP only. Though if this is a really old library, it’s possible that it’s required from WinXP onwards since win XP had stricter direct IO. Though those are all just assumptions and it’s pointless to speculate.

1 Like

What just came into my mind is that you have to make sure you’re building for the correct architecture. If the DLL is a 32 bit dll and you’re building for 64 bit it won’t work since pointers are twice as big in 64 bit. You can’t mix 32 and 64 bit code. Both ways. If you build for 32 bit but the dll is 64 bit it would not work either. So make sure you’re using the right architecture.

Mahy thanks for your answer and sorry i forget maybe i have to intilise the FPGA before

BOOL _FPGAInit(DWORD * dwError, PVOID funcIntHandler)