Technical support :

Perfect solutions

for software protection

and source code recovery

Deeper Native Code Intelligence in VB Decompiler v26.2


We are proud to announce the release of VB Decompiler version 26.2. This is a technically dense update focused on deep improvements to Native Code decompilation accuracy: a three-tier Variant variable detection system, full read/write support for multidimensional SafeArray access, snapshot-based state isolation for conditional branches, a significantly expanded COM/IID heuristics database, and numerous engine-level fixes and refactors that make the decompiler more reliable on complex real-world binaries.

Three-Tier Variant Variable Detection in Native Code


One of the core challenges in decompiling VB6 Native Code is correctly identifying Variant variables on the stack during function calls. Variants are VB6's universal container type and can hold integers, strings, objects, or any other subtype at runtime. In compiled native code, they are passed as raw memory structures with no explicit type annotation, making accurate reconstruction exceptionally difficult.

Version 26.2 introduces a comprehensive three-tier detection pipeline that approaches the problem from multiple angles simultaneously:
  • Heuristic detection, based on stack analysis. The decompiler inspects stack layout and calling conventions to infer where Variant structures are being constructed and passed, even in the absence of any other contextual information.
  • Signature-based detection, using a pre-prepared property and method database. A curated database of known object properties and methods - covering the most frequently called COM interfaces in VB6 programs - allows the decompiler to recognize Variant parameters by matching the call target against known signatures.
  • On-the-fly type determination by parsing the called object's interface via its IID. When the decompiler encounters a late-bound call and can resolve the Interface ID of the target object, it parses the object's interface description at decompilation time and uses the resulting type information to determine parameter types precisely.

Together, these three mechanisms dramatically reduce the number of cases where Variant parameters are left unresolved or misidentified, particularly in code that makes heavy use of COM automation.

Parsing of called function parameters during both early binding (resolved at compile time via TypeLib) and late binding (resolved at runtime via IDispatch), as well as direct Win32 API calls, has also been made significantly more accurate and consistent.

Decompiled VB6 Native Code showing correctly identified Variant parameters in function calls before and after the three-tier detection improvement

Expanded Variant Subtype Parsing


Alongside the new detection tiers, the Variant type parser itself has been extended. Previously, the parser recognized only five subtypes with dedicated handling: vbBoolean, vbSingle, vbDouble, vbCurrency, and vbDate. All other subtypes were treated generically and their types could only be inferred heuristically.

Version 26.2 adds dedicated parsing support for four additional subtypes:
  • vbInteger - 16-bit signed integer values.
  • vbLong - 32-bit signed integer values.
  • vbByte - unsigned 8-bit values.
  • vbString - BSTR string values embedded in a Variant.
  • vbObject - object references held inside a Variant.

TypeLibrary parsing and the object database construction pipeline have both been updated to account for these new subtypes. This allows large sections of code that were previously resolved only through guesswork to now be typed precisely and consistently, resulting in cleaner and more accurate decompiled output.

Support most cases of SafeArray: Multidimensional Array Read and Write


This is one of the most impactful decompilation improvements in version 26.2. VB6 stores most arrays internally as SafeArray structures - a COM-defined descriptor that carries element type, dimensionality, bounds, and a pointer to the underlying data. Correctly reconstructing array access from native code requires the decompiler to understand this structure in full: parse the descriptor, track the number of dimensions and their bounds, compute element addresses from multi-index expressions, and map all of that back to readable VB6 subscript notation.

Prior versions of VB Decompiler had only partial SafeArray awareness. The decompiler could recognize array creation and resizing operations - calls to __vbaRedim, __vbaRedimPreserve, and similar VB runtime APIs - and emit the corresponding ReDim statements correctly. However, the actual reading and writing of array elements at runtime was not understood. The raw compiled code that accessed array memory - loading the SafeArray pointer, reading the dimension descriptors, computing the flat memory offset from multi-dimensional indices, and dereferencing the element - was left unresolved. The decompiler emitted it as a tangled sequence of register-level expressions, pointer arithmetic, and partially-evaluated stack operations that bore no resemblance to the original source.

The results were particularly destructive in methods that performed heavy array work. Array element accesses would be rendered as grotesque compound expressions that mixed raw memory offsets with partially recognized structure fields, all concatenated with equals signs, parentheses and index values in ways that had no syntactic validity in any language. Any meaningful reconstruction of the method's logic was effectively impossible from such output.

What Was Happening Before


To understand the severity of the problem, consider a fragment from the Before output of a real-world decompilation of a method that works with a four-dimensional array. The following lines appeared in the output:


  loc_00412DEE:   If var_7C(152)(156) = var_7C(152)(160) Then
  loc_00412DF8:     If var_7C(152)(156) = var_7C(152)(160) = 4 Then
  loc_00412E15:       If (eax - var_7C(152)(156) = var_7C(152)(160)(44)) >= var_7C(152)(156) = var_7C(152)(160)(40) Then
  loc_00412E62:       var_20(24)*(edi - var_7C(152)(156) = var_7C(152)(160)(20)) = ...
  loc_00412E68:       var_20(24)*(edi - ...)+(ebx - ...)*var_7C(152)(156) = ... + var_BC
  loc_00412E72:       var_20(24)*(edi - ...)+(ebx - ...)*...+var_BC*... = ... + (eax - ...)

These lines represent what the decompiler produced when it encountered SafeArray element reads and writes without understanding the SafeArray structure. Each "expression" is actually a collapsed rendering of raw pointer arithmetic against the SAFEARRAY and SAFEARRAYBOUND descriptor fields - the cElements and lLbound members of each dimension's bound record, together with the computed flat offset into the data buffer. The output was syntactically invalid, semantically meaningless, and completely unreadable.

Side-by-side comparison of the same VB6 method decompiled before and after SafeArray support: the Before column shows dozens of lines of raw pointer arithmetic expressions; the After column shows clean Dim declarations and concise array subscript access

The New SafeArray Engine


Version 26.2 introduces comprehensive SafeArray analysis that covers the full lifecycle of array operations in compiled VB6 Native Code:
  • SafeArray descriptor parsing. The decompiler now fully parses the in-memory SAFEARRAY structure: element size (cbElements), dimension count (cDims), data pointer (pvData), and the per-dimension SAFEARRAYBOUND records containing element count and lower bound. In most cases this gives the decompiler a complete model of the array's shape at decompilation time.
  • Multidimensional index reconstruction. VB6 compiles multi-dimensional subscript access (arr(i, j, k)) into a flat memory offset computed as a sequence of multiply-and-add operations against the dimension sizes and lower bounds. The decompiler now recognizes most of these patterns, reverses the offset calculation, and reconstructs the original index expressions for each dimension.
  • Read access reconstruction. Array element reads - where compiled code loads a value from the computed element address - are now in most cases recognized and emitted as proper subscript expressions: var_20(i, j, k, l).
  • Write access reconstruction. Array element writes, including indexed assignment targets on the left-hand side of assignment statements, are handled symmetrically in most cases.
  • VarPtr on array elements. Calls to VarPtr with an array element as the argument - used to obtain a raw pointer to a specific element, a common pattern in VB6 code that passes array data to API functions - are now correctly reconstructed as VarPtr(arr(i, j, ...)) with proper index expressions.
  • Bounds-checking pattern recognition. VB6 compiled code inserts explicit bounds checks before each array access, comparing the requested index against the lower bound and element count stored in the SAFEARRAYBOUND record. The decompiler recognizes these generated check sequences and suppresses them from the output - they are compiler-inserted boilerplate, not original source logic, and their presence in prior output was responsible for much of the surrounding noise.
  • Variable type annotation. Variables identified as SafeArray references are now declared with the SafeArray type in the decompiled output, making the relationship between declarations and usage explicit.

The Result


The same method that previously produced dozens of lines of unreadable register-level noise is now decompiled to clean, immediately recognizable VB6 source. The critical section, which previously collapsed into the multi-line catastrophe shown above, is now rendered as:


  Dim var_20 As SafeArray
  loc_00412DE3:   var_20 = global_160
  loc_00412EA7:   global_176 = VarPtr(var_20(1, 1, 1, 1))
  loc_00412EB6:   global_180 = VarPtr(global_108(3))

Three lines instead of over twenty. A four-dimensional array access expressed as var_20(1, 1, 1, 1) instead of an arithmetic explosion of descriptor field offsets. The variable var_20 is correctly declared as Dim var_20 As SafeArray at the top of the procedure. Bounds-check boilerplate is gone. The method is readable.

This improvement affects most VB6 Native Code application that uses arrays in non-trivial ways - image processing code, numerical algorithms, data transformation routines, game logic, or any other domain where multidimensional arrays are a natural fit. The quality improvement on such methods is among the most visually dramatic of any change in recent versions.

Side-by-side comparison of the same VB6 method decompiled before and after SafeArray support: the Before column shows dozens of lines of raw pointer arithmetic and invalid expressions; the After column shows clean Dim declarations and concise array subscript access

Snapshot-Based State Isolation for Conditional Branches


A long-standing source of decompilation errors in complex VB6 Native Code methods was state pollution between If/Else branches. When the decompiler analyzed the If branch of a conditional construct, any changes it made to its internal model - register values, CPU/FPU stack state, variable assignments - would carry over into the analysis of the Else branch, as if the two paths shared execution context. This led to incorrect variable assignments, phantom references, and malformed output, especially in user-defined class methods with complex logic.

Version 26.2 resolves this fundamentally. When the decompiler encounters an If/Else construct, it now:
  • Captures a full state snapshot at the branch point, including all register values, the CPU and FPU stack state, and the complete variable assignment map.
  • Analyzes the If branch using a working copy of this snapshot.
  • Restores the saved snapshot when entering the Else block, ensuring the two branches are analyzed in complete isolation from each other.

This change has a measurably positive impact on decompilation quality for any binary that contains non-trivial conditional logic - which, in practice, means the vast majority of real-world VB6 Native Code programs.

Decompiled VB6 class method showing correct If/Else branch isolation with properly scoped variables in each branch

Rewritten Temporary and Unused Variable Analysis


The analysis and filtering of temporary and unused variables in decompiled VB6 Native Code has been completely rewritten from scratch. The previous implementation accumulated technical debt over time and produced inconsistent results on optimized or compiler-generated intermediate code patterns.

The new implementation applies a clean, structured analysis pass that correctly identifies variables that exist solely as compiler-generated temporaries and eliminates them from the output - without incorrectly removing variables that, while short-lived, carry semantic meaning. This results in significantly less noise in the decompiled output and more readable reconstructed source code.

Significantly Expanded Late-Binding IID Heuristics


VB6 programs that use late binding - that is, object variables declared as Object rather than a specific type - leave almost no type information in the compiled binary. The decompiler's late-binding heuristic analyzer identifies the runtime interface being called by matching the Interface ID (IID) of the target against a known database.

This database has been massively expanded in version 26.2 and now covers the following interface families:
  • Core COM/OLE interfaces - ITypeLib, ICreateTypeInfo, ITypeInfo, and related type system objects.
  • Microsoft Office automation - interfaces from Word, Excel, Access, Outlook, and PowerPoint.
  • Data access - ADO (ActiveX Data Objects) and DAO (Data Access Objects).
  • XML and HTTP - MSXML, WinHTTP, and WinInet interfaces.
  • Scripting and shell - FileSystemObject (Scripting runtime), Shell objects, and WScript.
  • Web browser - Internet Explorer and WebBrowser control interfaces.
  • Messaging - CDO and MAPI interfaces for email automation.
  • Directory services - ADSI (Active Directory Service Interfaces).
  • Scripting - VBScript.RegExp and related objects.

Each interface entry in the database also carries its parent library GUID. If the corresponding TypeLib is registered in the Windows registry on the machine running VB Decompiler, the tool will automatically pull the full TypeLib information from it and use it to enhance decompilation of that interface's methods and properties - without any manual configuration required.

Decompiled VB6 code using late-bound Excel and ADO objects, showing correctly resolved interface names and method calls

Reworked __vbaNew2 Object Creation Emulation


Object creation in VB6 Native Code is handled internally by the __vbaNew2 function in msvbvm60.dll (and equivalent variants in other runtime versions). Decompilation of this pattern has been completely reworked. The new implementation more accurately reconstructs set object to the new ClassName and handles edge cases that the previous version would either skip or misrepresent in the output.

Redesigned __vbaPrintObj Decompilation


The __vbaPrintObj runtime API, used internally by VB6 for Print method calls on various output objects, has been completely redesigned. The new implementation correctly identifies and distinguishes between the different print object types that can appear as the content of a print operation - such as strings, chars, tabs, and debug output - and represents each one accurately in the decompiled output.

SAR Instruction Emulation for Signed Division


The VB6 compiler frequently uses the sar (shift arithmetic right) assembly instruction as an optimization for dividing signed integers by powers of two. For example, sar eax, 1 is equivalent to dividing eax by 2, and sar eax, 2 divides by 4. Version 26.2 adds full emulation of this instruction in the Native Code analysis engine. Code that previously appeared as a raw bit-shift operation is now correctly reconstructed as an integer division expression, matching the original source intent.

Improved Code Analyzer: Conditional Jump Pre-Detection


The code analyzer now performs a pre-pass over all lines that contain conditional jumps before the main analysis begins. When such a line is identified, the analyzer will no longer collapse its address even if that location contains temporary variables that would normally be candidates for elimination. This prevents a class of edge-case errors where important branch targets were incorrectly merged or removed during optimization, causing structural defects in the reconstructed control flow.

Refactored and Faster Code Analyzer and Optimizer


The code analyzer and optimizer engine has undergone significant internal refactoring. Beyond correctness improvements, the refactoring has made the post-processing pipeline - the stage that transforms raw decompiled output into clean, structured VB6 source - substantially faster. Users working with large binaries or batch-processing multiple files will notice a meaningful reduction in analysis time.

Global Variable Highlighting


Version 26.1 introduced click-to-highlight for local string variables, making all occurrences of a selected str_XX variable visible at a glance. Version 26.2 extends this feature to global variables: clicking any global_XX variable in the code view will now highlight all its occurrences throughout the current procedure, making it easy to trace the flow of global state through complex methods.

VB Decompiler code view showing all occurrences of a global_XX variable highlighted after clicking one instance

String Reference Dialog: Over 32,000 Strings


The String Reference dialog has been updated to support binaries containing over 32,000 string references - a limit that was previously hard-coded and would cause truncation or display issues in very large programs. In addition, string searching and navigation are now fully supported within the dialog, allowing users to quickly locate specific strings in programs with large string tables.

Memory handling for string list operations has also been optimized, reducing memory consumption when working with large string tables.

String Reference dialog showing search field and large string list with over 32,000 entries

Updated API Signatures: rtcGetSetting and rtcDeleteSetting


The signatures for the rtcGetSetting and rtcDeleteSetting VB runtime API functions have been updated to correctly reflect their use of Variant type parameters. This correction ensures that calls to these functions - used for reading and deleting application settings stored in the registry - are decompiled with properly typed arguments rather than raw pointer expressions.

Completely Revised Spanish Help System


The Spanish-language help system has been completely rewritten and revised by a native speaker. The previous translation contained numerous inaccuracies and outdated sections that did not reflect the current feature set. The new translation covers the full help content with accurate, professional Spanish technical writing. Special thanks to Leonardo Donaire for his extensive work on this translation.

Bug Fixes


  • AI Helper tab resize on maximize: When maximizing the VB Decompiler window to full screen, the AI Helper tab did not resize correctly. The tab and its contents now resize properly along with the rest of the window.
  • String Reference dialog navigation (.NET): Attempting to navigate to a hidden compiler-generated function from the String Reference dialog in a .NET binary no longer causes an error.
  • Trace window residual lines: When reopening the trace window after a previous session, lines of code from the prior trace would in some cases remain visible. The trace window now correctly clears its content on reopen.
  • Missing End If in Else branch detection: The Else branch detection feature in the Native Code analyzer and optimizer failed to insert an End If closing block in certain rare code patterns. This has been corrected.
  • P-Code / Native Code cross-contamination: If a program compiled in P-Code was decompiled immediately after decompiling a program compiled in Native Code, the this pointer was incorrectly added to the basic properties of the Global object in the P-Code result. The decompiler now correctly resets all state between sessions.
  • Missing Else construct before conditional jump: The Else construct was not being generated when a conditional jump appeared immediately before the Else block in VB6 Native Code. This structural omission has been fixed.


April 5, 2026
© Sergey Chubchenko, VB Decompiler main developer



Visual Basic, Visual Studio, and Windows are registered trademarks of Microsoft Corporation.