C 애플리케이션을 C #으로 이식하고 있습니다. C 앱은 타사 DLL에서 많은 함수를 호출하므로 이러한 함수에 대한 P / Invoke 래퍼를 C #으로 작성했습니다. 이러한 C의 기능 중 일부는 내가 사용, 그래서 나는 C #을 응용 프로그램에서 사용할 수있는 데이터를 할당 IntPtr
'들, Marshal.PtrToStructure
그리고 Marshal.Copy
관리 변수로 기본 데이터 (배열과 구조)을 복사 할 수 있습니다.
불행히도 C # 앱은 C 버전보다 훨씬 느린 것으로 판명되었습니다. 빠른 성능 분석은 위에서 언급 한 마샬링 기반 데이터 복사가 병목임을 보여주었습니다. 대신 포인터를 사용하도록 다시 작성하여 C # 코드의 속도를 높이는 것을 고려하고 있습니다. C #에서 안전하지 않은 코드와 포인터에 대한 경험이 없기 때문에 다음 질문 에 대한 전문가 의견이 필요합니다 .
unsafe
대신 코드와 포인터 를 사용하는 단점은 무엇입니까 ? 예를 들어, 어떤 식 으로든 더 안전하지 않습니까 (의도 된 말장난)? 사람들은 마샬링을 선호하는 것 같지만 이유를 모르겠습니다.IntPtr
Marshal
상황을 더 명확하게하기 위해 작은 예제 코드를 함께 해킹했습니다 (실제 코드는 훨씬 더 복잡합니다). 이 예제가 "안전하지 않은 코드 및 포인터"대 "IntPtr 및 Marshal"에 대해 말할 때 제가 의미하는 바를 보여주기를 바랍니다.
MyLib.h
#ifndef _MY_LIB_H_
#define _MY_LIB_H_
struct MyData
{
int length;
unsigned char* bytes;
};
__declspec(dllexport) void CreateMyData(struct MyData** myData, int length);
__declspec(dllexport) void DestroyMyData(struct MyData* myData);
#endif // _MY_LIB_H_
MyLib.c
#include <stdlib.h>
#include "MyLib.h"
void CreateMyData(struct MyData** myData, int length)
{
int i;
*myData = (struct MyData*)malloc(sizeof(struct MyData));
if (*myData != NULL)
{
(*myData)->length = length;
(*myData)->bytes = (unsigned char*)malloc(length * sizeof(char));
if ((*myData)->bytes != NULL)
for (i = 0; i < length; ++i)
(*myData)->bytes[i] = (unsigned char)(i % 256);
}
}
void DestroyMyData(struct MyData* myData)
{
if (myData != NULL)
{
if (myData->bytes != NULL)
free(myData->bytes);
free(myData);
}
}
Main.c
#include <stdio.h>
#include "MyLib.h"
void main()
{
struct MyData* myData = NULL;
int length = 100 * 1024 * 1024;
printf("=== C++ test ===\n");
CreateMyData(&myData, length);
if (myData != NULL)
{
printf("Length: %d\n", myData->length);
if (myData->bytes != NULL)
printf("First: %d, last: %d\n", myData->bytes[0], myData->bytes[myData->length - 1]);
else
printf("myData->bytes is NULL");
}
else
printf("myData is NULL\n");
DestroyMyData(myData);
getchar();
}
IntPtr
및 사용하는 C # 응용 프로그램Marshal
Program.cs
using System;
using System.Runtime.InteropServices;
public static class Program
{
[StructLayout(LayoutKind.Sequential)]
private struct MyData
{
public int Length;
public IntPtr Bytes;
}
[DllImport("MyLib.dll")]
private static extern void CreateMyData(out IntPtr myData, int length);
[DllImport("MyLib.dll")]
private static extern void DestroyMyData(IntPtr myData);
public static void Main()
{
Console.WriteLine("=== C# test, using IntPtr and Marshal ===");
int length = 100 * 1024 * 1024;
IntPtr myData1;
CreateMyData(out myData1, length);
if (myData1 != IntPtr.Zero)
{
MyData myData2 = (MyData)Marshal.PtrToStructure(myData1, typeof(MyData));
Console.WriteLine("Length: {0}", myData2.Length);
if (myData2.Bytes != IntPtr.Zero)
{
byte[] bytes = new byte[myData2.Length];
Marshal.Copy(myData2.Bytes, bytes, 0, myData2.Length);
Console.WriteLine("First: {0}, last: {1}", bytes[0], bytes[myData2.Length - 1]);
}
else
Console.WriteLine("myData.Bytes is IntPtr.Zero");
}
else
Console.WriteLine("myData is IntPtr.Zero");
DestroyMyData(myData1);
Console.ReadKey(true);
}
}
unsafe
코드와 포인터 를 사용하는 C # 애플리케이션Program.cs
using System;
using System.Runtime.InteropServices;
public static class Program
{
[StructLayout(LayoutKind.Sequential)]
private unsafe struct MyData
{
public int Length;
public byte* Bytes;
}
[DllImport("MyLib.dll")]
private unsafe static extern void CreateMyData(out MyData* myData, int length);
[DllImport("MyLib.dll")]
private unsafe static extern void DestroyMyData(MyData* myData);
public unsafe static void Main()
{
Console.WriteLine("=== C# test, using unsafe code ===");
int length = 100 * 1024 * 1024;
MyData* myData;
CreateMyData(out myData, length);
if (myData != null)
{
Console.WriteLine("Length: {0}", myData->Length);
if (myData->Bytes != null)
Console.WriteLine("First: {0}, last: {1}", myData->Bytes[0], myData->Bytes[myData->Length - 1]);
else
Console.WriteLine("myData.Bytes is null");
}
else
Console.WriteLine("myData is null");
DestroyMyData(myData);
Console.ReadKey(true);
}
}
약간 오래된 스레드이지만 최근에 C #에서 마샬링으로 과도한 성능 테스트를 수행했습니다. 며칠 동안 직렬 포트에서 많은 데이터를 역 정렬 화해야합니다. 메모리 누수가없는 것이 중요했습니다 (가장 작은 누수가 몇백 만 호출 후에 중요해지기 때문입니다). 또한 매우 큰 구조체 (> 10kb)로 많은 통계 성능 (사용 된 시간) 테스트를 수행했습니다. 그것을 위해서 (아니요, 당신은 10kb 구조체를 가져서는 안됩니다 :-))
다음 세 가지 비 정렬 화 전략을 테스트했습니다 (정렬 화도 테스트했습니다). 거의 모든 경우에 첫 번째 (MarshalMatters)가 다른 두 개를 능가했습니다. Marshal. Copy는 항상 가장 느 렸고 나머지 두 개는 레이스에서 거의 매우 가깝습니다.
안전하지 않은 코드를 사용하면 심각한 보안 위험이 발생할 수 있습니다.
먼저:
public class MarshalMatters
{
public static T ReadUsingMarshalUnsafe<T>(byte[] data) where T : struct
{
unsafe
{
fixed (byte* p = &data[0])
{
return (T)Marshal.PtrToStructure(new IntPtr(p), typeof(T));
}
}
}
public unsafe static byte[] WriteUsingMarshalUnsafe<selectedT>(selectedT structure) where selectedT : struct
{
byte[] byteArray = new byte[Marshal.SizeOf(structure)];
fixed (byte* byteArrayPtr = byteArray)
{
Marshal.StructureToPtr(structure, (IntPtr)byteArrayPtr, true);
}
return byteArray;
}
}
둘째:
public class Adam_Robinson
{
private static T BytesToStruct<T>(byte[] rawData) where T : struct
{
T result = default(T);
GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);
try
{
IntPtr rawDataPtr = handle.AddrOfPinnedObject();
result = (T)Marshal.PtrToStructure(rawDataPtr, typeof(T));
}
finally
{
handle.Free();
}
return result;
}
/// <summary>
/// no Copy. no unsafe. Gets a GCHandle to the memory via Alloc
/// </summary>
/// <typeparam name="selectedT"></typeparam>
/// <param name="structure"></param>
/// <returns></returns>
public static byte[] StructToBytes<T>(T structure) where T : struct
{
int size = Marshal.SizeOf(structure);
byte[] rawData = new byte[size];
GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);
try
{
IntPtr rawDataPtr = handle.AddrOfPinnedObject();
Marshal.StructureToPtr(structure, rawDataPtr, false);
}
finally
{
handle.Free();
}
return rawData;
}
}
제삼:
/// <summary>
/// http://stackoverflow.com/questions/2623761/marshal-ptrtostructure-and-back-again-and-generic-solution-for-endianness-swap
/// </summary>
public class DanB
{
/// <summary>
/// uses Marshal.Copy! Not run in unsafe. Uses AllocHGlobal to get new memory and copies.
/// </summary>
public static byte[] GetBytes<T>(T structure) where T : struct
{
var size = Marshal.SizeOf(structure); //or Marshal.SizeOf<selectedT>(); in .net 4.5.1
byte[] rawData = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(structure, ptr, true);
Marshal.Copy(ptr, rawData, 0, size);
Marshal.FreeHGlobal(ptr);
return rawData;
}
public static T FromBytes<T>(byte[] bytes) where T : struct
{
var structure = new T();
int size = Marshal.SizeOf(structure); //or Marshal.SizeOf<selectedT>(); in .net 4.5.1
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(bytes, 0, ptr, size);
structure = (T)Marshal.PtrToStructure(ptr, structure.GetType());
Marshal.FreeHGlobal(ptr);
return structure;
}
}
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다