우리는 어떨때 Unsafe Code를 사용해야 할까 고민을 한다.

아래 그림과 같이 우린 Unsafe Code를 위해 아래와 같이 Setting을 해주면 된다.

그러나 그것만으로 끝나지 않는다.

여기서부터가 가장 큰 의문에 휩싸인다.

"왜~ 그리고 언제~" Unsafe Code를 사용하는 것인가?

필자가 사용한 경우를 토대로 설명을 하는게 좀 명쾌할 것 같다.

public unsafe static class DllClass

{

[DllImport("test.dll", SetLastError = true)]

public static extern ReturnValue* TestDllFunc(string strInput, ref int refTotal);

}

[StructLayout(LayoutKind.Sequential, Pack = Compile.PackSize), CLSCompliant(false)]

public unsafe struct ReturnValue

{

public fixed byte btTest[20];

public UInt16 iTest;

}

public class ReturnValue2

{

public string strTest = "";

public short iTest = 0;

}

일단 먼저 test.dll이 있어야 겠다. void형 pointer로 반환하는 것도 괜찮고, 구조체 pointer로 반환하는 것도 괜찮다. 암튼 그 내부를 구현하는건 마음이니 일단 저런식으로 한다고 치자.

저기서 중요한건 반환값이 ReturnValue 구조체 pointer형이라는 것이다.

dll 만들고, DllImport 만들어주고, 반환될 값 unsafe struct로 잘 만들어 준다.

물론, 반환되는 값의 struct 변수들은 동일해야 함은 당근이겄지만...

아래 코드들은 unsafe class내에서 이루어 져야 한다. 안그럼 저런 "*" 따윈 쓰지도 못한다.

ReturnValue* returnValues = NULL;

int nTotal = 0;

returnValues = DllClass.TestDllFunc("test string", ref nTotal);

여기서부터 중요한건 저 ReturnValue의 Array(?)를 어떻게 가공하냐는 것이다. 아래 코드를 보면

그 해답이 있다.

List<ReturnValue2> returnNodes = new List<ReturnValue2>();

ASCIIEncoding encodeASCII = new ASCIIEncoding();

for (int i = 0; i < nTotal; i++)

{

byte[] btTest = null;

ReturnValue2 rt = new ReturnValue2 ();

rt.iTest = (short)returnValues ->iTest ;

PtrToBuffer(returnValues->btTest, ref btTest );

rt.strTest = encodeASCII.GetString(btTest);

returnNodes.Add(rt);

}

/// <summary>

/// 포인터 스트링에서 byte Buffer로 변환

/// </summary>

[CLSCompliant(false)]

private int PtrToBuffer(byte* ptr, ref byte[] buffer)

{

int size = ByteStrLen(ptr);

if (size > (buffer == null ? 0 : buffer.Length))

Array.Resize<byte>(ref buffer, size);

if (ptr != null)

Marshal.Copy((IntPtr)ptr, buffer, 0, size);

return size;

}

/// <summary>

/// 길이 계산해서 Byte String으로 변환

/// </summary>

[CLSCompliant(false)]

private static int ByteStrLen(byte* str)

{

byte* tmp = str;

int len = 0;

if (tmp != null)

{

while (*tmp != 0) tmp++;

len = (int)(tmp - str);

}

return len;

}

위와 같은 아름다운 코드로 인해 닷넷에서 struct 포인터 반환형을 사용할 수 있는 것이다.

여러 짱나는 마샬링을 통해 처리하는 것보다 때로는 위와 같이 처리하는 것이 보다 더 깔끔하지 않을까 한다.

Posted by 요지
,