Visual Basic 6 / MSVBVM60.DLL · native x86 and P-Code

VB6 decompiler: from a compiled EXE back to a working VB6 project.

Our VB6 decompiler reads a compiled Visual Basic 6 program — an EXE, an ActiveX DLL or an OCX control — and rebuilds the project: the .vbp, every form with its controls and .frx resources, modules, classes, and the code behind them as readable Basic. It handles both ways VB6 could compile: native x86 machine code, the hard case and our specialty, and P-Code. Use it yourself with a license, or let us run the recovery.

See three native VB6 programs decompiled next to their original source →

// under the hood

How a VB6 EXE is put together

Every VB5 and VB6 binary carries a VB5! header. From it the decompiler walks the project information block to the object table: forms, standard modules, classes, user controls, property pages, user documents and designers, each with its own object info and type information. Form layouts are not text inside the EXE — each control's design-time properties are a persisted property stream, which the engine reads back through COM using VB6.OLB, and external OCX controls are instantiated by CLSID and asked to load their own saved state. Pictures and other binary resources are split back out into .frx files.

The code is where the two kinds of VB6 program part ways. In the IDE you chose Compile to Native Code or Compile to P-Code. A P-Code build imports only the runtime's interpreter entry points (ProcCallEngine, MethCallEngine) and carries a token stream; a native build imports the full __vba* helper set and carries real x86 machine code. The decompiler tells them apart from the import table and uses a completely separate engine for each. This page is mostly about the native engine; P-Code has its own page.

// what comes back

What the VB6 decompiler brings back

  • ✓

    The whole project, ready to open

    A .vbp with Form=, Module=, Class=, UserControl=, PropertyPage=, UserDocument= and Designer= entries, MDI forms and menus, plus the Object= and Reference= lines for every OCX and type library the original used. ActiveX DLLs and OCX controls come back as proper ActiveX projects.

  • ✓

    Readable Basic lifted from machine code

    Each procedure is disassembled and folded back into VB idioms: If / ElseIf / Else, Do While, For / Next, Select Case (including compiled jump tables), string operations and comparisons, file I/O (Open For Binary, Get, Put, Line Input, Print #), Load / Unload, Is Nothing, and control arrays such as lblSkillName(i).Caption.

  • ✓

    API declarations and constants

    Win32 Declare statements are emitted once with their signatures, and well-known Win32 constants are substituted back for their numeric values.

  • ✓

    User-defined types, rebuilt field by field

    The runtime's record descriptors let the engine rebuild every Type with its members and sizes, fixed-length strings included. On our Dungeon benchmark UDT_00405710 is the original MapType, field for field.

  • ✓

    Typed local declarations

    A Dim block opens every procedure, with types inferred from how each local is used — String, Long, Integer, a control class, a class you defined, or Variant when nothing narrows it.

  • ✓

    Names that VB6 keeps

    Form and control names, class method names, event handlers with their parameter names, Public form procedures, Declare names and control property names all come back as written.

// measured

Measured on real programs, not demos

Every change to the native engine is gated against programs whose original source we hold, so the numbers below are counts, not impressions. Placeholders are the markers the engine leaves where it will not guess — an unresolved condition is <cond>, an unresolved argument <arg>, an unresolved method call UnkVCall.

BenchmarkMetricThen → now
Dungeon (native VB6 game, full source)Unresolved conditions426 → 65
GoTo fallbacks in place of structured blocks925 → 413
Garbage compares (0 <> 0)77 → 7
Locals given a concrete type49% of 507
RPG World Online Auto-UpdaterUnresolved method calls57 → 0 in the current build
Rodent's RevengeRebuilt projectcompiles and runs

Judge the output yourself — three native VB6 recoveries, line for line →

// the fine print

What a native VB6 EXE strips, and what we do about it

Stripped by the compiler, so no decompiler can bring them back

Local variable names, module-level global names, the names of standard-module procedures and of Private form procedures, user-defined type and field names, and user constants — both the names and, because the compiler inlines them, the values. Comments were never compiled in the first place.

The decompiler gives each of these a stable, readable placeholder — var_18, arg_8, proc_401F20, global_0041B1A0, field_C, UDT_00405710 — that your team renames as it reads. Where a condition or argument cannot be proven, it is marked <cond> or <arg> rather than filled in. A confident wrong answer is worse than an honest gap, and the Dism view in the software shows the underlying instructions for any line you doubt.

// how to decompile your program

Three ways to get from the EXE to source

STEP 01

Confirm the format

Run the free VBScanner on the folder. It reads the file headers and tells you what built each EXE, DLL or OCX and whether it is P-Code, native or IL.

STEP 03

…or let us do it

Request a free evaluation. We examine the binary, tell you exactly what comes back, and quote a fixed price and timeline. Hand-finishing of anything the engine marks is included.

// questions

VB6 decompiler questions

Can a VB6 EXE really be decompiled back to source?

Yes. A VB6 EXE keeps the complete form layouts, the project structure, event-handler names, class method names and Declare signatures, and its code is either P-Code (decoded token by token) or native x86 (lifted back to Basic). What you get is a project that opens in VB6 with readable code and placeholder names where the compiler stripped the originals.

Is my program native code or P-Code, and does it matter?

It matters for how faithful the code comes back: P-Code keeps more structure, native needs the reconstruction engine. The free VBScanner reads the VB5! header and tells you which you have in seconds. Both are supported.

Does the VB6 decompiler handle DLLs and OCX controls?

Yes. ActiveX DLLs and OCX controls are rebuilt as ActiveX DLL and ActiveX Control projects with their public classes and user controls in place.

Will the decompiled project compile?

Frequently it loads and, after renaming placeholders and adding any missing declarations, compiles; our Rodent's Revenge benchmark rebuilds to a running EXE. The fastest route is to open the generated .vbp in VB6 and let the compiler's errors drive the clean-up. The evaluation tells you what to expect for your specific program before you commit.

What about UPX-packed or protected EXEs?

UPX-packed programs are detected and unpacked automatically on a working copy. Other packers and protectors are assessed during the free evaluation, and a memory dump can be decompiled from an offset.

// other formats

Every generation of Basic has its own page

Ready to decompile your VB6 program?

The evaluation is free and tells you exactly what your binary yields before you spend anything. Or license the decompiler and run it in-house today.

free evaluation fixed quotes NDA available software from $299