Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

C# 관리 코드에서 IMetaDataDispenserEx, IMetaDataImport 관련 인터페이스를 얻는 방법

이에 대해서는 다음의 글에 잘 나와 있습니다.

Introduction to CLR metadata
; https://iobservable.net/blog/2013/05/12/introduction-to-clr-metadata/

전체 소스 코드를 실어보면 다음과 같습니다.

using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

namespace ConsoleApplication1
{
    [ComImport, GuidAttribute("E5CB7A31-7512-11D2-89CE-0080C792E5D8")]
    class CorMetaDataDispenserExClass
    {
    }

    [ComImport, GuidAttribute("31BCFCE2-DAFB-11D2-9F81-00C04F79A0A3"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IMetaDataDispenserEx
    {
        uint DefineScope(ref Guid rclsid, uint dwCreateFlags, ref Guid riid, out object ppIUnk);

        uint OpenScope(string szScope, uint dwOpenFlags, ref Guid riid, out object ppIUnk);

        uint OpenScopeOnMemory(IntPtr pData, uint cbData, uint dwOpenFlags, ref Guid riid, out object ppIUnk);

        uint SetOption(ref Guid optionid, object value);

        uint GetOption(ref Guid optionid, out object pvalue);

        uint OpenScopeOnITypeInfo(ITypeInfo pITI, uint dwOpenFlags, ref Guid riid, out object ppIUnk);

        uint GetCORSystemDirectory(char[] szBuffer, uint cchBuffer, out uint pchBuffer);

        uint FindAssembly(string szAppBase, string szPrivateBin, string szGlobalBin, string szAssemblyName, char[] szName, uint cchName, out uint pcName);

        uint FindAssemblyModule(string szAppBase, string szPrivateBin, string szGlobalBin, string szAssemblyName, string szModuleName, char[] szName, uint cchName, out uint pcName);
    }

    [ComImport, GuidAttribute("7DAC8207-D3AE-4C75-9B67-92801A497D44"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IMetaDataImport
    {
        void CloseEnum(IntPtr hEnum);

        uint GetScopeProps(char[] szName, uint cchName, out uint pchName, ref Guid pmvid);

        uint GetTypeRefProps(uint tr, out uint ptkResolutionScope, char[] szName, uint cchName, out uint pchName);

        uint ResolveTypeRef(uint tr, ref Guid riid, out object ppIScope, out uint ptd);

        uint EnumMethods(ref IntPtr phEnum, uint cl, uint[] rMethods, uint cMax, out uint pcTokens);

        uint GetMethodProps(uint mb, out uint pClass, char[] szMethod, uint cchMethod, out uint pchMethod, out uint pdwAttr, out IntPtr ppvSigBlob, out uint pcbSigBlob, out uint pulCodeRVA, out uint pdwImplFlags);

        uint GetMemberRefProps(uint mr, out uint ptk, char[] szMember, uint cchMember, out uint pchMember, out IntPtr ppvSigBlob, out uint pbSigBlob);

        // for brevity, the rest of the methods are omitted
    }

    class Program
    {
        static void Main(string[] args)
        {
            Guid clsid = new Guid("E5CB7A31-7512-11D2-89CE-0080C792E5D8");

            Type dispenserType = Type.GetTypeFromCLSID(clsid);
            object dispenser = Activator.CreateInstance(dispenserType);

            var dispenserEx = dispenser as IMetaDataDispenserEx;

            Guid metaDataImportGuid = new Guid("7DAC8207-D3AE-4c75-9B67-92801A497D44");
            object rawScope;
            uint hr = dispenserEx.OpenScope("ClassLibrary1.dll", 0, ref metaDataImportGuid, out rawScope);
            var import = rawScope as IMetaDataImport;

            Console.WriteLine(import);
        }
    }
}

그런데, dispenserEx.OpenScope 코드에서 다음과 같은 오류가 발생합니다.

Managed Debugging Assistant 'InvalidVariant' has detected a problem in 'C:\...\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe'.

Additional information: An invalid VARIANT was detected during a conversion from an unmanaged VARIANT to a managed object. Passing invalid VARIANTs to the CLR can cause unexpected exceptions, corruption or data loss.


아마 저 때 당시에는 Managed Debugging Assistant가 해당 변환을 오류로 인식하지 않은 듯 한데... 어쨌든 지금은 명백히 예외를 던지고 있습니다. 릴리스 버전으로 빌드해도 다음과 같이 예외가 발생합니다.

Unhandled Exception: System.Runtime.InteropServices.InvalidOleVariantTypeException: Specified OLE variant is invalid.
   at System.StubHelpers.ObjectMarshaler.ConvertToManaged(IntPtr pSrcVariant)
   at ConsoleApplication1.IMetaDataDispenserEx.OpenScope(String szScope, UInt32 dwOpenFlags, Guid& riid, Object& ppIUnk)
   at ConsoleApplication1.Program.Main(String[] args) in C:\...\ConsoleApplication1\Program.cs:line 68

해결책은 간단합니다. ^^ 다음과 같이 OpenScope 메서드에 마샬링 관련 힌트를 주시면 됩니다.

uint OpenScope(string szScope, uint dwOpenFlags, ref Guid riid, [Out, MarshalAs(UnmanagedType.Interface)] out object ppIUnk);

IMetaDataDispenserEx를 이용해 IMetaDataImport를 구했으면 비관리 코드에서 했던 것처럼 동일한 방식으로 사용해 주시면 됩니다. ^^

string path = typeof(ClassLibrary1.Class1).Assembly.Location;
uint hr = dispenserEx.OpenScope(path, 0, ref metaDataImportGuid, out rawScope);
{
    var import = rawScope as IMetaDataImport;
    {
        uint typeDef = 0; //  (uint)typeof(ClassLibrary1.Class1).MetadataToken;
        import.FindTypeDefByName("ClassLibrary1.Class1", 0, out typeDef);

        var hEnum = IntPtr.Zero;
        var methodTokens = new uint[1024];
        var methodName = new char[1024];
        uint len;

        import.EnumMethods(ref hEnum, typeDef, methodTokens, (uint)methodTokens.Length, out len);
        for (var i = 0; i < len; i++)
        {
            uint tmpTypeDef;
            uint attrs;
            var sig = IntPtr.Zero;
            uint sigLen;
            uint rva;
            uint flags;
            uint methodNameLen;
            hr = import.GetMethodProps(methodTokens[i], out tmpTypeDef, methodName, (uint)methodName.Length, out methodNameLen, out attrs, out sig, out sigLen, out rva, out flags);

            var curName = new string(methodName, 0, (int)methodNameLen - 1);
            Console.WriteLine(curName);
        }

        import.CloseEnum(hEnum);

        Marshal.ReleaseComObject(import);
    }

    Marshal.ReleaseComObject(rawScope);
}

Marshal.ReleaseComObject(dispenser);
Marshal.ReleaseComObject(dispenserEx);

(첨부 파일은 위의 소스 코드를 포함합니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 7/5/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 121  122  123  124  125  126  [127]  128  129  130  131  132  133  134  135  ...
NoWriterDateCnt.TitleFile(s)
2880정성태3/7/201521678개발 환경 구성: 260. C# Code Coverage 도구 - Semantic Designs 소개
2879정성태3/3/201526683개발 환경 구성: 259. Visual Studio 없이 Visual C++ 컴파일하는 방법
2878정성태2/28/201527558.NET Framework: 503. == 연산자보다는 Equals 메서드의 호출이 더 권장됩니다. [3]파일 다운로드1
2877정성태2/28/201521752.NET Framework: 502. 연산자 재정의(operator overloading)와 메서드 재정의(method overriding)의 다른 점 - 가상 함수 호출 여부 [3]파일 다운로드1
2876정성태2/27/201524216VS.NET IDE: 98. IntegraStudio - Visual Studio에서 Java 프로그램 개발
2875정성태2/26/201522828디버깅 기술: 72. Visual Studio 2013에서의 sos.dll 사용 제한
2874정성태2/26/201519576디버깅 기술: 71. windbg + 닷넷 디버깅 (2) - null 체크 패턴
2873정성태2/25/201537054.NET Framework: 501. FtpWebRequest 타입을 이용해 FTP 파일 업로드 [4]파일 다운로드1
2872정성태2/25/201521234디버깅 기술: 70. windbg + 닷넷 디버깅 (1) - 배열 인덱스 사용 패턴
2871정성태2/24/201525204개발 환경 구성: 258. 윈도우 8.1에서 방화벽과 함께 FTP 서버 여는 (하지만, 권장하지 않는) 방법 [1]
2870정성태2/24/201526343개발 환경 구성: 257. 윈도우 8.1에서 방화벽과 함께 FTP 서버 여는 방법
2869정성태2/23/201520313.NET Framework: 500. struct로 정의한 값 형식(Value Type)의 경우 Equals 재정의를 권장합니다.파일 다운로드1
2868정성태2/23/201524823VS.NET IDE: 97. Visual C++ 프로젝트 디버깅 시에 Step-Into(F11) 동작이 원치 않는 함수로 진입하는 것을 막는 방법 [2]
2867정성태2/23/201518527오류 유형: 273. File History - Failed to initiate user data backup (error 80070005)
2866정성태2/23/201520367오류 유형: 272. WAT080 : Failed to locate the Windows Azure SDK. Please make sure the Windows Azure SDK v2.1 is installed.
1868정성태2/20/201517672오류 유형: 271. The type '...' cannot be used as type parameter 'TContext' in the generic type or method 'System.ServiceModel.DomainServices.EntityFramework.LinqToEntitiesDomainService&lt;T&gt;
1866정성태2/20/201518589오류 유형: 270. "aspnet_regiis -i" 실행 시 0x00000006 오류 해결 방법
1865정성태2/20/201519878.NET Framework: 499. 특정 닷넷 프레임워크 버전 이후부터 제공되는 타입을 사용해야 한다면?
1864정성태2/18/201524864.NET Framework: 498. C#으로 간단하게 만들어 본 ASCII Art 프로그램 [2]파일 다운로드1
1862정성태2/18/201528704.NET Framework: 497. .NET Garbage Collection에 대한 정리 [6]
1861정성태2/18/201524054.NET Framework: 496. 마우스 커서가 놓인 지점의 문자열 얻는 방법 [1]파일 다운로드1
1860정성태2/18/201523877.NET Framework: 495. CorElementType의 요소 값 설명파일 다운로드1
1859정성태2/17/201524287Windows: 106. 컴퓨터를 재부팅하면 절전(Power Saver) 전원 모드로 돌아가는 경우
1858정성태2/16/201534266Windows: 105. 자동으로 로그아웃/잠김 화면 상태로 전환된다면? [2]
1857정성태2/16/201522279.NET Framework: 494. 값(struct) 형식의 제네릭(Generic) 타입이 박싱되는 경우의 메타데이터 토큰 값파일 다운로드1
1856정성태2/15/201521257.NET Framework: 493. TypeRef 메타테이블에 등록되는 타입의 조건파일 다운로드1
... 121  122  123  124  125  126  [127]  128  129  130  131  132  133  134  135  ...