How v2rayN Handles QR Code Scanning for vmess:// and ss:// URLs
v2rayN uses ZXing.SkiaSharp to decode QR codes from screen captures or image files, then routes the extracted vmess:// or ss:// URLs through FmtHandler to protocol-specific parsers that convert them into ProfileItem objects stored in SQLite.
v2rayN is a Windows GUI client for V2Ray and Xray cores that simplifies proxy configuration through QR code scanning. Understanding how v2rayN handles QR code scanning for vmess:// and ss:// URLs reveals a robust pipeline from image capture to database persistence. This article examines the complete workflow implemented in the 2dust/v2rayN repository, from the UI triggers in MainWindowViewModel to the protocol-specific parsing logic in VmessFmt and ShadowsocksFmt.
The QR Code Scanning Pipeline Overview
When a user selects "Scan QR code on screen" or "Scan QR code from an image", v2rayN executes a nine-step pipeline:
- UI triggers scan command via
AddServerViaScanCmd - Image capture or file loading via
QRCodeUtils.CaptureScreen - QR decoding via
QRCodeUtils.ParseBarcodeusing ZXing.SkiaSharp - Result processing via
MainWindowViewModel.AddScanResultAsync - Batch import via
ConfigHandler.AddBatchServers - Protocol detection via
FmtHandler.ResolveConfig - URL parsing via
VmessFmt.ResolveorShadowsocksFmt.Resolve - Profile creation as
ProfileItemobjects - Database persistence via SQLite
Step 1: UI Trigger and Image Capture
The process begins in ServiceLib/ViewModels/MainWindowViewModel.cs when the AddServerViaScanCmd command executes. Depending on user selection, it raises either EViewAction.ScanScreenTask or EViewAction.ScanImageTask.
For screen captures, the application calls QRCodeUtils.CaptureScreen, which is implemented in ServiceLib/Common/QRCodeUtils.cs. This method captures the entire screen bitmap on Windows systems. For image files, the file path passes directly to the decoding routine.
Step 2: QR Code Decoding with ZXing.SkiaSharp
The core decoding logic resides in QRCodeUtils.ParseBarcode. This method uses SkiaSharp to load the bitmap and ZXing.SkiaSharp to read the barcode data.
// QRCodeUtils.ParseBarcode – returns the raw URL string
string? url = QRCodeUtils.ParseBarcode(imageBytes);
// url might be something like "vmess://eyJ...==" or "ss://YWVzLTI1Ni1j..."
// If the QR is mirrored, a second attempt with a flipped bitmap is performed internally.
If the initial scan fails, the implementation attempts a second pass with a horizontally flipped bitmap to handle mirrored QR codes.
Step 3: Routing URLs to the Import Engine
Once decoded, MainWindowViewModel.AddScanResultAsync processes the raw string:
private async Task AddScanResultAsync(string? result)
{
if (result.IsNullOrEmpty())
NoticeManager.Instance.Enqueue(ResUI.NoValidQRcodeFound);
else
{
// Import the server(s) – handles vmess, ss, vless, … automatically
int imported = await ConfigHandler.AddBatchServers(_config, result,
_config.SubIndexId, false);
if (imported > 0)
NoticeManager.Instance.Enqueue(ResUI.SuccessfullyImportedServerViaScan);
}
}
This method delegates to ConfigHandler.AddBatchServers in ServiceLib/Handler/ConfigHandler.cs, which handles batch imports from any source.
Step 4: Protocol-Specific Parsing
ConfigHandler.AddBatchServers invokes FmtHandler.ResolveConfig in ServiceLib/Handler/Fmt/FmtHandler.cs. This dispatcher examines the URL prefix and routes to the appropriate formatter.
Parsing vmess:// URLs
For vmess:// links, VmessFmt.Resolve in ServiceLib/Handler/Fmt/VmessFmt.cs handles two formats:
- Standard vmess URI: Base64-encoded JSON following the VMess AEAD standard
- Legacy JSON: Plain JSON objects in the QR code
// Inside VmessFmt.Resolve
var vmessQRCode = JsonUtils.Deserialize<VmessQRCode>(jsonString);
if (vmessQRCode != null)
{
var profile = new ProfileItem { ConfigType = EConfigType.VMess };
profile.Address = Utils.ToString(vmessQRCode.add);
profile.Port = vmessQRCode.port;
profile.Password = Utils.ToString(vmessQRCode.id);
profile.SetProtocolExtra(new ProtocolExtraItem {
AlterId = vmessQRCode.aid.ToString(),
VmessSecurity = vmessQRCode.scy.IsNullOrEmpty()
? Global.DefaultSecurity : vmessQRCode.scy,
});
// … fill network, TLS, etc.
return profile;
}
Parsing ss:// URLs
For ss:// links, ShadowsocksFmt.Resolve in ServiceLib/Handler/Fmt/ShadowsocksFmt.cs supports:
- Legacy format:
ss://BASE64(method:password)@host:port - SIP002 format:
ss://BASE64(method:password)@host:port/?plugin=...
// Inside ShadowsocksFmt.Resolve
if (str.StartsWith(Global.ProtocolShares[EConfigType.Shadowsocks]))
{
// Strip prefix, Base64‑decode, then split "method:password@host:port"
var decoded = Utils.Base64Decode(str.Substring(prefix.Length));
var parts = decoded.Split('@');
var methodPwd = parts[0].Split(':');
var hostPort = parts[1].Split(':');
var profile = new ProfileItem { ConfigType = EConfigType.Shadowsocks };
profile.Address = hostPort[0];
profile.Port = int.Parse(hostPort[1]);
profile.SetProtocolExtra(new ProtocolExtraItem {
SsMethod = methodPwd[0],
// Shadowsocks password is the second part
SsPassword = methodPwd[1],
});
return profile;
}
Step 5: Database Persistence
After parsing, the ProfileItem objects return to ConfigHandler.AddBatchServers, which inserts them into the SQLite database via SQLiteHelper.Instance.InsertAllAsync. The UI refreshes automatically, displaying the new server in the profile list with a success notification.
Summary
- v2rayN QR code scanning begins with screen capture or image selection in
MainWindowViewModel.cs - ZXing.SkiaSharp handles decoding in
QRCodeUtils.ParseBarcode, with fallback for mirrored codes - FmtHandler.ResolveConfig routes URLs to protocol-specific parsers based on the scheme prefix
- VmessFmt.Resolve handles both Base64-encoded and JSON vmess formats
- ShadowsocksFmt.Resolve supports legacy and SIP002 ss:// URL formats
- All parsed servers persist as ProfileItem objects in SQLite via
ConfigHandler.AddBatchServers
Frequently Asked Questions
How does v2rayN handle invalid or mirrored QR codes?
If QRCodeUtils.ParseBarcode fails to decode a QR code on the first attempt, it automatically retries with a horizontally flipped bitmap. This handles cases where the QR code is mirrored or reflected. If both attempts fail, the method returns null and the UI displays "No valid QR code found."
What QR code formats are supported for vmess:// URLs?
v2rayN supports two vmess QR code formats in VmessFmt.Resolve: the standard vmess URI format (vmess://BASE64_ENCODED_JSON) and legacy plain JSON objects. The parser attempts JSON deserialization first, then falls back to Base64 decoding if needed, extracting fields like address, port, UUID, alterId, and security settings.
Can v2rayN scan QR codes from files as well as the screen?
Yes. The MainWindowViewModel exposes two distinct commands: one for screen scanning (EViewAction.ScanScreenTask) that uses QRCodeUtils.CaptureScreen, and one for image files (EViewAction.ScanImageTask) that passes the selected file path directly to QRCodeUtils.ParseBarcode. Both paths converge on the same decoding and import logic.
How does v2rayN distinguish between vmess and Shadowsocks URLs?
The FmtHandler.ResolveConfig method acts as a dispatcher, checking the URL prefix against the Global.ProtocolShares dictionary. If the string starts with vmess://, it delegates to VmessFmt.Resolve. If it starts with ss://, it delegates to ShadowsocksFmt.Resolve. This allows v2rayN to handle mixed batches of different protocol URLs in a single scan operation.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →