2017-04-21 24 views
0

현재 C++ DLL과 통신하는 일부 C# 코드를 작성하고 있습니다. 이것은 내가 - 또는 내 회사의 다른 누군가 - 어떤 경험이있는 영역이 아닙니다. 가장 적은 것을 말하면서 눈을 뜨게되었습니다.비 관리 C# 코드의 액세스 위반 소스 추적

많은 독서, 시행 착오 및 좌절감을 겪은 후, 나는 대부분의 꼬임을 철저히 제거하고 대부분 기능을 발휘하게되었습니다. 그러나 때로는 여전히 내게 이것을 던졌습니다 ...

System.AccessViolationException : 보호 된 메모리를 읽거나 쓰려고했습니다. 이것은 종종 다른 메모리가 손상되었다는 표시입니다.

.. 그리고 나서 죽는다. 이 오류는 병렬 스레드에서 호출을 실행할 때만 나타납니다. 이 dll은 스레드로부터 안전해야하고 올바르게 처리되어야한다고 믿을만한 충분한 이유가 있습니다.

이 오류의 원인은 항상 같은 함수를 호출입니다

: 나는이 함수를 정의하는 라이브러리의 헤더 파일이

[DllImport(DLL, SetLastError = true, CharSet = CharSet.Ansi)] 
public static extern int QABatchWV_Close(IntPtr vi1); 

: 어떤에서

__declspec(dllimport) int __stdcall QABatchWV_Close(int); 

를 I 내가 처분 할 수있는 SafeHandle 및 MarshalAs와 같은 추가 도구가 있음을 이해하십시오. 솔직히, 나는이 상황에서 어떻게 그들을 가장 잘 배치 할 수 있는지 확신 할 수 없다.

이 오류는 표시하는 데 몇 시간의 시간이 걸리는 경향이 있으므로 조정과 희망은 여기서 생산적인 접근 방식이 될 수 없습니다. 누구든지 내가 C++ 함수를 호출 할 때 잘못된 일을 할 수 있다고 지적 할 수 있습니까?

+0

반환 값에서와 같이 매개 변수를'int' 대신'IntPtr'로 사용하기로 결정한 이유가 있습니까? – nvoigt

+0

@novigt 실험적으로 행동하도록 유도합니다. 나는 양쪽 끝에서 int로, 양쪽 끝에서 IntPtr, 그리고 그림과 같이 각각 하나씩 시도했다. 나는 이것에 관해 나의 깊이에서 빠져 나가고, 단지 필사적으로 물건을 시험해 보는 것을 기뻐한다. –

+0

C++의 라이브러리를 컴파일 할 때'int'가 어떤 크기인지 알아야합니다. 그렇다면 C#에서 올바른 것을 고를 필요가 있습니다. 'IntPtr'은 코드를 어디서 실행하는지에 따라 32 비트 또는 64 비트입니다. C++ 라이브러리는 런타임시 C#이 할 수 있음을 결정할 수 없으며 컴파일시 수정 될 것입니다. – nvoigt

답변

1

d (C++가 아닌) dll을 호출하는 데 사용하는 다음 코드를 살펴보십시오. 나는 그것이 정말로 당신의 질문에 대한 답이 아니라는 것을 알고 있습니다. 그러나 아마도 당신은이 전진하는 것을 사용할 수 있습니다.

dll 선언의 "CallingConvention"- 지정자와 try catch의 "finally"부분의 "FreeGlobal"에 유의하십시오. C#에서

public class csInterface 
{ 
    [DllImport(@"myDLL.dll", EntryPoint = "dllFunc", CallingConvention = CallingConvention.StdCall)] 
    private static extern void dllFunc(IntPtr inp, IntPtr outp); 

    public static int myDll(ref MyInput myInput, ref MyOutput myOutput) 
    { 
     int sizeIn, sizeOut; 
     IntPtr ptr_i = IntPtr.Zero, ptr_u = IntPtr.Zero; 

     sizeIn = Marshal.SizeOf(typeof(myInput)); 
     sizeOut = Marshal.SizeOf(typeof(myOutput)); 
     /* Calling C */ 
     try 
     { 
      ptr_i = Marshal.AllocHGlobal(sizeIn); 
      ptr_u = Marshal.AllocHGlobal(sizeOut); 

      Marshal.StructureToPtr(myInput, ptr_i, true); 
      Marshal.StructureToPtr(myOutput, ptr_u, true); 

      dllFunc(ptr_i, ptr_u); 

      myOutput = (MyOutput)(Marshal.PtrToStructure(ptr_u, typeof(MyOutput))); 
     } 
     catch (Exception) 
     { 
      //Return something meaningful (or not) 
      return -999; 
     } 
     finally 
     { 
      //Free memory 
      Marshal.FreeHGlobal(ptr_i); 
      Marshal.FreeHGlobal(ptr_u); 
     } 
     //Return something to indicate it all went well 
     return 0; 
    } 
} 

나는 나의 유형의 출력 비슷한

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
    public struct MySubType 
    { 
     public int a; 
     public double b; 
    } 

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
    public struct MyInput 
    { 
     [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 4)] 
     public string aString; //A string of length 3 
     public bool aBoolean;  
     public int anInt; 
     public char aChar; 
     public double aDouble; 

     [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = 12)] 
     public MySubType[] aSubType; //Array of struct of length 12 
    } 

그리고 뭔가를 선언합니다. C에서 이제

(그 아마 동일 또는 C++과 유사) 난 내 DLL

__declspec(dllexport) void _stdcall dllFunc(MyCInput *myCInput, MyCOutput *myCOutput) 
{ 
    //Code 
} 

선언 그리고 분명히 정확히

typedef struct 
{ 
    int a; 
    double b; 
} MyCSubType; 

typedef struct 
{ 
    char aString[4]; 
    int aBoolean; //This needs to be cast over to your C boolean type 
    int anInt; 
    char aChar; 
    double aDouble; 
    MyCSubType myCSubType[12]; 
} MyCType; 

이제 C#을 유형을 반영해야 해당 C 유형 이 예제에서 사용한 유형은 내 코드에서 사용한 것과 정확히 일치하지 않으며이 코드를 테스트하지 않았습니다. 따라서 오타가있을 수 있지만 "원리"는 괜찮습니다.

1

문자열이 없으므로 우선 여기에서 Charset을 설정할 필요가 없습니다.모든

둘째 - CPP의 기능은 가져 오지 내 보낸로 선언되어야한다, 그래서처럼이 보일 것입니다 :

[DllImport(DLL, SetLastError = true, CallingConvention=CallingConvention.Stdcall)] 
:

__declspec(dllimport) int __stdcall QABatchWV_Close(int); 

다음, 당신은 STDCALL하기 위해 C# 코드에서 호출 규칙 설정해야합니다

다음에는 IntPtr 대신 int가 C# 코드에 있어야합니다. 그리고 나는이 함수의 이름 (C++ dll에서)이 엉망이며 QABatchWV_Close가 아니라 QABatchWV_Close @ 32와 같은 것을 확신합니다. "dll 내보내기 뷰어"를 사용하여 확인해야합니다.