[C#] 모든 CPU를 대상으로하는 C # 프로젝트에서이 코드가 System.AccessViolationException을 던지는 이유는 무엇입니까?


Answers

Question

내 IDL 프로젝트에이 IDL이 있습니다.

[
    object,
    uuid(61B0BFF7-E9DF-4D7E-AFE6-49CC67245257),
    dual,
    nonextensible,
    pointer_default(unique)
]
interface ICrappyCOMService : IDispatch {
    typedef
        [
            uuid(C65F8DE6-EDEF-479C-BD3B-17EC3F9E4A3E),
            version(1.0)
        ]
    struct CrapStructure {
        INT ErrorCode;
        BSTR ErrorMessage;
    } CrapStructure;
    [id(1)] HRESULT TestCrap([in] INT errorCode, [in] BSTR errorMessage, [in, out] CrapStructure *crapStructure);
};
[
    uuid(763B8CA0-16DD-48C8-BB31-3ECD9B9DE441),
    version(1.0),
]
library CrappyCOMLib
{
    importlib("stdole2.tlb");
    [
        uuid(F7375DA4-2C1E-400D-88F3-FF816BB21177)      
    ]
    coclass CrappyCOMService
    {
        [default] interface ICrappyCOMService;
    };
};

이것은 C ++에서 구현 한 것입니다.

STDMETHODIMP CCrappyCOMService::InterfaceSupportsErrorInfo(REFIID riid)
{
    static const IID* const arr[] = {
        &IID_ICrappyCOMService
    };
    for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) {
        if (InlineIsEqualGUID(*arr[i], riid))
            return S_OK;
    }
    return S_FALSE;
}

STDMETHODIMP CCrappyCOMService::TestCrap(INT errorCode, BSTR errorMessage, CrapStructure *crapStructure) {
    memset(crapStructure, 0, sizeof(CrapStructure));
    crapStructure->ErrorCode = errorCode;
    crapStructure->ErrorMessage = errorMessage;
    CComPtr<ICreateErrorInfo> x;
    ICreateErrorInfo* pCreateErrorInfo;
    CreateErrorInfo(&pCreateErrorInfo);
    pCreateErrorInfo->AddRef();
    pCreateErrorInfo->SetDescription(errorMessage);
    pCreateErrorInfo->SetGUID(IID_ICrappyCOMService);
    pCreateErrorInfo->SetSource(L"Component.TestCrap");
    IErrorInfo* pErrorInfo;
    pCreateErrorInfo->QueryInterface(IID_IErrorInfo, (void**)&pErrorInfo);
    pErrorInfo->AddRef();
    SetErrorInfo(0, pErrorInfo);
    pErrorInfo->Release();
    pCreateErrorInfo->Release();
    printf("Going to return %d...\n", errorCode);
    return errorCode;
}

나는 이것을 C #에서 이렇게 부른다.

static void Main(string[] args)
{
    var service = new CrappyCOMService();
    var crapStructure = new CrapStructure();
    try
    {
        service.TestCrap(-1, "This is bananas.", ref crapStructure);
    }
    catch (COMException exception)
    {
        Console.WriteLine(exception.ErrorCode);
        Console.WriteLine(exception.Message);
    }
    Console.WriteLine(crapStructure.ErrorCode);
    Console.WriteLine(crapStructure.ErrorMessage);
}

x64를 대상으로 C # 프로젝트에서 코드를 실행하면 모든 것이 잘 작동합니다. 문제는 C #의 모든 CPU를 대상으로하는 프로젝트에서 TestCrap 메서드를 호출 할 때 System.AccessViolationException 을 throw한다는 것 입니다. 왜 그런가요? 나는 그것이 System.AccessViolationException 을 throw 할 때 -1 을 콘솔 창 에 반환하도록 Going을 인쇄하는지 확인할 수 있습니다.

편집 : 나는 복제 단계를 좁혔다. 죄송합니다.이 코드를 추가하는 것을 잊어 버렸습니다. 이것은 x64 빌드 (x64를 대상으로하는 ATL 프로젝트와 모든 CPU를 대상으로하는 C # 테스트 프로젝트)를 사용하여 내 코드를 컴파일 한 다음 모든 CPU 빌드 (ATL 프로젝트 모든 CPU를 대상으로하는 Win32 및 C # 테스트 프로젝트를 대상으로합니다.

편집 : 나는 조금 더 아래로 오류를 좁혔습니다. 무엇보다도, 모든 CPU C # 프로젝트가 항상 내 COM 개체의 x64 버전을 사용하는 것처럼 보이므로 x64 시스템의 모든 CPU가 실제로 갈 예정이므로 코드를 전파하려면 먼저 내 ATL 프로젝트의 버전을 컴파일해야합니다. x64 컨텍스트에서 실행됩니다. 또한, 그것은 라인처럼 보입니다, crapStructure->ErrorMessage = errorMessage; System.AccessViolationException 이 발생합니다. 그것은 라인을 넘어 COM 세계에서 코드를 실행하지만 C # 세계로 돌아와 예외를 던집니다.

편집 : C ++에서 printf("%d\n", sizeof(CrapStructure)); 결과는 16 입니다. C #에서 Console.WriteLine(Marshal.SizeOf(typeof(CrapStructure))); 또한 결과는 16 입니다. 하지만 아아, 그것을 좀 더 읽고,이 한스 '대답에 의해 언급 된 쓸모없는 검사가 있습니다 : 구조체에 의해 소비 된 바이트 수를 어떻게 확인합니까?

편집 : 나는 tlbimp CrappyCOM.dll /out:CrappyCOMx86Managed.dll 하고 tlbimp CrappyCOM.dll /out:CrappyCOMx86Managed.dll 을 사용하여 모든 CPU 대상 C # 프로젝트에서 시도했지만 System.AccessViolationException 다시 던졌습니다. 시스템에 등록 된 x64 COM 개체로 다시 이동했기 때문에 이것이라고 생각했습니다. 따라서 regsvr32 를 사용하여 x64 COM 개체의 등록을 취소했습니다. 코드를 다시 실행하고 x86 COM 개체를 등록하면 CLSID가 {F7375DA4-2C1E-400D-88F3-FF816BB21177} 인 구성 요소의 COM 클래스 팩터 리 검색이 다음 오류로 인해 실패했습니다 : 80040154 클래스가 등록되지 않았습니다 (HRESULT의 예외 : 0x80040154 (REGDB_E_CLASSNOTREG)). 내가 tlbimp 생성 한 스텁에서 x86 COM 개체를 대상으로 삼는 유일한 방법은 내 C # 프로젝트의 빌드 대상을 x86으로 수정 한 다음이 코드가 내 x86 COM 개체를 테스트하기 위해 작동하므로이 종류의 것 같습니다. 모든 CPU가 정확한 x86 구조의 COM 객체 또는 올바른 구조체 크기를 가진 x64 COM 객체를 대상으로 삼고 싶기 때문에 저의 논점과 같습니다. COM은 C #의 재앙입니다.




Links