2009-12-07 62 views
4

나는 P/Invoke와 함께 C#을 사용하여 DLL 메서드에 액세스합니다.C#에서 배열 멤버가있는 마샬 러 구조체

[StructLayout(LayoutKind.Sequential)] 
public class USER_LIST 
{ 
    public uint NumUsers; 
    [MarshalAs(UnmanagedType.ByValArray)] 
    public USER_LIST_ITEM [] List; 
} 

[StructLayout(LayoutKind.Sequential)] 
public class USER_LIST_ITEM 
{ 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] 
    public string name; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] 
    public string address; 
}; 

하지만 오류가 다음

typedef struct user_list { 
    unsigned short NumUsers; 
    USER_LIST_ITEM List[VARLEN]; 
} USER_LIST 

typedef struct user_list_item { 
    char name[260]; 
    unsigned char address[256]; 
} USER_LIST_ITEM 

내가했던 구조체의 레이아웃입니다 :

[DllImport("userManager.dll")] 
static extern int GetUsers(out IntPtr userList); 

기존 구조체 : 방법의 정의는 다음과 같다 언 마샬하려고 할 때 :

USER_LIST userList = new USER_LIST(); 
// Prepare pointer 
IntPtr uList = Marshal.AllocHGlobal(Marshal.SizeOf(userList)); 
Marshal.StructureToPtr(userList, uList, false); 
result = GetUsers(out uList); 

Marshal.PtrToStructure(uList, userList); <-- 

런타임에 치명적인 오류가 발생했습니다. 오류의 주소는 0x79f82af6, 스레드 0x464에있었습니다. 오류 코드는 0xc0000005입니다. 이 오류는 CLR 또는 안전하지 않거나 확인할 수없는 사용자 코드 부분의 버그 일 수 있습니다. 이 버그의 일반적인 출처에는 스택을 손상시킬 수있는 COM-interop 또는 PInvoke의 사용자 마샬링 오류가 포함됩니다.

NumUsers 속성이 올바르지 만 배열을 비 정렬화할 때 오류가 발생하는 것 같습니다. 이견있는 사람?

+0

원본 GetUsers 기능의 서명은 무엇입니까? – dtb

+0

왜 공간을 할당하고 포인터를 준비합니까? 'out uList'는'uList'가 덮어 씌워 질 것이지만 네이티브 코드에 값이 전달되지 않는다는 것을 의미합니다. – dtb

+0

원래 서명 : 상태 UMAPI받는 사람 ( OUT USER_LIST ** userList ); – Sergi

답변

4

, 당신은 될 것 배열이 무엇인지 길이 마샬 말할 필요가있다. 코드를 사용하면 마샬 러가 길이가 0 인 배열을 할당하거나 null을 사용하여 충돌을 일으킬 수 있습니다. 불행하게도 가변 길이 out 배열을 구조체의 멤버로 지정할 수있는 방법이없는 것 같습니다. MarshalAs.SizeParamIndex은 메서드에서만 작동하기 때문입니다. 당신은 MarshalAs.SizeConst를 사용하여 대형, 일정한 크기의 배열을 지정 넘어갈 수도 있지만, 일반적으로이 같은 (아마도 호출자 할당) 반환 버퍼 구문 분석해야 할 것 :

var count = Marshal.ReadInt32 (uList) ; 
var users = new List<USER_LIST_ITEM> () ; 
var ptr = (long)uList + 4 ; 
for (int i = 0 ; i < count ; ++i) 
{ 
    users.Add (Marshal.PtrToStructure (typeof (USER_LIST_ITEM), 
     new IntPtr (ptr))) ; 
    ptr += Marshal.SizeOf (typeof (USER_LIST_ITEM)) ; 
} 

당신은 추가로 지불해야합니다을 맞춤 & 패딩 및 32/64 비트 문제에주의하십시오.

+0

수동 매셔 닝이 잘 작동했습니다. 고마워 – Sergi

3

List이 아직 할당되지 않았기 때문입니다.

모든 필드를 초기화해야합니다.

내가 보는 또 다른 문제는 다음과 같이이다 : 당신은 outref 안 있는지 확인

IntPtr uList = Marshal.AllocHGlobal(Marshal.SizeOf(userList)); 
... 
result = GetUsers(out uList); 

있습니까? 다른 점은 없습니다 (ref이 맞는지 확실하지 않음).

업데이트 : 코드를 다시 보면,이 작업을 수행해야합니다 (그리고 메모리 누수가 눈을 찌를 필요가 없습니다). 다시

IntPtr uList; 
var result = GetUsers(out uList); 

var userlist = (USER_LIST) Marshal.PtrToStructure(ulist, typeof(USER_LIST)); 

Marshal.FreeHGlobal(ulist); // pray here or shoot the author of the C function 

업데이트 :

귀하의 페이지는/서명을 호출하는 것은 가능성이 잘못하거나 잘못 해석하는 것입니다.

는 아마와 같은 이름을 추측 할 수 있습니다

int GetUsers(USER_LIST* ulist); 

을 그리고 당신은 무엇을 그 같은 일이 아닙니다.

이 경우 솔루션은 간단합니다.

클래스로 변경 USER_LIST (순차적 인 레이아웃을 유지)하고 사용

// pinvoke sig 
int GetUsers(USER_LIST ulist); 

var ulist = new USER_LIST(); 
// initialize fields 
var r = GetUsers(ulist); 

- 또는 -

전화를 ref에 의해.

// pinvoke sig 
int GetUsers(ref USER_LIST ulist); 

var ulist = new USER_LIST(); 
// initialize fields 
var r = GetUsers(ref ulist); 

이 방법을 사용하면 수동 정렬 화 엉망 필요 없다, 나는 메모리 누수에 대한 더 이상 가능성이 볼 수 없습니다.

최종 업데이트 :

GetUsers 반환 값이 계산되는과 USER_LIST의 목록에 대한 포인터를 반환 것 같습니다, 당신이 게시 서명을 감안할 때. 좋은 기억 누출 거기.

어쨌든 여기서는 안전하지 않은 접근 방식을 시도해보고 결과를 살펴보고 모든 것이 해제되는지 확인하십시오. (나는 아직도 당신이 저자를 쏜다 고 생각한다).

+0

반환 값은 0 또는 1이며, 호출 성공을 나타 내기 위해서입니다. – Sergi

+0

@Sergi : 그러면 큰 누출과 부팅에 좋은 버퍼 오버런이 있습니다! – leppie

+0

@Sergi : 안전하지 않을 경우 C와 똑같이 보이며 가장 쉽습니다. – leppie

1

원본 코드가 너무 잘못 생각한 것은 아닙니다. 방금 ​​wrong overloadMarshal.PtrToStructure으로 사용했습니다.

시도해 보셨습니까? 안전하지 않은 코드를 사용

[DllImport("userManager.dll")] 
static extern int GetUsers(out IntPtr userList); 

[DllImport("userManager.dll")] 
static extern void UMFree(IntPtr userList); 

static void Main() 
{ 
    IntPtr userList;    // no need to allocate memory in managed code; 
    GetUsers(out userList);  // memory is allocated by native function 
    USER_LIST u = (USER_LIST)Marshal.PtrToStructure(userList, typeof(USER_LIST)); 
    UMFree(userList); 
} 

: 당신이 out 매개 변수로 사용되는 구조의 배열을 지정하는 경우

public unsafe struct USER_LIST 
{ 
    public uint numUsers; 
    public USER_LIST_ITEM* list; 
} 

public unsafe struct USER_LIST_ITEM 
{ 
    public fixed byte name[260]; 
    public fixed byte address[256]; 
} 

class Program 
{ 
    [DllImport("userManager.dll")] 
    static unsafe extern int GetUsers(USER_LIST** userList); 

    [DllImport("userManager.dll")] 
    static unsafe extern int UMFree(USER_LIST* userList); 

    private static unsafe void Main() 
    { 
     USER_LIST* list; 
     GetUsers(&list); 
     UMFree(list); 
    } 
} 
+0

코드 조각을 가져 주셔서 감사합니다. 그러나 작동하지 않습니다. PtrToStructure를 사용할 때 같은 오류가 발생합니다. 구조체 정의가 올바른지 잘 모릅니다. 왜냐하면 코드가 모두 나에게 맞을 수도 있기 때문입니다. – Sergi

+0

문제는 변수라고 생각합니다. USER_LIST의 크기가 지정된 배열. 마샬 러가 크기를 알 수있는 방법 (크기가'NumUsers'에 저장되는지는 알 수 없음). 안전하지 않은 코드를 사용하는 것이 가장 좋은 해결책 일 것입니다. – dtb

+0

BTW, C#에서 구조체는 실제로 클래스로 선언됩니다. 'struct '로 변경하려고 했습니까? – dtb