이 기사에서는 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;
};
[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의 형태로 표시된다. -차원 바이트 배열이므로 동일한 크기의