Heim  >  Artikel  >  Backend-Entwicklung  >  Detaillierte Erläuterung der Lösung für C#, die C++DLL aufruft, um ein Strukturarray zu übergeben

Detaillierte Erläuterung der Lösung für C#, die C++DLL aufruft, um ein Strukturarray zu übergeben

黄舟
黄舟Original
2017-03-28 11:52:152078Durchsuche

In diesem Artikel werden hauptsächlich relevante Informationen über die ultimative Lösung für C# zum Aufrufen von C++DLL zum Übergeben von Strukturarrays vorgestellt. Freunde in Not können sich auf

C# zum Aufrufen von C++DLL zum Übergeben beziehen Struktur Die ultimative Lösung für Body-Arrays

Beim Entwickeln eines Projekts müssen Sie eine in C++ gekapselte DLL aufrufen. Allgemeine Typen entsprechen im Allgemeinen C#, um die Funktion aus der DLL einzuführen Das ist es. Wenn jedoch eine Struktur, ein Strukturarray oder ein Strukturzeiger übergeben wird, werden Sie feststellen, dass es in C# keinen entsprechenden Typ gibt. Was ist zu diesem Zeitpunkt zu tun? Die erste Reaktion besteht darin, dass C# auch die Struktur definiert und sie dann als Parameter weitergibt. Wenn wir jedoch eine Struktur definieren und Parameter daran übergeben möchten, wird eine Ausnahme ausgelöst oder die Struktur wird übergeben, aber der Rückgabewert ist nicht das, was wir wollen festgestellt, dass sich diese Werte überhaupt nicht geändert haben, der Code lautet wie folgt.

Später, nach der Suche nach Informationen, gab es einen Artikel, in dem erwähnt wurde, dass C# verwalteter Speicher ist. Jetzt müssen wir das Strukturarray übergeben, das ein
[DllImport("workStation.dll")] 
    private static extern bool fetchInfos(Info[] infos); 
    public struct Info 
    { 
      public int OrderNO; 
 
      public byte[] UniqueCode; 
 
      public float CpuPercent;          
 
    }; 
    private void buttonTest_Click(object sender, EventArgs e) 
    { 
      try 
      { 
      Info[] infos=new Info[128]; 
        if (fetchInfos(infos)) 
        { 
          MessageBox.Show("Fail"); 
        } 
      else 
      { 
          string message = ""; 
          foreach (Info info in infos) 
          { 
            message += string.Format("OrderNO={0}\r\nUniqueCode={1}\r\nCpu={2}", 
                       info.OrderNO, 
                       Encoding.UTF8.GetString(info.UniqueCode), 
                       info.CpuPercent 
                       ); 
          } 
          MessageBox.Show(message); 
      } 
      } 
      catch (System.Exception ex) 
      { 
        MessageBox.Show(ex.Message); 
      } 
    }
Attribut

unverwalteter Speicher ist und sein muss mit Marsh angegeben. Ändern Sie also die Struktur wie folgt.

Allerdings sind die Laufergebnisse nach solchen Verbesserungen immer noch nicht optimal und die Werte sind entweder falsch oder wurden nicht geändert. Was ist der Grund dafür? Nachdem ich ständig nach Informationen gesucht hatte, fand ich endlich einen Artikel, in dem die Übertragung von Strukturen erwähnt wurde. Einige können wie oben durchgeführt werden, andere jedoch nicht, insbesondere wenn der Parameter ein Strukturzeiger oder ein Strukturarray-Zeiger in C++ ist verwendet werden, um C#-Aufrufen zu entsprechen, und der folgende Code wird später verbessert.
StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] 
   public struct Info 
   { 
     public int OrderNO; 
 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] 
     public byte[] UniqueCode; 
 
     public float CpuPercent;          
 
   };

Es ist zu beachten, dass zu diesem Zeitpunkt die
[DllImport("workStation.dll")] 
  private static extern bool fetchInfos(IntPtr infosIntPtr); 
  [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] 
  public struct Info 
  { 
    public int OrderNO; 
 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] 
    public byte[] UniqueCode; 
 
    public float CpuPercent; 
 
  }; 
  private void buttonTest_Click(object sender, EventArgs e) 
  { 
    try 
    { 
      int workStationCount = 128; 
      int size = Marshal.SizeOf(typeof(Info)); 
      IntPtr infosIntptr = Marshal.AllocHGlobal(size * workStationCount); 
      Info[] infos = new Info[workStationCount]; 
      if (fetchInfos(infosIntptr)) 
      { 
        MessageBox.Show("Fail"); 
        return; 
      } 
      for (int inkIndex = 0; inkIndex < workStationCount; inkIndex++) 
      { 
        IntPtr ptr = (IntPtr)((UInt32)infosIntptr + inkIndex * size); 
        infos[inkIndex] = (Info)Marshal.PtrToStructure(ptr, typeof(Info)); 
      } 
 
      Marshal.FreeHGlobal(infosIntptr); 
 
      string message = ""; 
      foreach (Info info in infos) 
      { 
        message += string.Format("OrderNO={0}\r\nUniqueCode={1}\r\nCpu={2}", 
                    info.OrderNO, 
                    Encoding.UTF8.GetString(info.UniqueCode), 
                    info.CpuPercent 
                    ); 
      } 
      MessageBox.Show(message); 
 
    } 
    catch (System.Exception ex) 
    { 
      MessageBox.Show(ex.Message); 
    } 
  }
Schnittstelle

in IntPtr geändert wurde. Durch die obige Methode wird schließlich das Strukturarray übergeben. Hierbei ist jedoch zu beachten, dass verschiedene Compiler sich über die Größe der Struktur nicht sicher sind. Wenn die obige Struktur
beispielsweise nicht in BCB byteorientiert ist, ist sie manchmal größer Die Körpergröße beträgt 2 Byte mehr als die normale Struktur. Weil BCB standardmäßig die 2-Byte-Sortierung verwendet und VC standardmäßig die 1-Byte-Sortierung verwendet. Um dieses Problem zu lösen, fügen Sie entweder die Byte-Ausrichtung zur BCB-Struktur hinzu oder öffnen Sie zwei weitere Bytes (falls mehr vorhanden sind) in C#. Der Byte-Ausrichtungscode lautet wie folgt.

Verwenden Sie Marsh.AllocHGlobal, um Speicherplatz für Strukturzeiger freizugeben. Der Zweck besteht darin, ihn in nicht verwalteten Speicher umzuwandeln, gibt es dann eine andere Möglichkeit?
#pragma pack(push,1) 
  struct Info 
{ 
  int OrderNO; 
       
  char UniqueCode[32]; 
 
  float CpuPercent; 
}; 
#pragma pack(pop)


Egal ob es sich um einen Zeiger oder ein Array in C++ handelt, wird er letztendlich einzeln im Speicher gespeichert. Mit anderen Worten, er wird letztendlich in Form einer Eins angezeigt -dimensionales Byte-Array. Wenn wir also ein

eindimensionales Array

gleicher Größe öffnen, ist das in Ordnung? Die Antwort ist ja, die Implementierung ist unten angegeben.

Wenn Sie sich wirklich nicht vorstellen können, wie Sie Daten übertragen sollen, können Sie erwägen, sie als Byte-Array zu übertragen (sogar eine Ganzzahl ist in Ordnung, solange sie 4 Bytes (unter 32 Bit) öffnet )), solange es entsprechend der Länge geöffnet ist, müssen die Daten nach dem Abrufen gemäß den Typregeln in die erforderlichen Daten konvertiert werden. Im Allgemeinen kann der Zweck erreicht werden.
[DllImport("workStation.dll")] 
    private static extern bool fetchInfos(IntPtr infosIntPtr); 
    [DllImport("workStation.dll")] 
    private static extern bool fetchInfos(byte[] infos); 
    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] 
    public struct Info 
    { 
      public int OrderNO; 
 
      [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] 
      public byte[] UniqueCode; 
 
      public float CpuPercent; 
 
    }; 
 
   
    private void buttonTest_Click(object sender, EventArgs e) 
    { 
      try 
      { 
        int count = 128; 
        int size = Marshal.SizeOf(typeof(Info)); 
        byte[] inkInfosBytes = new byte[count * size];         
        if (fetchInfos(inkInfosBytes)) 
        { 
          MessageBox.Show("Fail"); 
          return; 
        } 
        Info[] infos = new Info[count]; 
        for (int inkIndex = 0; inkIndex < count; inkIndex++) 
        { 
          byte[] inkInfoBytes = new byte[size]; 
          Array.Copy(inkInfosBytes, inkIndex * size, inkInfoBytes, 0, size); 
          infos[inkIndex] = (Info)bytesToStruct(inkInfoBytes, typeof(Info)); 
        } 
 
        string message = ""; 
        foreach (Info info in infos) 
        { 
          message += string.Format("OrderNO={0}\r\nUniqueCode={1}\r\nCpu={2}", 
                      info.OrderNO, 
                      Encoding.UTF8.GetString(info.UniqueCode), 
                      info.CpuPercent 
                      ); 
        } 
        MessageBox.Show(message); 
 
      } 
      catch (System.Exception ex) 
      { 
        MessageBox.Show(ex.Message); 
      } 
    } 
 
    #region bytesToStruct 
    /// <summary> 
    /// Byte array to struct or classs. 
    /// </summary> 
    /// <param name=”bytes”>Byte array</param> 
    /// <param name=”type”>Struct type or class type. 
    /// Egg:class Human{...}; 
    /// Human human=new Human(); 
    /// Type type=human.GetType();</param> 
    /// <returns>Destination struct or class.</returns> 
    public static object bytesToStruct(byte[] bytes, Type type) 
    { 
 
      int size = Marshal.SizeOf(type);//Get size of the struct or class.      
      if (bytes.Length < size) 
      { 
        return null; 
      } 
      IntPtr structPtr = Marshal.AllocHGlobal(size);//Allocate memory space of the struct or class.  
      Marshal.Copy(bytes, 0, structPtr, size);//Copy byte array to the memory space. 
      object obj = Marshal.PtrToStructure(structPtr, type);//Convert memory space to destination struct or class.      
      Marshal.FreeHGlobal(structPtr);//Release memory space.   
      return obj; 
    } 
    #endregion

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der Lösung für C#, die C++DLL aufruft, um ein Strukturarray zu übergeben. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn