2026/8/16
Qiitaなんかに書いたら怒られそうな技術ブログ。
TL;DR
コードを生成する仕組み自体はある。
でもこのクラスが生成するコードは古すぎる。VSがそのまま使ってるとは思えない。じゃあVSはどうやってコードを生成してるの?
調査
やることは簡単で、dnsPyを使ってVS内部のDLLを逆アセンブル(?)して実装を読んでいく。
名前であたりを付けたり虱潰しに探したりすると、、Microsoft.VisualStudio.Desgin.dll に MergedCodeDomPrivider というのが見つかるので、こいつの実装を追っていく。
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として登録されていることが理解る。