Most migration plans account for the code and the database. The thing that stalls them is a grid.
Legacy Microsoft-stack applications are full of third-party components: an Infragistics or Telerik grid on a WebForms page, a ComponentOne or Sheridan OCX on a VB6 form, a barcode or PDF control someone bought in 2007, a scheduler nobody can find a license file for. None of it ships with the framework, none of it has a .NET 8 equivalent you can drop in, and some of it cannot legally be rebuilt because the license was per-developer and the developer left.
This tutorial produces an inventory of those dependencies and a decision for each one, before you estimate a slice. Doing it afterward is how a six-week slice becomes a ten-week slice.
Find what the application actually uses
VB6: read the project and the forms
A .vbp file lists every component the project references. Controls appear as Object= lines with a CLSID, a version, and a file name; libraries appear as Reference= lines.
grep -h -E '^(Object|Reference)=' *.vbp | sort -u
You will get lines like:
Object={F9043C88-F6F2-101A-A3C9-08002B2F49FB}#1.2#0; COMDLG32.OCX
Object={86CF1D34-0C5F-11D2-A9FC-0000F8754DA1}#2.0#0; MSCOMCTL.OCX
Object={5E9E78A0-531B-11CF-91F6-C2863C385E30}#1.0#0; MSFLXGRD.OCX
The .vbp tells you what is referenced. The .frm files tell you what is actually placed on a form and in what quantity, which is what drives effort:
grep -h -oE 'Begin [A-Za-z0-9_.]+ ' *.frm | sort | uniq -c | sort -rn
Count instances, not control types. One flex grid used on three screens is a different job from the same grid used on sixty.
WebForms: read the Register directives and the bin folder
Every third-party control on an .aspx page is declared:
grep -rhoiE '<%@\s*Register[^%]+%>' --include='*.aspx' --include='*.ascx' . \
| grep -oiE 'Assembly="[^"]+"' | sort | uniq -c | sort -rn
Then cross-check web.config (controls are often registered globally under <pages><controls>), packages.config, and the raw contents of bin:
ls bin/*.dll | grep -viE 'System\.|Microsoft\.|^bin/(YourCompany)'
Anything in bin that is neither framework nor yours is a vendor dependency, including the ones nobody remembers buying.
Find the licensing artifacts
Two files decide whether you can still build:
licenses.licxin a WebForms project. It is a plain text list of licensed component type names, compiled into a.licensesresource at build time bylc.exe. If the design-time license is not installed on the build machine, the build fails or the control renders a nag banner at runtime.*.licor registry entries for VB6-era OCX files, which were licensed per development machine, not per server.
find . -name 'licenses.licx' -o -name '*.lic' | xargs -r -n1 head -20
If you cannot build the legacy application today because of a missing design-time license, say so in the inventory in those words. It is a schedule risk, not a detail.
Classify each component
Put the result in a table your client can read. Five columns: component, where it is used, how many instances, what it does, and the decision. There are only four decisions worth making.
Replace with framework or first-party. Most legacy grids, date pickers, tab strips and masked text boxes exist to fill gaps the platform has since filled. A MSFlexGrid showing rows with a sort is a QuickGrid in Blazor or a plain table with a sort parameter. Decide this per use, not per control: the same grid may be a read-only list on one screen and an in-place editor with merged cells on another.
Buy the current version from the same vendor. Infragistics, Telerik, DevExpress and ComponentOne all ship Blazor and ASP.NET Core suites, and the API shapes rhyme with the WebForms ones. This is the fastest path when there are forty screens of grids, and it costs real money per seat per year. Get the quote before the estimate, not after.
Wrap and keep. A component that is pure computation with no UI — a PDF generator, a tax table, a barcode encoder, a device SDK — can often stay where it is for now. Out-of-process COM behind a small HTTP endpoint lets the new slice call it without hosting it. We walked through the mechanics in Reaching 32-bit dependencies from a 64-bit .NET 8 slice.
Delete. Expect 20 to 40 percent of the inventory to be components referenced by the project and used on no live screen, or used on a screen reached by nobody. Check the access logs before you spend a day reimplementing a report viewer.
Validate the decisions before you commit them
Two checks, both cheap.
Pick the single hardest instance of each component you plan to replace — the grid with the frozen columns, grouping and the Excel export — and build that one screen on the target stack. Not a prototype of the easy case. A day spent on the worst screen turns a guess into a measurement, which is the only kind of estimate worth giving.
Then record the old screen's behavior before you touch it, so the replacement has something to agree with. The harness approach is in Golden-master tests for legacy web apps; for a grid, the behavior you record is the data and ordering, not the markup, since the markup is the part you are deliberately changing.
Where AI helps and where it does not
A coding agent is good at the mechanical half of this: reading every .frm and .aspx in a large solution, counting instances, pulling property settings off each control, and drafting the inventory table. We let it do that, then check the counts against a grep we wrote ourselves, because an agent that reads 400 forms will quietly summarize rather than enumerate.
It is not good at the decision column. Whether a merged-cell grid with conditional formatting is "a table" depends on who uses the screen and what they do with it, and that answer is in a conversation with the user, not in the code.
What to watch for
- Server-side Office and report controls. They bring their own problems beyond licensing; see Getting off server-side Excel automation and What to do with Crystal Reports.
- Vendor suites with runtime licensing by server. Confirm the deployment license covers the new hosts, not just the old ones, before cutover weekend.
- Dead vendors. If the vendor is gone, "buy the current version" is off the table and only replace, wrap or delete remain. Find that out in week one.
- Printing and hardware. Scales, scanners, label printers and signature pads are usually the last dependency anyone mentions and the first one that stops a cutover. Ask about the warehouse.
An inventory like this takes a day or two on a typical mid-market application and changes the shape of the plan more often than the code review does. If you want one done against your system, tell us what you are running and a senior engineer will reply within one business day.