>  기사  >  백엔드 개발  >  구조 배열을 전달하기 위해 C++DLL을 호출하는 C# 솔루션에 대한 자세한 설명

구조 배열을 전달하기 위해 C++DLL을 호출하는 C# 솔루션에 대한 자세한 설명

黄舟
黄舟원래의
2017-03-28 11:52:152076검색

이 기사에서는 C++DLL을 호출하여 구조 배열을 전달하는 C#의 궁극적인 솔루션에 대한 관련 정보를 주로 소개합니다. 필요한 친구는

C#을 참조하여 C++DLL을 호출하여 전달할 수 있습니다. 구조 본문 배열을 위한 최고의 솔루션

프로젝트를 개발할 때 C++로 캡슐화된 DLL을 호출해야 합니다. 일반적인 유형은 일반적으로 C#에 해당합니다. DLL에서 함수를 도입하기만 하면 됩니다. 그렇습니다. 그러나 구조체, 구조체 배열 또는 구조 포인터가 전달되면 C#에는 해당 유형이 없다는 것을 알 수 있습니다. 이때 무엇을 해야 할까요? 첫 번째 반응은 C#도 구조를 정의한 다음 이를 매개 변수로 전달한다는 것입니다. 그러나 구조를 정의하고 매개변수를 전달하려고 하면 예외가 발생하거나 구조가 전달되지만 반환 값이 디버깅 추적한 후에는 아닙니다. 그 값이 전혀 변하지 않은 것을 확인하였고, 코드는 다음과 같습니다.

[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); 
      } 
    }

나중에 정보를 찾아보니 C#이 관리되는 메모리라는 기사가 나오네요. 이제 관리되지 않는 메모리인 구조체 배열을 전달해야 하고 Marsh를 사용해야 합니다. 그런 다음 공간을 지정하십시오. 따라서 다음과 같이 구조를 변경합니다.

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;          
 
   };
그런데 이렇게 개선한 후에도 여전히 실행 결과가 이상적이지 않고 값도 틀리거나 변경되지 않습니다. 그 이유는 무엇입니까? 끊임없이 정보를 검색한 끝에 마침내 구조체의 전송에 대해 언급한 기사를 찾았습니다. 일부는 위와 같이 수행할 수 있지만 일부는 수행할 수 없습니다. 특히 매개변수가 C++의 구조 포인터 또는 구조 배열 포인터인 경우 포인터도 수행해야 합니다. C# 호출에 대응하기 위해 사용되며, 다음 코드는 추후 개선될 예정입니다.

[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); 
    } 
  }

이번에는

인터페이스

가 IntPtr로 변경되었다는 점에 유의하세요. 위의 방법을 통해 최종적으로 구조체 배열이 전달됩니다. 그러나 여기서 주목해야 할 점은 서로 다른 컴파일러가 구조의 크기를 확신하지 못할 수 있다는 것입니다. 예를 들어 위 구조
가 BCB에서 바이트 정렬되지 않은 경우 때로는 더 커질 수 있습니다. 일반 구조보다 본문 크기가 2바이트 더 큽니다. BCB의 기본값은 2바이트 정렬이고 VC의 기본값은 1바이트 정렬이기 때문입니다. 이 문제를 해결하려면 BCB 구조에 바이트 정렬을 추가하거나 C#에서 2바이트(더 많은 경우)를 더 엽니다. 바이트 정렬 코드는 다음과 같습니다.

#pragma pack(push,1) 
  struct Info 
{ 
  int OrderNO; 
       
  char UniqueCode[32]; 
 
  float CpuPercent; 
}; 
#pragma pack(pop)

Marsh.AllocHGlobal을 사용하여 구조 포인터용 메모리 공간을 엽니다. 목적은 관리되지 않는 메모리를 변환하는 것입니다. 따라서 Marsh.AllocHGlobal을 사용하지 않는 경우 다른 방법이 있습니까?


사실 C++에서는 포인터든 배열이든 결국 하나씩 메모리에 저장된다. 즉, 결국 1의 형태로 표시된다. -차원 바이트 배열이므로 동일한 크기의

1차원 배열

을 열면 괜찮을까요? 대답은 '예'입니다. 구현은 아래에 나와 있습니다.

[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
데이터를 어떻게 전송해야 할지 막막할 때는 바이트 배열로 전송하는 것을 고려해 볼 수 있습니다(4바이트(32비트 미만)이면 정수도 허용됩니다). 길이가 일치하는 한 데이터를 가져온 후 유형 규칙에 따라 필요한 데이터로 변환해야 합니다. 일반적으로 목적은 달성될 수 있습니다.

위 내용은 구조 배열을 전달하기 위해 C++DLL을 호출하는 C# 솔루션에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.