Ответ 1
Вот быстрый и грязный способ делать то, что вы хотите. Он основан на одном из приведенных выше замечаний, который указывает на SebbyLive. Это просто доказательство концепции, я бы не пытался использовать ее в производстве.
Основная идея заключается в том, что вы меняете компилятор проекта, который хотите изменить. И этот измененный компилятор выполнит инъекцию кода. Поэтому вам нужно будет написать новый компилятор (AopCompiler.exe) и установить его как инструмент построения в вашем проекте.
Установка AopCompiler.exe в качестве инструмента сборки легко, в файле проекта вам нужно добавить следующие две строки:
<CscToolPath>$(SolutionDir)AopCompiler\bin\Debug</CscToolPath>
<CscToolExe>AopCompiler.exe</CscToolExe>
AopCompiler должен быть простым консольным приложением. Это также делает модификацию кода и компиляцию. Если вы не хотите изменять исходный код, просто создайте его, тогда самый простой способ - вызвать csc.exe самостоятельно:
static void Main(string[] args)
{
var p = Process.Start(@"C:\Program Files (x86)\MSBuild\14.0\Bin\csc.exe",
string.Join(" ", args));
p.WaitForExit();
}
Итак, если вы установите это до сих пор, у вас будет нормальный процесс сборки, без апробации.
В этот момент, если вы посмотрите, что находится в args
, вы увидите, что есть путь к файлу .RSP, который содержит все параметры командной строки для csc.exe. Естественно, эти параметры содержат все имена файлов .CS. Таким образом, вы можете проанализировать этот .RSP файл и найти все .CS файлы, которые являются частью компиляции.
С помощью файлов С#, переписывание может быть выполнено с помощью Roslyn. В CSharpSyntaxRewriter
есть много руководств, например здесь и здесь. Вам нужно будет написать свой собственный CSharpSyntaxRewriter
, который проверяет данный атрибут, а затем добавляет запись в начало найденных методов. Добавление журнала в конец каждого метода немного сложнее, потому что может быть несколько точек выхода. Чтобы найти их, вы можете использовать анализ потока управления. Встроенный анализ потока управления Roslyn может дать вам именно то, что вам нужно, свойство ExitPoints
содержит набор операторов внутри региона, которые переходят в местоположения за пределами область.
Чтобы получить семантическую модель (а затем выполнить анализ CFG), вы можете сделать что-то вроде этого:
public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
{
var semanticModel = _compilation.GetSemanticModel(node.SyntaxTree);
// semanticModel.AnalyzeControlFlow(node.Block)
return node;
}
Наконец, чтобы обработать каждый из входных файлов, ваш AopCompiler, вам просто нужно вызвать метод rewriter Visit
в корне дерева. Это создаст модифицированное дерево, которое вы можете записать в файл. (Или вы можете изменить исходный файл, либо записать результат на новый, и соответственно изменить файл .RSP.)
Извините за отсутствие полного рабочего решения, но я надеюсь, этого достаточно, чтобы вы начали.