Search Unity

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

Discussion in 'Scripting' started by kamercode, Oct 24, 2021.

  1. kamercode

    kamercode

    Joined:
    Jul 30, 2020
    Posts:
    8
    I am having trouble marshalling data between a C# Unity 2D application and an existing C++ DLL.

    Code (c++):
    1. // ============================================================================
    2. // "General Output" Function (GPO)
    3. // ============================================================================
    4. bool _GetFPGAOCount(unsigned char *ucCount);
    5. bool _SetFPGAOIdx(unsigned long uIdx, bool bFlag);
    6. bool _SetFPGAOAll(bool bFlag);
    7. bool _SetFPGAOutput(unsigned long ulData1,unsigned long ulData2);
    8. bool _GetFPGAOIdx(unsigned long uIdx, bool *bFlag);
    9. bool _GetFPGAOutput(unsigned long* ulData1,unsigned long* ulData2);
    10.  
    11. // ============================================================================
    12. // "General Input" Function (GPI)
    13. // ============================================================================
    14. bool _GetFPGAICount(unsigned char *ucCount);
    15. bool _GetFPGAIIdx(unsigned long uIdx, bool *bFlag);
    16. bool _GetFPGAInput(unsigned long* ulData1,unsigned long* ulData2);
    17. // Set debounce time
    18. bool _SetFPGAIHWDebTime(unsigned long uIdxGroup, unsigned long time);
    My Unity C# Example

    Code (CSharp):
    1. [DllImport(pluginName, EntryPoint = "_SetFPGAOAll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    2. private static extern bool SetFPGAOAll(bool bFlag);
    3.  
    4. public void OnBnClickedButtonSetAll()
    5. {
    6.     SetFPGAOAll(true);
    7. }
    Please need any help for converting should be welcome thanks
     
  2. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,999
    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.
     
    kamercode and VolodymyrBS like this.
  3. kamercode

    kamercode

    Joined:
    Jul 30, 2020
    Posts:
    8
    Already Have a complete Dll Working in C++ GUI example But not Working when i call it in Unity
     
  4. kamercode

    kamercode

    Joined:
    Jul 30, 2020
    Posts:
    8
    Code (C++):
    1. #ifndef _FPGAWINXPLIB_H
    2. #define _FPGAWINXPLIB_H
    3.  
    4. #ifdef FPGAWINXPLIB_EXPORTS
    5. #define FPGAWINXPLIB_API __declspec(dllexport)
    6. #else
    7. #define FPGAWINXPLIB_API __declspec(dllimport)
    8. #endif
    9.  
    10. //FPGA Information Structure
    11. typedef struct
    12. {
    13.     char szManufactureID[5+1];            // 00000~00004    Manufacture ID
    14.     char szCustomerID[8+1];                // 00005~0000C    Customer ID
    15.     char szModuleName[7+1];                // 0000D~00013    Module Name
    16.     unsigned int szVersionNumber[3];    // 00014~00016    Version Number
    17.     char szBuildtime[8+1];                // 00017~0001E    Buildtime
    18.     int szGPIOdir[5];                    // 0001F~00023    GPIO output define
    19.     unsigned int szAPIVersion[3];        // API Version
    20.     char szFPGADriverVerion[12];        // FPGA Driver
    21. }    FPGAInfo, *pFPGAInfo;
    22.  
    23. enum _MEMORY_TYPE
    24. {
    25.     RAM,
    26.     FLASH,
    27.     // 2011/01/13 Add "EEPROM" Function
    28.     EEPROM,
    29.     // 2011/09/29 Add "ExpandROM" Function
    30.     EXPANDROM,
    31.     TYPEMAX
    32. };
    33.  
    34. // ============================================================================
    35. // "FPGA Initial and Uninitial Function (These functions just for Window XP.)
    36. // ============================================================================
    37. extern "C" FPGAWINXPLIB_API BOOL _FPGAInit(DWORD * dwError, PVOID funcIntHandler);
    38. extern "C" FPGAWINXPLIB_API BOOL _FPGAUnInit();
    39. // ============================================================================
    40. // "General Output" Function (GPO)
    41. // ============================================================================
    42. extern "C" FPGAWINXPLIB_API BOOL _GetFPGAOCount(unsigned char *ucCount);                        // Valid output port counts.
    43. extern "C" FPGAWINXPLIB_API BOOL _SetFPGAOIdx(unsigned long uIdx, bool bFlag);                    // Set bFlag to uIdx position of output port.
    44. extern "C" FPGAWINXPLIB_API BOOL _SetFPGAOAll(bool bFlag);                                        // Set bFlag to all position of output port.
    45. 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.  
    46. extern "C" FPGAWINXPLIB_API BOOL _GetFPGAOIdx(unsigned long uIdx, bool *bFlag);                    // Get bFlag from uIdx position of output port.
    47. 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.
    48.  
    49. // ============================================================================
    50. // "General Input" Function (GPI)
    51. // ============================================================================
    52. extern "C" FPGAWINXPLIB_API BOOL _GetFPGAICount(unsigned char *ucCount);                        // Valid input port counts.
    53. extern "C" FPGAWINXPLIB_API BOOL _GetFPGAIIdx(unsigned long uIdx, bool *bFlag);                    // Get bFlag from uIdx position of input port.
    54. 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.
    55. // Set debounce time
    56. extern "C" FPGAWINXPLIB_API BOOL _SetFPGAIHWDebTime(unsigned long uIdxGroup, unsigned long time);
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,732
    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.
     
    kamercode likes this.
  6. VolodymyrBS

    VolodymyrBS

    Joined:
    May 15, 2019
    Posts:
    150
    Did you get any errors in Console window when you tried to call OnBnClickedButtonSetAll?
    or any errors when you import native dll into Unity?
     
    Last edited: Oct 24, 2021
  7. kamercode

    kamercode

    Joined:
    Jul 30, 2020
    Posts:
    8
    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 ..
     
    Last edited: Oct 24, 2021
  8. kamercode

    kamercode

    Joined:
    Jul 30, 2020
    Posts:
    8
     

    Attached Files:

  9. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,735
    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.
     
    kamercode and Bunny83 like this.
  10. kamercode

    kamercode

    Joined:
    Jul 30, 2020
    Posts:
    8
    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
     
  11. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    3,735
    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?
     
  12. kamercode

    kamercode

    Joined:
    Jul 30, 2020
    Posts:
    8
    The DLL is founded do functions and return true as answer But nothing Happen in my Platform. But the sample work correct
     

    Attached Files:

  13. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,999
    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:

    Code (CSharp):
    1.  
    2.     public static class FPGAInterface
    3.     {
    4.         const string dllName = "YourDllName.dll";
    5.         [DllImport(dllName)]
    6.         private static extern byte _GetFPGAOCount(ref byte ucCount);
    7.         [DllImport(dllName)]
    8.         private static extern byte _SetFPGAOIdx(uint uIdx, byte bFlag);
    9.         [DllImport(dllName)]
    10.         private static extern byte _SetFPGAOAll(byte bFlag);
    11.         [DllImport(dllName)]
    12.         private static extern byte _SetFPGAOutput(uint ulData1, uint ulData2);
    13.         [DllImport(dllName)]
    14.         private static extern byte _GetFPGAOIdx(uint uIdx, ref byte bFlag);
    15.         [DllImport(dllName)]
    16.         private static extern byte _GetFPGAOutput(ref uint ulData1, ref uint ulData2);
    17.  
    18.         [DllImport(dllName)]
    19.         private static extern byte _GetFPGAICount(ref byte ucCount);
    20.         [DllImport(dllName)]
    21.         private static extern byte _GetFPGAIIdx(uint uIdx, ref byte bFlag);
    22.         [DllImport(dllName)]
    23.         private static extern byte _GetFPGAInput(ref uint ulData1, ref uint ulData2);
    24.         [DllImport(dllName)]
    25.         private static extern byte _SetFPGAIHWDebTime(uint uIdxGroup, uint time);
    26.  
    27.         public static bool GetFPGAOCount(ref byte ucCount) => _GetFPGAOCount(ref ucCount) != 0;
    28.         public static bool SetFPGAOIdx(uint uIdx, bool bFlag) => _SetFPGAOIdx(uIdx, bFlag ? 1 : 0) != 0;
    29.         public static bool SetFPGAOAll(bool bFlag) => _SetFPGAOAll(bFlag ? 1 : 0) != 0;
    30.         public static bool SetFPGAOutput(ulong ulData) => _SetFPGAOutput((uint)(ulData & 0xFFFFFFFF), (uint)((ulData>>32) & 0xFFFFFFFF)) != 0;
    31.         public static bool GetFPGAOIdx(uint uIdx, ref bool bFlag)
    32.         {
    33.             byte flag = 0;
    34.             if (_GetFPGAOIdx(uIdx, ref flag) == 0)
    35.                 return false;
    36.             bFlag = flag != 0;
    37.             return true;
    38.         }
    39.         public static bool GetFPGAOutput(ref ulong ulData)
    40.         {
    41.             uint d1 = 0, d2 = 0;
    42.             if (_GetFPGAOutput(ref d1, ref d2) == 0)
    43.                 return false;
    44.             ulData = (ulong)d1 | (ulong)d2 << 32;
    45.             return true;
    46.         }
    47.  
    48.         public static bool GetFPGAICount(ref byte ucCount) => _GetFPGAICount(ref ucCount) != 0;
    49.         public static bool GetFPGAIIdx(uint uIdx, ref bool bFlag)
    50.         {
    51.             byte flag = 0;
    52.             if (_GetFPGAIIdx(uIdx, ref flag) == 0)
    53.                 return false;
    54.             bFlag = flag != 0;
    55.             return true;
    56.         }
    57.         public static bool GetFPGAInput(ref ulong ulData)
    58.         {
    59.             uint d1 = 0, d2 = 0;
    60.             if (_GetFPGAInput(ref d1, ref d2) == 0)
    61.                 return false;
    62.             ulData = (ulong)d1 | (ulong)d2 << 32;
    63.             return true;
    64.         }
    65.         public static bool SetFPGAIHWDebTime(uint uIdxGroup, uint time) => _SetFPGAIHWDebTime(uIdxGroup, time) != 0;
    66.  
    67.     }
    68.  
    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.

    Code (CSharp):
    1.     public static class FPGAInterfaceEx
    2.     {
    3.  
    4.         public class EFPGAException : System.Exception
    5.         {
    6.             public EFPGAException(string aMessage) : base(aMessage) { }
    7.         }
    8.         const string dllName = "YourDllName.dll";
    9.         [DllImport(dllName)]
    10.         private static extern byte _GetFPGAOCount(ref byte ucCount);
    11.         [DllImport(dllName)]
    12.         private static extern byte _SetFPGAOIdx(uint uIdx, byte bFlag);
    13.         [DllImport(dllName)]
    14.         private static extern byte _SetFPGAOAll(byte bFlag);
    15.         [DllImport(dllName)]
    16.         private static extern byte _SetFPGAOutput(uint ulData1, uint ulData2);
    17.         [DllImport(dllName)]
    18.         private static extern byte _GetFPGAOIdx(uint uIdx, ref byte bFlag);
    19.         [DllImport(dllName)]
    20.         private static extern byte _GetFPGAOutput(ref uint ulData1, ref uint ulData2);
    21.  
    22.         [DllImport(dllName)]
    23.         private static extern byte _GetFPGAICount(ref byte ucCount);
    24.         [DllImport(dllName)]
    25.         private static extern byte _GetFPGAIIdx(uint uIdx, ref byte bFlag);
    26.         [DllImport(dllName)]
    27.         private static extern byte _GetFPGAInput(ref uint ulData1, ref uint ulData2);
    28.         [DllImport(dllName)]
    29.         private static extern byte _SetFPGAIHWDebTime(uint uIdxGroup, uint time);
    30.  
    31.         public static byte GetFPGAOCount()
    32.         {
    33.             byte count = 0;
    34.             if (_GetFPGAOCount(ref count) == 0)
    35.                 throw new EFPGAException($"{nameof(GetFPGAICount)} failed");
    36.             return count;
    37.         }
    38.         public static void SetFPGAOIdx(uint uIdx, bool bFlag)
    39.         {
    40.             if (_SetFPGAOIdx(uIdx, bFlag ? 1 : 0) == 0)
    41.                 throw new EFPGAException($"{nameof(SetFPGAOIdx)} failed");
    42.         }
    43.         public static void SetFPGAOAll(bool bFlag)
    44.         {
    45.             if (_SetFPGAOAll(bFlag ? 1 : 0) == 0)
    46.                 throw new EFPGAException($"{nameof(SetFPGAOAll)} failed");
    47.         }
    48.         public static void SetFPGAOutput(ulong ulData)
    49.         {
    50.             if (_SetFPGAOutput((uint)(ulData & 0xFFFFFFFF), (uint)((ulData >> 32) & 0xFFFFFFFF)) == 0)
    51.                 throw new EFPGAException($"{nameof(SetFPGAOutput)} failed");
    52.         }
    53.         public static bool GetFPGAOIdx(uint uIdx)
    54.         {
    55.             byte flag = 0;
    56.             if (_GetFPGAOIdx(uIdx, ref flag) == 0)
    57.                 throw new EFPGAException($"{nameof(GetFPGAOIdx)} failed");
    58.             return flag != 0;
    59.         }
    60.         public static ulong GetFPGAOutput()
    61.         {
    62.             uint d1 = 0, d2 = 0;
    63.             if (_GetFPGAOutput(ref d1, ref d2) == 0)
    64.                 throw new EFPGAException($"{nameof(GetFPGAOutput)} failed");
    65.             return (ulong)d1 | (ulong)d2 << 32;
    66.         }
    67.  
    68.         public static byte GetFPGAICount()
    69.         {
    70.             byte count = 0;
    71.             if (_GetFPGAICount(ref count) == 0)
    72.                 throw new EFPGAException($"{nameof(GetFPGAICount)} failed");
    73.             return count;
    74.         }
    75.         public static bool GetFPGAIIdx(uint uIdx)
    76.         {
    77.             byte flag = 0;
    78.             if (_GetFPGAIIdx(uIdx, ref flag) == 0)
    79.                 throw new EFPGAException($"{nameof(GetFPGAIIdx)} failed");
    80.             return flag != 0;
    81.         }
    82.         public static ulong GetFPGAInput()
    83.         {
    84.             uint d1 = 0, d2 = 0;
    85.             if (_GetFPGAInput(ref d1, ref d2) == 0)
    86.                 throw new EFPGAException($"{nameof(GetFPGAInput)} failed");
    87.             return (ulong)d1 | (ulong)d2 << 32;
    88.         }
    89.         public static void SetFPGAIHWDebTime(uint uIdxGroup, uint time)
    90.         {
    91.             if (_SetFPGAIHWDebTime(uIdxGroup, time) == 0)
    92.                 throw new EFPGAException($"{nameof(SetFPGAIHWDebTime)} failed");
    93.         }

    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.
     
    kamercode likes this.
  14. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,999
    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.
     
  15. kamercode

    kamercode

    Joined:
    Jul 30, 2020
    Posts:
    8
    Mahy thanks for your answer and sorry i forget maybe i have to intilise the FPGA before

    Code (C++):
    1. BOOL _FPGAInit(DWORD * dwError, PVOID funcIntHandler)