2011-05-02 3 views
8

소스 코드를 사용할 수없는 제 3의 독점적 인 DLL을 사용하고 있습니다. 그러나 SWIG 1.3.39를 사용하여 자동 생성 된 것으로 보이는 래퍼 코드는 나에게 제공됩니다. 래퍼 코드는 (DLL을 설명하는 일부 헤더를 사용하여) DLL로 컴파일하고 C++ 래퍼 DLL에 PInvoke 호출을 수행하는 C# 프로젝트를 컴파일하는 C++ 파일로 구성됩니다.64 비트 Windows에서 C++에서 C#으로 문자열을 반환 할 때 AccessViolationException을 방지하려면 어떻게해야합니까?

필자는 공급 업체의 설명서를 해석 할 때 대상 플랫폼에 따라 x86 또는 x64로 솔루션의 모든 것을 컴파일했습니다. 공급 업체는 독점 DLL의 32 비트 및 64 비트 버전을 모두 제공하며 주어진 빌드에 대해 올바른 버전을 사용하도록했습니다. 내 컴퓨터는 32 비트입니다. 릴리스 또는 디버그 빌드에서 내 시스템에서 내 응용 프로그램의 x86 버전을 테스트하면 정상적으로 작동하는 것 같습니다. 그러나 64 비트에서 응용 프로그램은 디버그 모드에서 작동하지만 릴리스 모드에서는 System.AccessViolationException으로 실패합니다.

나는 블로그 게시물을 야기한 this question and answer뿐만 아니라 디버그 대 릴리스 문제를 잘 설명하는 것으로 보이는 this nice blog entry을 읽었습니다. 그러나이 경우 문제를 해결하는 방법을 잘 모르겠습니다.

AccessViolationException은 실제 길이의 문자열이 C++ 래퍼에서 반환되거나 반환되도록 처음 시도 된 것으로 보입니다.

// In one file of the C# wrapper: 
public string GetKey() 
{ 
    // swigCPtr is a HandleRef to an object already created 
    string ret = csWrapperPINVOKE.mdMUHybrid_GetKey(swigCPtr); 
    return ret; 
} 

// In the csWrapperPINVOKE class in another file in the C# wrapper: 
[DllImport("csWrapper.dll", EntryPoint="CSharp_mdMUHybrid_GetKey")] 
public static extern StringBuilder mdMUHybrid_GetKey(HandleRef jarg1); 

그리고 C++ 래퍼에서 번잡 한 C++ 코드 :

SWIGEXPORT char * SWIGSTDCALL CSharp_mdMUHybrid_GetKey(void * jarg1) { 
    char * jresult ; 
    mdMUHybrid *arg1 = (mdMUHybrid *) 0 ; 
    char *result = 0 ; 

    arg1 = (mdMUHybrid *)jarg1; 
    result = (char *)(arg1)->GetKey(); 
    jresult = SWIG_csharp_string_callback((const char *)result); 
    return jresult; 
} 

SWIGEXPORT 가 이미 __declspec(dllexport)로 정의했다

다음은 잘못된 C# 코드입니다. 디버깅, I는 다음과 같이 정의 된 SWIG_csharp_string_callback을 발견 :

/* Callback for returning strings to C# without leaking memory */ 
typedef char * (SWIGSTDCALL* SWIG_CSharpStringHelperCallback)(const char *); 
static SWIG_CSharpStringHelperCallback SWIG_csharp_string_callback = NULL; 

합니다 (C# 1 래퍼) 대리인 설정되고 하였다

static string CreateString(string cString) { 
    return cString; 
} 

I과 같은 구조를 사용하는 코드 장난 시도 Marshal.PtrToStringAut을 사용할 수 없습니다. 이 문제를 어떻게 해결합니까?

+0

나는 똑같은 문제에 부딪 쳤고이 게시물은 실제로 그것을 고쳤다. 그러나, 나는 어떻게하면 불쾌한 코드를 발견했는지 알고 싶다. 릴리스 모드에서 프로젝트를 실행할 때 예외가 발생하지 않았기 때문입니다. "액세스 위반"은 내가 폴더를 해제하고 거기에서 직접 exe를 실행할 때 그림으로 나타납니다! – Simsons

+0

@Subhen 시간이 지났으므로 고용주가 변경되어 더 이상이 코드에 액세스 할 수 없습니다. 기억 나면 Windows Server 인스턴스에서이 작업을 실행하고 원격 디버거를 어떻게 든 사용할 수있었습니다. 나는 그것에 착각 할 수는 있었지만, 이것이 깨고있는 곳을 찾기 위해 하루 이틀이 걸렸습니다. 로깅은 물론 예외를 얻을 때 코드에있는 위치를 정확하게 기록 할 수 있다면 도움이 될 수 있습니다. – Andrew

답변

3

이 방법은 관리 코드가 비 관리 코드가 쓰여질 버퍼 (문자열 데이터)를 할당하는 것입니다. 어떤 이유로 든 비실용적이라고 가정하면, 관리 코드에서 할당 해제 할 수있는 방식으로 문자열 데이터를 할당해야합니다.

일반적으로 접근 방식은 LocalAlloc으로 메모리를 할당 한 다음 Marshal.FreeHGlobal을 사용하여 관리 코드에서 할당을 해제 할 수 있습니다. 이렇게하면 더 이상 (kludgy 및 분명히 비 기능적인) SWIG_csharp_string_callbackCreateString이 필요하지 않게됩니다.

C++ 코드 :

SWIGEXPORT HLOCAL SWIGSTDCALL CSharp_mdMUHybrid_GetKey(mdMUHybrid* jarg1) 
{ 
    char const* const str = jarg1->GetKey(); 
    std::size_t const len = std::strlen(str); 
    HLOCAL const result = ::LocalAlloc(LPTR, len + 1u); 
    if (result) 
     std::strncpy(static_cast<char*>(result), str, len); 
    return result; 
} 

C# 코드 : 여담으로

// In one file of the C# wrapper: 
public string GetKey() 
{ 
    return csWrapperPINVOKE.mdMUHybrid_GetKey(swigCPtr); 
} 

// ... 

public static class csWrapperPINVOKE 
{ 
    // ... 

    [DllImport("csWrapper.dll")] 
    private static extern IntPtr CSharp_mdMUHybrid_GetKey(HandleRef jarg1); 

    public static string mdMUHybrid_GetKey(HandleRef jarg1) 
    { 
     var ptr = CSharp_mdMUHybrid_GetKey(jarg1); 
     try 
     { 
      return Marshal.PtrToStringAnsi(ptr); 
     } 
     finally 
     { 
      Marshal.FreeHGlobal(ptr); 
     } 
    } 
} 

, 당신이 보여 C++ 코드의 작은 조각은 끔찍한 C-와-클래스 유물이다; 그것이 나머지 코드베이스를 대표한다면, 와우 ...:/

+0

글쎄, 모든 공정성, 코드 _does_ autogenerated 것으로 보인다 (그리고 공급 업체가 아닌 내부, 우리는 감사 할 수 있습니다). 당신의 대답에 감사드립니다. 할 수 있으면 내일 시도해 볼게. – Andrew

+0

@ildjarm 이것은 훌륭했습니다. 고맙습니다! Google 군중에게 알리기 위해 (예를 들어 경고를 없애기 위해'strncpy_s '를 사용하여) 몇 가지 조정을하고이 함수 대부분을 별도의 함수로 옮겨서 (돌아 오는 데 사용한) 모든 함수와 함께 사용할 수있었습니다 'char *'. C# 측에서, 나는 같은 종류의 일을했다. 존재하지 않는 여유 시간에는 적어도 2.0.1 버전이 비슷한 코드를 생성한다는 것을 다른 프로젝트에서 보았 기 때문에 swig에 기여하는 것을 고려해야합니다. 다시 한 번 감사드립니다! – Andrew

+0

CSharp_mdMUHybrid_GetKey 함수는 실제로 효율적이지 않습니다. 가능한 경우이 여분의 레이어를 피해야합니다. 그렇지 않으면 C++과의 Interop가 너무 비쌉니다. SWIG에 대한 경험이 없지만 Parg/Invoke를 통해 SWIG를 사용하여 C#에서 jarg1-> GetKey()를 호출 할 수 있습니까? – xInterop