locked
IMetaDataImport::OpenScope broken for 4.5 Executable with 32BITPREF+ ?

    Question

  • Here's the scenario:

    I have a .NET executable compiled with VS2012 in the default configuration (AnyCPU with Prefer 32-Bit flag). 

    Here is the output of corflags on the executable.

    Microsoft (R) .NET Framework CorFlags Conversion Tool.  Version  4.0.30319.17929
    Copyright (c) Microsoft Corporation.  All rights reserved.
    
    Version   : v4.0.30319
    CLR Header: 2.5
    PE        : PE32
    CorFlags  : 0x20003
    ILONLY    : 1
    32BITREQ  : 0
    32BITPREF : 1
    Signed    : 0

    I'm trying to use IMetaDataDispenser -> IMetaDataImport -> OpenScope to read the metadata, but OpenScope fails with 0x8007000B - An attempt was made to load a program with an incorrect format (BadImageFormatException). However, setting the 32BITPREF flag to zero allows the OpenScope call to proceed without any problem.

    CComPtr<IMetaDataDispenser> metadata; HRESULT hr = ::CoCreateInstance(CLSID_CorMetaDataDispenser, NULL, CLSCTX_INPROC_SERVER, IID_IMetaDataDispenser, (LPVOID *) &metadata); if (FAILED(hr)) { printf("Failed to create CorMetaDataDispenser. [0x%X]", hr); return hr; } CComPtr<IMetaDataImport> mdImport;
    // This call fails with 0x8007000B BadImageFormatException
    hr = metadata->OpenScope(argv[1], ofRead, IID_IMetaDataImport, (IUnknown **) &mdImport);
    if (FAILED(hr)) {

    printf("Failed to OpenScope. [0x%X]", hr); return hr; }


    Thursday, November 15, 2012 10:21 PM

Answers

  • Hi Peter,

    using CoCreateInstance on CLSID_CorMetaDataDispenser will give you the 3.5 version of the metadata dispenser that doesn't understand the 32BITPREF flag. We are moving away from direct activation using CoCreateInstance since it doesn't work well in side-by-side scenarios introduced in .NET 4.

    Please use the hosting APIs to get to the correct version of the dispenser:

    CComPtr<ICLRMetaHost> pMetaHost;
    CComPtr<ICLRRuntimeInfo> pRuntime;
    CComPtr<IMetaDataDispenserEx> pDisp;
    
    hr = CLRCreateInstance(
        CLSID_CLRMetaHost, 
        IID_ICLRMetaHost, 
        (void **)&pMetaHost);
    hr = pMetaHost->GetRuntime(
        L"v4.0.30319", 
        IID_ICLRRuntimeInfo, 
        (void **)&pRuntime);
    hr = pRuntime->GetInterface(
        CLSID_CorMetaDataDispenser, 
        IID_IMetaDataDispenserEx, 
        (void **)&pDisp);
    
    Hope this helps!

    -Michal

    Wednesday, November 21, 2012 11:24 PM

All replies

  • Hi Peter,

    Welcome to the MSDN Forum.

    When the value is 1, it refer to this:

    /32BITPREF+

    Sets the 32BITPREFERRED flag. The app runs as a 32-bit process even on 64-bit platforms. Set this flag only on EXE files. If the flag is set on a DLL, the DLL fails to load in 64-bit processes, and a BadImageFormatExceptionexception is thrown. An EXE file with this flag can be loaded into a 64-bit process.

    This is exactly as the documentation stated: http://msdn.microsoft.com/en-us/library/ms164699.aspx 

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.


    Friday, November 16, 2012 4:51 PM
    Moderator
  • Thanks for the response Mike.

    I thought the same thing you did when I read that article; however, the test program I supplied acts exactly the same way when the OpenScope call is compiled in 32-bit and 64-bit. Both versions fail in exactly the same way, and both versions succeed when 32BITPREF is not set. The dll is not executing in the process, I'm just reading the metadata from the assembly.

    The assembly that I've generated is definitely AnyCPU. I've verified it several different ways. For what it's worth, when I run CorFlags on other AnyCPU executables on my computer, they all come back with PE: PE32.


    Friday, November 16, 2012 6:53 PM
  • Hi Peter,

    I forgot the another flag: 32bit.

    The values should be something like this: 

    • anycpu: PE = PE32    and  32BIT = 0
    • x86:      PE = PE32    and  32BIT = 1
    • 64-bit:  PE = PE32+  and  32BIT = 0

    When you set the 32BITREF for a anyCPU assembly, this conflict to the AnyCPU platform: run on x86 OS, it run as x86 process, when x64, it  run as x64 process. This flag indicates it only can be run as x86 process whatever the OS platform.

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Tuesday, November 20, 2012 6:48 AM
    Moderator
  • Thanks Mike,

    I do understand the corflags settings. That all makes sense and works exactly as I expected. That is not my problem.

    I am not loading/running the executable. I am trying to read the metadata from the executable using the IMetadataImport::OpenScope call in an unmanaged process. The unmanaged code is compiled as both 32-bit and 64-bit (and has the same result in both cases). Note that this works for every other assembly we've tried. AnyCPU, x64, x86, CLR version 1.0, 1.1, 2.0, 4.0, 4.5, etc....

    There is only one combination that doesn't work under any conditions that I can find. That condition is an assembly compiled under CLR 4.5 as AnyCPU with the prefer-32 bit flag. In that specific scenario, OpenScope fails to read the file as I mentioned in my original post. And it fails whether the unmanaged process that is calling the API is 32-bit or 64-bit. 

    Maybe I should ask the question a different way.....Can you explain under what conditions, OpenScope should succeed when opening a 4.5 assembly compiled with AnyCPU+32BITPREF?

    Best Regards,

    Peter Waldschmidt

    Tuesday, November 20, 2012 3:06 PM
  • Hi Peter,

    I got you. Thank you for clarify this again.

    I am trying to involving some other one into this case.

    Please wait it patiently.

    Thank you.

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Wednesday, November 21, 2012 9:02 AM
    Moderator
  • Hi Peter,

    using CoCreateInstance on CLSID_CorMetaDataDispenser will give you the 3.5 version of the metadata dispenser that doesn't understand the 32BITPREF flag. We are moving away from direct activation using CoCreateInstance since it doesn't work well in side-by-side scenarios introduced in .NET 4.

    Please use the hosting APIs to get to the correct version of the dispenser:

    CComPtr<ICLRMetaHost> pMetaHost;
    CComPtr<ICLRRuntimeInfo> pRuntime;
    CComPtr<IMetaDataDispenserEx> pDisp;
    
    hr = CLRCreateInstance(
        CLSID_CLRMetaHost, 
        IID_ICLRMetaHost, 
        (void **)&pMetaHost);
    hr = pMetaHost->GetRuntime(
        L"v4.0.30319", 
        IID_ICLRRuntimeInfo, 
        (void **)&pRuntime);
    hr = pRuntime->GetInterface(
        CLSID_CorMetaDataDispenser, 
        IID_IMetaDataDispenserEx, 
        (void **)&pDisp);
    
    Hope this helps!

    -Michal

    Wednesday, November 21, 2012 11:24 PM
  • Thanks Michal,

    That definitely works. I appreciate your help.

    Now I just have to figure out all the fallback scenarios...which apis are supported on which machines. :-)

    PW

    Monday, November 26, 2012 9:38 PM
  • Hi, Peter.

    It's always a good idea for your profiler to collect information about the version of the CLR loaded into the app you're profiling.  (Then any offline post-processing component reads this version info logged by your profiler to decide how to behave.)  Your profiler can QI for ICorProfilerInfo3--if it's supported, then the CLR version is >= 4.0, and then you use ICorProfilerInfo3::GetRuntimeInformation to get more detailed info on the version.

    Thanks,
    Dave

    Tuesday, November 27, 2012 2:00 AM
    Owner
  • Thanks David. That's a great idea. 
    Tuesday, November 27, 2012 12:55 PM