2014-10-30 5 views
1

알 수있는 문자를 받아들이는 관리되지 않는 메서드를 호출 할 때 * C에서 # 인수, 모두 StringBuilder를 전달하고 관리되지 않는 C 코드를 수정하는 것이 가능하다 . 그러나 StringBuilder에 넣으려는 데이터의 크기를 알아야하므로 올바른 크기의 버퍼를 전달할 수 있습니다. 나는 이것을 도와주는 많은 스레드를 발견했다.** 관리 C# 코드에서 인수

그러나 char ** 인수를 허용하는 C 메서드가 있습니다.

이것은 (아래) check_if_encrypted 같은 방법 check_if_encrypted에서 다음과 같은 코드를 사용하여 오류 메시지 버퍼에 할당하는 공간이 얼마나되는지 알 필요 호출 방법없이 malloc 'D 에러 메시지를 제공 할 수 : *strTheCharStarStarArgument = strLocalMallocdErrorMessage을 그리고를 통해 호출 할 때 &strErrorMessagestrErrorMessagechar*

입니다. C#에서 이러한 메서드에 어떤 DLLImport 서명을 사용해야합니까?

는 예를 들어, 나는 다음과 같은 C 코드를 한 :

헤더 :

extern __declspec(dllexport) KeyFile *create_source_key_file_from_path(char *strPath); 
extern __declspec(dllexport) int check_if_encrypted(KeyFile *kfSourceKey, int ktSourceKeyType, const char **strErrorMessage); 

홈페이지 코드 :

KeyFile *create_source_key_file_from_path(char *strPath) { 
    /* Snip */ 
} 

int check_if_encrypted(KeyFile *kfSourceKey, int ktSourceKeyType, const char **strErrorMessage) { 
    /* Snip */ 
    char* strLocalErrorMessage = (char*)malloc(sizeof(char)*17); 
    strcpy(strLocalErrorMessage,"This is an error"); 
    *strErrorMessage = strLocalErrorMessage; 
    /* Snip */ 
} 

/* Snip */ 

int main(int argc, char *argv[]) { 
    KeyFile *kfSource; 
    char *strErrorMessage; 
    const char **strErrorMessageP = &strErrorMessage; 
    int intIsEncrypted; 

    kfSource = create_source_key_file_from_path("test.dat"); 
    intIsEncrypted = check_if_encrypted(kfSource,0,strErrorMessageP); 
    if (strErrorMessage != NULL) { 
     /* Handle error here. */ 
    } 
    else if (intIsEncrypted == 1) { 
     /* Handle encrypted file here. */ 
    } 
    else { 
     /* Handle unencrypted file here. */ 
    } 
} 

사용하여 C#으로이 주요 방법을 복제하려고 시도한은 check_if_encrypted 방법 관리되지 않는 메서드로 인해 check_if_encrypted를 호출 할 때 AccessViolationException (보호 된 메모리를 읽거나 쓰려고 시도 함)이 발생했습니다.

[DllImport("PPKConverter.exe", SetLastError = true, CallingConvention = CallingConvention.Cdecl, CharSet=CharSet.Ansi)] 
public extern static IntPtr create_source_key_file_from_path([MarshalAs(UnmanagedType.LPStr)] StringBuilder strPath); 
[DllImport("PPKConverter.exe", SetLastError = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] 
public extern static int check_if_encrypted(IntPtr kfSourceKey, int ktSourceKeyType, [MarshalAs(UnmanagedType.LPStr)] ref StringBuilder sbErrorMessage); 

[STAThread] 
public static void Main(String[] args) 
{ 
    StringBuilder sbPath = new StringBuilder(@"test.dat"); 
    StringBuilder sbErrorMessage = new StringBuilder(); 
    IntPtr ipKeyFile = create_source_key_file_from_path(sbPath); // This works 
    int intIsEncrypted = check_if_encrypted(ipKeyFile, 0, ref sbErrorMessage); // This throws the exception. 
    /* Snip */ 
} 

나는이 잘못되는 같이 DllImport 서명에 ref StringBuilder의 내 사용으로 인해 생각합니다. 대신 무엇을 사용해야합니까?

감사 호출자에게 비 관리 코드 반환 malloc의 호출에 할당되는

답변

1

문자열입니다. 따라서 p/invoke 프레임 워크는이를 할당 해제 할 수 없습니다. 유출을 피하기 위해 분리 기 (deallocator)를 내보내기해야합니다. p/invoke를 사용하여 함수를 호출하려면 수동으로 마샬링을 수행해야합니다.

[DllImport("PPKConverter.exe", CallingConvention = CallingConvention.Cdecl)] 
public extern static int check_if_encrypted(
    IntPtr kfSourceKey, 
    int ktSourceKeyType, 
    out IntPtr sbErrorMessage 
); 

IntPtrMarshal.PtrToStringAnsi에 대한 호출을 문자열로 sbErrorMessage 반환 변환합니다. 그리고 위에서 언급했듯이 누설을 피하기 위해 sbErrorMessage을 다시 할당 해제 자에게 전달해야합니다.

관리되지 않는 기능이 실제로 SetLastError을 호출하는 것으로 의심되기 때문에 SetLastError = true을 삭제했습니다. create_source_key_file_from_path 이후

입력으로 문자열을 다음과 같이 선언한다 수신 : 당신은 당신이 .exe 파일에서 함수에 연결되어야 함을 확인

[DllImport("PPKConverter.exe", CallingConvention = CallingConvention.Cdecl, 
    CharSet=CharSet.Ansi)] 
public extern static IntPtr create_source_key_file_from_path(string strPath); 

있습니까?

관리되지 않는 코드가 malloc으로 문자열을 할당하는 것이 현명한 것인지 궁금합니다. 그건 당신이 deallocator를 강제로 내보낼 수 있습니다. 호출자에게 메모리 할당을 요청하는 것이 좋습니다.또는 비 관리 코드에 할당해야하는 경우 COM 힙과 같은 공유 힙을 사용하십시오. 그렇게하면 C# marshaller가 메모리 할당을 해제 할 수 있습니다.

+0

답변 해 주셔서 감사합니다. 당신이 언급 한 P/Invoke 선언을 변경하고, IntPtr.Zero로 초기화 된 IntPtr에 대한 참조를 전달하는 것을 확인할 수 있습니다. 실제로 EXE에 연결하는 것이 아니라 잘못된 파일 확장명을 가진 DLL을 연결하는 중입니다. 나는 원래 exe를 사용하려고했지만 작동하지 않았고, 아직 이름을 다시 변경하지 않았습니다. malloc없이 알 수없는 크기의 문자열을 되돌려 보내는 방법이 있습니까? 또한 deallocator를 내보내서 무료라는 다른 DLL 함수를 의미합니까? –

+0

매개 변수가 의미 론적으로 벗어 났으므로 지금은 ref에서 out으로 변경했습니다. 그런 다음 초기화 할 필요가 없습니다. 내 실수. –

+0

deallocator를 내 보냅니다. 예, 포인터를 받아들이고 그것을 자유롭게 전달하는 함수를 내 보냅니다. –