How to Build a VSCode Extension for Barcode and QR Code Development

Dynamsoft Barcode Reader, as an enterprise-class barcode and QR code SDK, supports many platforms (Windows, Linux, macOS, Android, iOS, Raspberry Pi OS, and Web) and programming languages (Python, Java, Swift, C#, Cpp, and JavaScript). There are many sample projects distributed in Dynamsoft GitHub repositories and official website. To help developers get started with Dynamsoft Barcode Reader quickly, we created a VSCode extension that features project generation for different programming languages and frameworks, code snippet insertion, and template download.

What you’ll build: A Visual Studio Code extension that generates barcode/QR code projects in multiple languages, inserts SDK code snippets, and downloads pre-configured parameter templates — all from the VS Code Command Palette.

Key Takeaways

  • A single VSCode extension can scaffold barcode-scanning projects for .NET, C++, Python, Android, iOS, and Web from ready-made templates.
  • Dynamsoft Barcode Reader code snippets can be registered per language so developers type a short prefix and get a working SDK call instantly.
  • Parameter template files (Speed, Balanced, Coverage) are downloadable directly inside VS Code using the built-in https module with progress notification.
  • The extension architecture — commands in package.json, handlers in extension.ts, resources under res/ — follows the standard VS Code extension pattern and is easy to fork.

Common Developer Questions

  • How do I create a VSCode extension that generates barcode scanning projects?
  • How do I add Dynamsoft Barcode Reader code snippets to VS Code?
  • How do I download barcode parameter templates from a VSCode extension?

Prerequisites

Install the Dynamsoft Barcode Reader VSCode Extension

https://marketplace.visualstudio.com/items?itemName=Dynamsoft.dynamsoft-barcode-reader

Create Your First VSCode Extension

Let’s follow the official tutorial to create a basic VSCode extension project:

  1. Install Yeoman and VS Code Extension Generator:

     npm install -g yo generator-code
    
  2. Run the following command to scaffold a TypeScript or JavaScript project:

     yo code
    
  3. Press F5 to debug the project and then press F1 to show the Command Palette. We can enter Hello World command to see the result.

Build the Barcode Reader Extension Features

The extension will support:

  • Project generation

    Create console, desktop GUI, web and mobile apps.

  • Code snippet insertion

    Add code snippets into corresponding programing language files.

  • File download

    Download pre-set parameter template files for barcode and QR code recognition.

Generate a Barcode Reader Project

Step 1: Define Commands for the Command Palette

In package.json file, the commands for project generation are defined as follows:

"activationEvents": [
    "onCommand:dbr.dotnet",
    "onCommand:dbr.cpp",
    "onCommand:dbr.web",
    "onCommand:dbr.python",
    "onCommand:dbr.android",
    "onCommand:dbr.ios"
  ],
"contributes": {
    "commands": [
      {
        "command": "dbr.dotnet",
        "title": "DBR: Create .NET Project"
      },
      {
        "command": "dbr.cpp",
        "title": "DBR: Create C/C++ Project"
      },
      {
        "command": "dbr.web",
        "title": "DBR: Create Web Project"
      },
      {
        "command": "dbr.python",
        "title": "DBR: Create Python Project"
      },
      {
        "command": "dbr.android",
        "title": "DBR: Create Android Project"
      },
      {
        "command": "dbr.ios",
        "title": "DBR: Create iOS Project"
      }
    ],
  },

To make the commands work, we need to add the following TypeScript code to extension.ts file:

export function activate(context: vscode.ExtensionContext) {

	context.subscriptions.push(...[
		vscode.commands.registerCommand('dbr.dotnet', async () => {
		}),
		vscode.commands.registerCommand('dbr.cpp', async () => {
		}),
		vscode.commands.registerCommand('dbr.web', async () => {
		}),
		vscode.commands.registerCommand('dbr.python', async () => {
		}),
		vscode.commands.registerCommand('dbr.android', async () => {
		}),
		vscode.commands.registerCommand('dbr.ios', async () => {
		})
	]);
}

vscode extension for Dynamsoft Barcode Reader

Step 2: Prepare the Project Templates

We put some project templates under the res folder and store their physical paths in TypeScript:

private samples = {
        "python": path.join(__dirname, '../res/python/'),
        "dotnet": path.join(__dirname, '../res/dotnet/'),
        "cpp": path.join(__dirname, '../res/cpp/'),
        "web": path.join(__dirname, '../res/web/'),
        "android": path.join(__dirname, '../res/android/'),
        "ios": path.join(__dirname, '../res/ios/')
    };

According to the input command, we can return the corresponding project path:

enum Type {
    CONSOLE = 'Console App',
    WINFORMS = 'Windows Forms App',
    FILE = 'File Reader',
    CAMERA = 'Camera Scanner',
    GUI = 'GUI App',
}

enum FolderName {
    FOLDER_NAME_CONSOLE = 'console',
    FOLDER_NAME_WINFORMS = 'winforms',
    FOLDER_NAME_FILE = 'file',
    FOLDER_NAME_CAMERA = 'camera',
    FOLDER_NAME_GUI = 'gui',
}

switch (option) {
    case "dotnet":
        root = this.samples.dotnet;
        projectType = await this.getProjectType([Type.CONSOLE, Type.WINFORMS]);
        break;
    case "cpp":
        root = this.samples.cpp;
        projectType = await this.getProjectType([Type.CONSOLE]);
        break;
    case "web":
        root = this.samples.web;
        projectType = await this.getProjectType([Type.FILE, Type.CAMERA]);
        break;
    case "python":
        root = this.samples.python;
        projectType = await this.getProjectType([Type.CONSOLE, Type.GUI]);
        break;
    case "android":
        root = this.samples.android;
        projectType = await this.getProjectType([Type.CAMERA]);
        break;
    case "ios":
        root = this.samples.ios;
        projectType = await this.getProjectType([Type.CAMERA]);
        break;
}
if (projectType === '') { return; }

switch (projectType) {
    case Type.CONSOLE:
        src = path.join(root, FolderName.FOLDER_NAME_CONSOLE);
        break;
    case Type.WINFORMS:
        src = path.join(root, FolderName.FOLDER_NAME_WINFORMS);
        break;
    case Type.FILE:
        src = path.join(root, FolderName.FOLDER_NAME_FILE);
        break;
    case Type.CAMERA:
        src = path.join(root, FolderName.FOLDER_NAME_CAMERA);
        break;
    case Type.GUI:
        src = path.join(root, FolderName.FOLDER_NAME_GUI);
        break;
}

Step 3: Select and Open the Target Project Folder

When creating a new project, we call vscode.window.showQuickPick to list two options: using current workspace folder or opening a new folder.

const answer = await vscode.window.showQuickPick(['Yes', 'No'], { placeHolder: 'Do you want to create a new folder?' });
        if (!answer) { return; }

If the answer is Yes, we call vscode.window.showOpenDialog to pop up a window for opening a folder:

const projectName = await vscode.window.showInputBox({
            prompt: 'Enter a name for the new project',
            validateInput: (value: string): string => {
                if (!value.length) {
                    return 'A project name is required';
                }
                return '';
            }
        });

let workspace = '';
const folderUris = await vscode.window.showOpenDialog({ canSelectFolders: true, canSelectFiles: false, canSelectMany: false, openLabel: 'Select folder' });
if (!folderUris) {
    return '';
}

let workspaceFolderUri = folderUris[0];
workspace = workspaceFolderUri.fsPath;
let des = path.join(workspace, projectName);

If the answer is No, we use the current workspace folder:

let folders = vscode.workspace.workspaceFolders;
let des = folders[0].uri.fsPath;

Step 4: Copy Project Files to the Target Folder

Once the target folder is determined, we can copy the project template files to the target folder:

export function copyFolder(src: string, des: string) {
	fs.readdir(src, (err, files) => {
		if (err) {
			return;
		}

		files.forEach(file => {
			let srcFile = path.join(src, file);
			let desFile = path.join(des, file);
			if (fs.statSync(srcFile).isDirectory()) {
				fs.mkdirSync(desFile);
				copyFolder(srcFile, desFile);
			} else {
				fs.copyFileSync(srcFile, desFile);
			}
		});
	});
}

copyFolder(src, des);

Add Code Snippet Support

The structure of the snippet files are as follows:

{
    "prefix": "",
    "body": "",
    "description": ""
}

Create a folder named snippets and add some snippet files:

snippets
 - android.json
 - cpp.json
 - csharp.json
 - python.json
 - swift.jon
 - web.json

After that, open package.json to add the snippet file paths:

"contributes": {
    "snippets": [
			{
				"language": "python",
				"path": "./snippets/python.json"
			},
      {
				"language": "csharp",
				"path": "./snippets/csharp.json"
			},
      {
				"language": "html",
				"path": "./snippets/web.json"
			},
      {
				"language": "cpp",
				"path": "./snippets/cpp.json"
			},
      {
				"language": "java",
				"path": "./snippets/android.json"
			},
      {
				"language": "swift",
				"path": "./snippets/swift.json"
			}
		]
}

As long as the extension is installed in Visual Studio Code, we can quickly add code snippet in corresponding language files to expedite the development process.

vscode Dynamsoft Barcode Reader snippets

Download Parameter Template Files

To download files from specified URLs in VSCode extension, we invoke http or https module. The vscode.window.withProgress is used to visually show the progress status.

import * as https from 'https';

export async function httpsDownload(url: string, filename: string) {
	await vscode.window.withProgress({
		location: vscode.ProgressLocation.Notification,
		cancellable: true
	}, (progress, token) => {
		token.onCancellationRequested(() => {
			console.log("User canceled the long running operation");
		});

		progress.report({ message: "Downloading " + filename });

		return new Promise((resolve, reject) => {
			const file = fs.createWriteStream(filename);
			https.get(url, function (response: any) {
				response.pipe(file);
				file.on("finish", () => {
					file.close();
					console.log("Download Completed");
					resolve("Download Completed");
				});
			});

		});
	});
}

We call the function defined above to download parameter template files of Dynamsoft Barcode Reader:

import * as vscode from 'vscode';
import { httpsDownload } from './utils';
import * as path from 'path';

enum Template {
    URL = "https://raw.githubusercontent.com/yushulx/cmake-cpp-barcode-qrcode/main/templates/",
    SPEED = 'Speed',
    SPEED_FILE = 'speed.json',
    BALANCED = 'Balanced',
    BALANCED_FILE = 'balanced.json',
    COVERAGE = 'Coverage',
    COVERAGE_FILE = 'coverage.json',
    MORECOVERAGE = 'More Coverage',
    MORECOVERAGE_FILE = 'morecoverage.json',
    MOSTCOVERAGE = 'Most Coverage',
    MOSTCOVERAGE_FILE = 'mostcoverage.json',
}

export class ParameterManager {
    private templates: any = {};

    constructor() {
        this.templates[Template.SPEED] = Template.URL + Template.SPEED_FILE;
        this.templates[Template.BALANCED] = Template.URL + Template.BALANCED_FILE;
        this.templates[Template.COVERAGE] = Template.URL + Template.COVERAGE_FILE;
        this.templates[Template.MORECOVERAGE] = Template.URL + Template.MORECOVERAGE_FILE;
        this.templates[Template.MOSTCOVERAGE] = Template.URL + Template.MOSTCOVERAGE_FILE;
    }

    public async downloadParameterFile() {
        // Select a template file
        const answer = await vscode.window.showQuickPick([Template.SPEED, Template.BALANCED, Template.COVERAGE, Template.MORECOVERAGE, Template.MOSTCOVERAGE], { placeHolder: 'Select a template' });
        if (!answer) { return; }

        let folders = vscode.workspace.workspaceFolders;
        if (!folders) {
            vscode.window.showWarningMessage('No folder is opened.');
        }
        else {
            let des = folders[0].uri.fsPath;
            switch (answer) {
                case Template.SPEED:
                    httpsDownload(this.templates[Template.SPEED], path.join(des, Template.SPEED_FILE));
                    break;
                case Template.BALANCED:
                    httpsDownload(this.templates[Template.BALANCED], path.join(des, Template.BALANCED_FILE));
                    break;
                case Template.COVERAGE:
                    httpsDownload(this.templates[Template.COVERAGE], path.join(des, Template.COVERAGE_FILE));
                    break;
                case Template.MORECOVERAGE:
                    httpsDownload(this.templates[Template.MORECOVERAGE], path.join(des, Template.MORECOVERAGE_FILE));
                    break;
                case Template.MOSTCOVERAGE:
                    httpsDownload(this.templates[Template.MOSTCOVERAGE], path.join(des, Template.MOSTCOVERAGE_FILE));
                    break;
            }
        }
    }
}

Common Issues and Edge Cases

  • Extension activation fails silently: If the commands do not appear in the Command Palette after installation, verify that every command string in package.json under activationEvents exactly matches the corresponding entry under contributes.commands. A single typo will prevent VS Code from registering the command.
  • Project template folder not found: The path.join(__dirname, '../res/...') paths are resolved relative to the compiled JavaScript output, not the TypeScript source. After changing the res/ folder structure, run a clean build and confirm the files are copied into the out/ directory.
  • HTTPS download hangs behind a corporate proxy: The built-in https.get does not respect VS Code’s proxy settings automatically. If downloads stall, read vscode.workspace.getConfiguration('http').get('proxy') and pass it as an agent option to https.get.

Source Code

https://github.com/yushulx/vscode-extension-dev/tree/main/dbr