201 lines
8.2 KiB
C#
201 lines
8.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Threading;
|
|
using dnSpy.Contracts.Decompiler;
|
|
using dnSpy.Contracts.Documents.Tabs.DocViewer;
|
|
using dnSpy.Contracts.Documents.TreeView;
|
|
using dnSpy.Contracts.Images;
|
|
using dnSpy.Contracts.Text;
|
|
using dnSpy.Contracts.TreeView;
|
|
|
|
// This file contains classes that create new child nodes of IAssemblyFileNode and IModuleFileNode
|
|
|
|
namespace Example2.Extension {
|
|
// This class adds a new child node to all assembly nodes
|
|
[ExportTreeNodeDataProvider(Guid = DocumentTreeViewConstants.ASSEMBLY_NODE_GUID)]
|
|
sealed class AssemblyTreeNodeDataProvider : ITreeNodeDataProvider {
|
|
public IEnumerable<TreeNodeData> Create(TreeNodeDataProviderContext context) {
|
|
yield return new AssemblyChildNode();
|
|
}
|
|
}
|
|
|
|
// This class adds a new child node to all module nodes
|
|
[ExportTreeNodeDataProvider(Guid = DocumentTreeViewConstants.MODULE_NODE_GUID)]
|
|
sealed class ModuleTreeNodeDataProvider : ITreeNodeDataProvider {
|
|
public IEnumerable<TreeNodeData> Create(TreeNodeDataProviderContext context) {
|
|
yield return new ModuleChildNode();
|
|
}
|
|
}
|
|
|
|
sealed class AssemblyChildNode : DocumentTreeNodeData { // All file tree nodes should implement DocumentTreeNodeData or derive from DocumentTreeNodeData
|
|
//TODO: Use your own guid
|
|
public static readonly Guid THE_GUID = new Guid("6CF91674-16CE-44EA-B9E8-80B68C327D30");
|
|
|
|
public override Guid Guid => THE_GUID;
|
|
public override NodePathName NodePathName => new NodePathName(Guid);
|
|
|
|
// The image must be in an Images folder (in the resources) and have a .png extension
|
|
protected override ImageReference GetIcon(IDotNetImageService dnImgMgr) => DsImages.EntryPoint;
|
|
|
|
protected override void WriteCore(ITextColorWriter output, IDecompiler decompiler, DocumentNodeWriteOptions options) =>
|
|
output.Write(BoxedTextColor.Text, "Assembly Child");
|
|
|
|
// If you don't want the node to be appended to the children, override this
|
|
public override ITreeNodeGroup? TreeNodeGroup => TreeNodeGroupImpl.Instance;
|
|
|
|
sealed class TreeNodeGroupImpl : ITreeNodeGroup {
|
|
public static readonly TreeNodeGroupImpl Instance = new TreeNodeGroupImpl(1);
|
|
|
|
public TreeNodeGroupImpl(double order) => Order = order;
|
|
|
|
public double Order { get; }
|
|
|
|
public int Compare([AllowNull] TreeNodeData x, [AllowNull] TreeNodeData y) {
|
|
if (x == y)
|
|
return 0;
|
|
var a = x as AssemblyChildNode;
|
|
var b = y as AssemblyChildNode;
|
|
if (a is null) return -1;
|
|
if (b is null) return 1;
|
|
// More checks can be added here...
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// This class can decompile its own output and implements IDecompileSelf
|
|
sealed class ModuleChildNode : DocumentTreeNodeData, IDecompileSelf { // All file tree nodes should implement DocumentTreeNodeData or derive from DocumentTreeNodeData
|
|
//TODO: Use your own guid
|
|
public static readonly Guid THE_GUID = new Guid("C8892F6C-6A49-4537-AAA0-D0DEF1E87277");
|
|
|
|
public override Guid Guid => THE_GUID;
|
|
public override NodePathName NodePathName => new NodePathName(Guid);
|
|
|
|
// Initialize() is called after the constructor has been called, and after the TreeNode prop
|
|
// has been initialized
|
|
public override void Initialize() =>
|
|
// Set LazyLoading if creating the children could take a while
|
|
TreeNode.LazyLoading = true;
|
|
|
|
// If TreeNode.LazyLoading is false, CreateChildren() is called after Initialize(), else it's
|
|
// called when this node gets expanded.
|
|
public override IEnumerable<TreeNodeData> CreateChildren() {
|
|
// Add some children in random order. They will be sorted because SomeMessageNode
|
|
// overrides the TreeNodeGroup prop.
|
|
yield return new SomeMessageNode("ZZZZZZZZZZZZZ");
|
|
yield return new SomeMessageNode("AAAAaaaaaAAAAAAA");
|
|
yield return new SomeMessageNode("SAY");
|
|
}
|
|
|
|
// The image must be in an Images folder (in the resources) and have a .png extension
|
|
protected override ImageReference GetIcon(IDotNetImageService dnImgMgr) => DsImages.String;
|
|
|
|
protected override void WriteCore(ITextColorWriter output, IDecompiler decompiler, DocumentNodeWriteOptions options) => output.Write(BoxedTextColor.Text, "Module Child");
|
|
|
|
// Gets called by dnSpy if there's only one node to decompile. This method gets called in a
|
|
// worker thread. The output is also cached unless you call IDocumentViewerOutput.DisableCaching().
|
|
public bool Decompile(IDecompileNodeContext context) {
|
|
// Pretend we actually do something...
|
|
Thread.Sleep(2000);
|
|
|
|
// If you don't want the output to be cached, call DisableCaching()
|
|
bool cacheOutput = true;
|
|
if (!cacheOutput)
|
|
(context.Output as IDocumentViewerOutput)?.DisableCaching();
|
|
|
|
// Create the output and a few references that other code in this extension will use, eg.
|
|
// to show a tooltip when hovering over the reference.
|
|
context.ContentTypeString = ContentTypes.CSharp;
|
|
context.Output.WriteLine("// Initialize it to the secret key", BoxedTextColor.Comment);
|
|
context.Output.Write("int", new StringInfoReference("This is a reference added by the code"), DecompilerReferenceFlags.None, BoxedTextColor.Keyword);
|
|
context.Output.Write(" ", BoxedTextColor.Text);
|
|
context.Output.Write("secret", new StringInfoReference("The real secret is actually 42 not 1234"), DecompilerReferenceFlags.None, BoxedTextColor.Local);
|
|
context.Output.Write(" ", BoxedTextColor.Text);
|
|
context.Output.Write("=", BoxedTextColor.Operator);
|
|
context.Output.Write(" ", BoxedTextColor.Text);
|
|
context.Output.Write("1234", BoxedTextColor.Number);
|
|
context.Output.Write(";", BoxedTextColor.Punctuation);
|
|
context.Output.WriteLine();
|
|
|
|
// We decompiled ourselves so return true
|
|
return true;
|
|
}
|
|
|
|
// If you don't want the node to be appended to the children, override this
|
|
public override ITreeNodeGroup? TreeNodeGroup => TreeNodeGroupImpl.Instance;
|
|
|
|
sealed class TreeNodeGroupImpl : ITreeNodeGroup {
|
|
// Make sure the order is unique. 0 is already used by the PE node, so use 1
|
|
public static readonly TreeNodeGroupImpl Instance = new TreeNodeGroupImpl(1);
|
|
|
|
public TreeNodeGroupImpl(double order) => Order = order;
|
|
|
|
public double Order { get; }
|
|
|
|
public int Compare([AllowNull] TreeNodeData x, [AllowNull] TreeNodeData y) {
|
|
if (x == y)
|
|
return 0;
|
|
var a = x as ModuleChildNode;
|
|
var b = y as ModuleChildNode;
|
|
if (a is null) return -1;
|
|
if (b is null) return 1;
|
|
// More checks can be added here...
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
sealed class SomeMessageNode : DocumentTreeNodeData {
|
|
//TODO: Use your own guid
|
|
public static readonly Guid THE_GUID = new Guid("1751CD40-68CE-4F8A-84AF-99371B6FD843");
|
|
|
|
public string Message { get; }
|
|
|
|
public SomeMessageNode(string msg) => Message = msg;
|
|
|
|
public override Guid Guid => THE_GUID;
|
|
public override NodePathName NodePathName => new NodePathName(THE_GUID, Message);
|
|
protected override ImageReference GetIcon(IDotNetImageService dnImgMgr) => DsImages.String;
|
|
|
|
protected override void WriteCore(ITextColorWriter output, IDecompiler decompiler, DocumentNodeWriteOptions options) =>
|
|
output.Write(BoxedTextColor.Comment, Message);
|
|
|
|
public override ITreeNodeGroup? TreeNodeGroup => TreeNodeGroupImpl.Instance;
|
|
|
|
sealed class TreeNodeGroupImpl : ITreeNodeGroup {
|
|
public static readonly TreeNodeGroupImpl Instance = new TreeNodeGroupImpl(0);
|
|
|
|
public TreeNodeGroupImpl(double order) => Order = order;
|
|
|
|
public double Order { get; }
|
|
|
|
public int Compare([AllowNull] TreeNodeData x, [AllowNull] TreeNodeData y) {
|
|
if (x == y)
|
|
return 0;
|
|
var a = x as SomeMessageNode;
|
|
var b = y as SomeMessageNode;
|
|
if (a is null) return -1;
|
|
if (b is null) return 1;
|
|
return StringComparer.OrdinalIgnoreCase.Compare(a.Message, b.Message);
|
|
}
|
|
}
|
|
}
|
|
|
|
// SomeMessageNode doesn't implement IDecompileSelf, so we add a "decompiler" that can "decompile"
|
|
// those nodes.
|
|
[ExportDecompileNode]
|
|
sealed class SomeMessageNodeDecompiler : IDecompileNode {
|
|
public bool Decompile(IDecompileNodeContext context, DocumentTreeNodeData node) {
|
|
var msgNode = node as SomeMessageNode;
|
|
if (msgNode is null)
|
|
return false;
|
|
|
|
context.Decompiler.WriteCommentLine(context.Output, "The secret message has been decrypted.");
|
|
context.Decompiler.WriteCommentLine(context.Output, $"The message is: {msgNode.Message}");
|
|
context.ContentTypeString = ContentTypes.PlainText;
|
|
return true;
|
|
}
|
|
}
|
|
}
|