2026/8/16

Qiitaなんかに書いたら怒られそうな技術ブログ。

TL;DR

コードを生成する仕組み自体はある。

でもこのクラスが生成するコードは古すぎる。VSがそのまま使ってるとは思えない。じゃあVSはどうやってコードを生成してるの?

調査

やることは簡単で、dnsPyを使ってVS内部のDLLを逆アセンブル(?)して実装を読んでいく。

名前であたりを付けたり虱潰しに探したりすると、、Microsoft.VisualStudio.Desgin.dllMergedCodeDomPrivider というのが見つかるので、こいつの実装を追っていく。

public override ICodeGenerator CreateGenerator()
{
    if (this._generator == null)
    {
        this._generator = new MergedCodeDomGenerator(this);
    }
    return this._generator;
}

MergedCodeDomGeneratorらしい。

public void GenerateCodeFromCompileUnit(CodeCompileUnit compileUnit, TextWriter writer, CodeGeneratorOptions codeGenOptions)
{
    if (this._mergedCodeDomProvider.CodeDomProviders.Count == 1)
    {
        this.PrimaryCodeGenerator.GenerateCodeFromCompileUnit(compileUnit, writer, codeGenOptions);
        return;
    }
    foreach (object obj in compileUnit.Namespaces)
    {
        System.CodeDom.CodeNamespace nameSpace = (System.CodeDom.CodeNamespace)obj;
        this.GenerateCodeFromNamespace(nameSpace, writer, codeGenOptions);
    }
}

private ICodeGenerator PrimaryCodeGenerator
{
    get
    {
        return ((CodeDomProvider)this._mergedCodeDomProvider.CodeDomProviders[0]).CreateGenerator();
    }
}

Mergedというのは要は多数のProviderをマージして管理するクラスのよう。というわけでMergedCodeDomGeneratorに戻ると AddCodeDomProvider というメソッドを発見。こいつを読んでいる箇所を探していく。internalだからアセンブリ内にあるだろう。

あった。ちょっと長いけど全文引用。

IVSMDCodeDomProvider IVSMDCodeDomCreator.CreateCodeDomProvider(IVsHierarchy vsProject, int itemid)
{
    ProjectItem projectItem = null;
    if (itemid != -1 && pHier != null)
    {
        vsProject.GetProperty((uint)itemid, VSHPROPID_ExtObject, out projectItem);
        if (projectItem == null)
        {
            throw new NotSupportedException("DesignSurfaceManager_NoFileCodeModel");
        }
    }

    if (vsProject.GetGuidProperty(VSITEMID.Root, VSHPROPID_TypeGuid, out Guid typeGuid))
    {
        throw new NotSupportedException("DesignSurfaceManager_NoProjectType");
    }
    CodeDomProvider codeDomProvider = null;
    if (vsProject != null && vsProject.GetItemContext(VSITEMID.Root, out IServiceProvider serviceProvider) == 0 && serviceProvider != null)
    {
        ServiceProvider serviceProvider2 = new ServiceProvider(serviceProvider);
        codeDomProvider = (CodeDomProvider)serviceProvider2.GetService(typeof(CodeDomProvider));
    }
    Guid g = new Guid("{E24C65DC-7377-472b-9ABA-BC803B73C61A}");
    if (codeDomProvider == null)
    {
        Guid g2 = new Guid("{FAE04EC0-301F-11d3-BF4B-00C04F79EFBC}");
        Guid g3 = new Guid("{F184B08F-C81C-45f6-A57F-5ABD9991F28F}");
        if (typeGuid.Equals(g2))
        {
            codeDomProvider = new CSharpCodeProvider();
        }
        else if (typeGuid.Equals(g3))
        {
            codeDomProvider = new VBCodeProvider();
        }
        else
        {
            if (typeGuid.Equals(g))
            {
                try
                {
                    string text;
                    if (projectItem.Kind.Equals("{6BB5F8EF-4483-11D3-8BCF-00C04F8EC28C}"))
                    {
                        text = projectItem.Properties.Item("CodeLanguage").Value.ToString();
                    }
                    else
                    {
                        string extension = projectItem.Properties.Item("Extension").Value.ToString();
                        text = CodeDomProvider.GetLanguageFromExtension(extension);
                    }
                    if (text == null)
                    {
                        throw new NotSupportedException("DesignSurfaceManager_NoProjectType");
                    }
                    codeDomProvider = CodeDomProvider.CreateProvider(text);
                    goto IL_178;
                }
                catch
                {
                    goto IL_178;
                }
            }
            codeDomProvider = this.CreateProviderFromRegistry(typeGuid);
        }
    }
    IL_178:
    if (codeDomProvider == null)
    {
        throw new NotSupportedException("DesignSurfaceManager_NoLanguageSupport");
    }
    string moniker = null;
    object obj2;
    if (vsProject.GetProperty((uint)itemid, -2102, out obj2) == 0 || vsProject.GetProperty(VSITEMID.Root, -2102, out obj2) == 0)
    {
        moniker = (obj2 as string);
    }
    MergedCodeDomProvider mergedCodeDomProvider = new MergedCodeDomProvider(this._provider, projectItem, moniker);
    mergedCodeDomProvider.AddCodeDomProvider(new VSCodeDomProvider(this._provider, projectItem, codeDomProvider, true));
    if (!typeGuid.Equals(g))
    {
        ProjectItems projectItems = projectItem.ProjectItems;
        if (projectItems != null)
        {
            string value = projectItem.Properties.Item("Extension").Value.ToString();
            foreach (object obj3 in projectItems)
            {
                ProjectItem projectItem2 = (ProjectItem)obj3;
                prjBuildAction prjBuildAction = (prjBuildAction)projectItem2.Properties.Item("BuildAction").Value;
                string text2 = projectItem2.Properties.Item("Extension").Value.ToString();
                if (prjBuildAction == prjBuildAction.prjBuildActionCompile && text2.Equals(value))
                {
                    mergedCodeDomProvider.AddCodeDomProvider(new VSCodeDomProvider(this._provider, projectItem2, codeDomProvider, false));
                }
            }
        }
    }
    return new VSMDCodeDomProvider(mergedCodeDomProvider);
}

何をやってるんだかさっぱりだが、大事なのは CodeDomProvider で、結局注入された vsProject から IVSMDCodeDomProvider 越しに CodeDomProvider を吸い出しているであろうことがわかる。インターフェース越しだとdnsPyの機能では追いにくい……。
仕方がないので DesignerActivationService を使ってるところを探すと、VSDesignerActivationPackage でServiceとして登録されていることが理解る。

  • 日記