Skip to content

Feature: Expose a VS Code Extension API for programmatic decompilation (by file path or base64) #639

Description

@guramrit-dhillon

Summary

I'd like to propose that ILSpy VS Code expose a public Extension API (via vscode.extensions.getExtension(...).exports) that other extensions can invoke programmatically to decompile .NET assemblies — without requiring user interaction or relying on internal command/URI conventions.

Motivation

Currently, if another VS Code extension needs to decompile a .NET assembly, there are only two options:

  1. Bundle their own decompiler backend — duplicating the ILSpy dependency, increasing extension size, and maintaining a separate .NET process lifecycle.
  2. Invoke ILSpy commands (e.g., ilspy.decompileSelectedAssembly) — but these are UI-oriented and don't return structured data suitable for programmatic consumption. (Related: [request] return Node values on ilspy.decompileSelectedAssembly, etc for third-party integration #496)

A public API would make ILSpy the canonical decompilation service in VS Code, similar to how the C# extension exposes language services. Extension authors working on .NET tooling (plugin management, assembly inspection, NuGet browsing, etc.) could depend on icsharpcode.ilspy-vscode and call decompilation directly.

Driving adoption

An extension API would also significantly boost ILSpy VS Code's adoption and install base. Every extension that integrates with the API would effectively recommend ILSpy as a dependency — users who install those extensions would be prompted to install ILSpy VS Code as well. This creates a network effect:

  • Extensions that consume the API list icsharpcode.ilspy-vscode in their extensionDependencies, driving organic installs
  • ILSpy becomes the de facto decompilation layer in the VS Code .NET ecosystem — similar to how extensions depend on the C# extension for language services
  • More installs → more visibility on the Marketplace → more contributors and bug reports → better quality for everyone
  • Extension authors who currently bundle their own ILSpy backend would have a strong incentive to switch, reducing fragmentation and consolidating the community around one well-maintained implementation

Real-world use case

I've started working on Dataverse Tools for VS Code — a suite of extensions for Dynamics 365 / Power Platform plugin development. One of the extensions lets users browse decompiled plugin assemblies fetched from Dataverse (cloud). Today we ship our own ILSpy-based .NET backend (~30 MB per platform) just to decompile assemblies. If ILSpy VS Code offered a programmatic API, we could:

  • Drop our custom backend entirely
  • Let users benefit from ILSpy VS Code updates automatically
  • Reduce our extension bundle size by ~120 MB (4 platforms)
  • Avoid duplicating backend process management, idle timeouts, etc.

Proposed API

// Consumer obtains via:
// const ilspy = vscode.extensions.getExtension('icsharpcode.ilspy-vscode')?.exports;

interface ILSpyApi {
  /**
   * Load an assembly from a file path on disk.
   * Returns a handle for subsequent operations.
   */
  loadAssembly(filePath: string): Promise<AssemblyHandle>;

  /**
   * Load an assembly from base64-encoded bytes.
   * Useful when the DLL isn't on disk (e.g., fetched from a remote server/API).
   */
  loadAssemblyFromBase64(name: string, base64Content: string): Promise<AssemblyHandle>;

  /** API version for forward compatibility */
  readonly version: number;
}

interface AssemblyHandle {
  /** List all namespaces in the assembly */
  listNamespaces(): Promise<string[]>;

  /** List types within a namespace */
  listTypes(namespace: string): Promise<TypeInfo[]>;

  /** Decompile a single type to C# source */
  decompileType(typeFullName: string): Promise<string>;

  /** Dispose / unload this assembly from the backend */
  dispose(): Promise<void>;
}

interface TypeInfo {
  fullName: string;
  name: string;
  kind: 'class' | 'interface' | 'struct' | 'enum' | 'delegate';
}

Why both file path and base64?

Input Use case
File path Standard scenario — user has a .dll on disk, NuGet cache, build output
Base64 Assembly bytes come from a remote API (Dataverse, Azure DevOps artifacts, REST endpoint) and never touch disk

Relationship to #496

Issue #496 requests Node values from existing commands for URI construction. This proposal is complementary — it asks for a headless, data-oriented API that doesn't depend on the tree view or editor UI. Both could coexist: the command-level improvements from #496 help extensions that want to drive ILSpy's UI, while this API helps extensions that want decompiled source code as data.

Implementation notes

The backend already supports these operations internally (the LSP server handles load/list/decompile). The main work would be:

  1. Exposing an activate() return value with the API object
  2. Delegating API calls to the existing backend LSP client
  3. Managing assembly lifecycle (load/unload) per consumer

I'd be happy to contribute a PR if the maintainers are open to the approach.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions