Modernizing Legacy SCADA from VB6 to .NET: Practical Guide
Matteo Migliore

Matteo Migliore is an entrepreneur and software architect with over 25 years of experience developing .NET-based solutions and evolving enterprise-grade application architectures.

He has led enterprise projects, trained hundreds of developers, and helped companies of all sizes simplify complexity by turning software into profit for their business.

There is a file somewhere on a server. It has a .vbp extension and nobody knows how to open it anymore without installing Visual Basic 6, a development environment Microsoft officially abandoned in 2008. The developer who wrote it has retired. Or changed companies. Or, in some cases, is no longer around.

The system runs. Every morning, operators open a dedicated PC running Windows XP or Windows 7, and the screen fills with values, charts, and alarms. Everything works, in the sense that nothing has exploded in the past year. But that "works" is the kind of precarious functioning on which you cannot build anything.

Whoever inherits this situation, an IT manager, a developer brought in as a consultant, a department head who received the problem like an unwanted gift, knows that feeling well: the fear of touching something that "works but nobody understands anymore." Modifying it means potentially breaking it. Not modifying it means waiting for the day it breaks on its own, at the worst possible moment.

The real cost of this situation is not in a potential migration. It is in continuing to operate a system nobody can maintain, that cannot be extended, that runs on obsolete hardware and operating systems, and that holds the entire production hostage by depending on one last reachable technician, or worse, on nobody at all.

This article is written for those in that position: C# developers or technical managers who need to understand how to approach legacy SCADA modernization, from VB6 to .NET, without stopping production and without accumulating new technical debt in the attempt to escape the old. The strategies exist. The patterns are established. The Italian market urgently needs professionals who know how to apply them.

Why Legacy SCADA Systems Are a Ticking Time Bomb for Your Business

VB6 is not just "old." It is a technology that Microsoft officially stopped supporting in 2008. This means that every security vulnerability discovered after that date never received an official patch. Every dependency on COM and ActiveX libraries from third parties, widely used in VB6 projects of the era, is a blind spot: those libraries are not updated, many are no longer distributed, some no longer exist.

But the problem is not just security. It is operational compatibility. Windows 11 has removed or limited support for some 32-bit components and the VB6 runtime. Installing a VB6-based SCADA system on new hardware is often an adventure: dependencies fail, drivers cannot be found, ActiveX components for trend graphics refuse to register. The system that "worked" on that 2012 PC might not work on hardware purchased today.

The same applies to Delphi and C++/MFC: technologies that had their moment, but that today require skills that are increasingly rare on the market. Finding a qualified, available Delphi developer in 2026 is difficult. Finding one who also knows the specific SCADA plant you are running is nearly impossible.

One hour of production downtime at a medium-sized manufacturing plant, in the automotive or pharmaceutical sector, costs between 10,000 and 80,000 euros. This is not an estimate: it is the figure that emerges from the risk analyses companies perform when evaluating investments in operational continuity. The Italian pharmaceutical sector, with its FDA and GMP traceability obligations, has even higher downtime costs because every undocumented interruption risks compromising entire production batches.

The right question is not "does migrating cost too much?" but "how much does not migrating cost?" A legacy SCADA system has a useful operational window that is shrinking every year. It is not a question of whether it will become a critical problem, but when. Those who prepare in advance have options; those who wait for an emergency do not.

There is also an organizational knowledge problem. VB6 code written by a single developer, without documentation, with arbitrary tag naming and business logic buried inside Form_Load and Timer_Tick event handlers, is company knowledge at risk of disappearing. When that developer is no longer available, that knowledge becomes inaccessible. Not because it does not exist, but because nobody knows how to read it anymore.

How to Assess a Legacy SCADA System: the First Step Before Touching Anything

Before writing a single line of C#, before opening Visual Studio, before even setting a project plan, there is an activity that many underestimate: auditing the existing system. It is not a glamorous activity, but it is the one that determines the success or failure of everything that follows.

The audit covers five distinct areas. The first is tag inventory. A tag is a measurement or control point: a temperature, a pressure, an on/off state of a valve. In a legacy SCADA system, tags are often defined in proprietary configuration files, Access databases, or directly hard-coded in VB6 code. Every single one must be enumerated, with its name, data type, update frequency, and the PLC or field device it is associated with. This list is the backbone of the new system.

The second area is PLC and protocol mapping. What PLCs are installed? Siemens S7? Allen-Bradley ControlLogix? Modicon Quantum? Each family uses different protocols: the Siemens S7 protocol is not the same as Modbus TCP, which is not the same as EtherNet/IP. How does the old system connect to these PLCs? Does it use OPC Classic (DA/HDA)? Proprietary drivers? This information determines how difficult it is to build the new communication layer.

The third area is COM and ActiveX dependency discovery. VB6 projects almost universally use COM components for trend charts (Citect, WonderWare, or custom components), serial communication, and database access. Every COM component is a dependency that must be identified, and for each one you need to decide whether a modern equivalent exists or whether it needs to be rewritten.

The fourth area is business logic analysis. In a VB6 system, logic is often spread chaotically: calculations in forms, validations in timers, alarm rules buried in utility functions with unclear names. It needs to be extracted and documented before being rewritten. This step is also the opportunity to understand what the system actually does, distinct from what people thought it did.

The fifth area is technical debt estimation. The goal is not to grade the legacy code (the verdict is almost always the same: poor) but to quantify the migration effort honestly. How many forms are there? How many active lines of code? How many external integrations? These numbers determine the project timeline and allow you to choose the right strategy.

A well-executed audit takes one week to one month, depending on system complexity. It is an investment, not a waste of time. Every hour spent understanding the legacy system is an hour saved in migration errors.

Migration Strategies: Big Bang, Strangler Fig, and the Incremental Approach

There are fundamentally three strategies for migrating a legacy SCADA system. Each has its own contexts, risks, and advantages. Choosing the wrong one is one of the most expensive mistakes you can make in an industrial modernization project.

The first strategy is the so-called big bang: you shut down the old system and start the new one in a single moment. It is the simplest approach to plan and the riskiest to execute. It only works when it is possible to schedule an extended plant shutdown (from several days to weeks), the new system has been exhaustively tested in an environment that faithfully replicates production, and the risk of having to fall back to the old system is acceptable and the rollback is fast. In practice, the big bang applies in sectors where scheduled shutdowns are already planned (annual reviews in pharmaceutical plants, seasonal maintenance in certain food plants) and where the old system is so degraded it cannot even serve as a fallback.

The second strategy is the Strangler Fig pattern, named after a type of fig tree that grows around a host tree until it completely replaces it without felling it. It is the recommended approach in the vast majority of SCADA modernization cases. The principle is simple: the new system is built alongside the old one, not replacing it. It starts with the least critical modules or those most easily isolated. The new module runs in parallel, is validated, and only when it is stable and verified is the old module disabled. The process repeats module by module, area by area, until the old system is completely empty.

The Strangler Fig pattern does not eliminate risk: it distributes it over time and makes it manageable. Every increment is a small, controlled risk. If something goes wrong on a module, the old one is still there. Rollback takes minutes, not weeks.

The third strategy is the pure incremental approach, similar to the Strangler Fig but less structured: new features are added to the new system while the old one continues handling existing functionality. It is useful when the old system does not have the logical module separation needed for a classic Strangler Fig, but still has remaining operational life. You build in parallel, gradually shift traffic, and reach full replacement without a sharp moment of discontinuity.

In all three cases one rule applies: the rollback plan is mandatory and must be tested before the migration even begins. In the industrial world, "we hope it works" is not a strategy. The rollback procedure must be executable in under 30 minutes, documented step by step, and rehearsed in a staging environment.

From VB6 to C#: What Changes in the Architecture and How to Structure the New Project

Migrating from VB6 to C# is not a simple port of the code. It is a rewrite that requires explicit architectural choices, something VB6 did not require at all because architecture was absent by definition. A typical VB6 project is a collection of Forms with scattered code, globals everywhere, and no separation between UI, logic, and data access. The new C# system is the opportunity to do things correctly. Not just better: correctly for the first time.

The most important change is the move from monolithic forms to MVVM with WPF. In VB6, the form was everything: it contained the code that read data, processed it, displayed it, and handled user events. In WPF with MVVM, these roles are explicitly separated. The View is only XAML: it defines the visual appearance without any logic. The ViewModel is a C# class that exposes observable properties through INotifyPropertyChanged, receives data from the infrastructure layer, and transforms it into properties that the View can display through binding. The Model represents domain entities: a Tag, an Alarm, a Setpoint.

This separation is not theoretical: it has immediate practical consequences. The ViewModel can be tested with unit tests without opening any window. The View can be modified or replaced without touching the logic. Two developers can work in parallel on the View and ViewModel without conflicts.

On the data access side, you leave behind DAO and ADO (VB6 database access mechanisms) in favor of Entity Framework Core or Dapper. For configuration and state data, Entity Framework Core with SQLite or SQL Server is the pragmatic choice. For time-series data, meaning continuous readings from sensors, Dapper on TimescaleDB or an InfluxDB client library performs better.

On the PLC communication side, you move away from DCOM and OPC Classic toward OPC-UA. The official OPC Foundation .NET Standard library, available on NuGet as OPCFoundation.NetStandard.Opc.Ua, is the foundation on which to build the communication layer. It reads tags, subscribes to value changes, and writes setpoints, all with a modern, typed, and testable interface.

The recommended structure for a .NET SCADA project has three distinct layers. The Domain layer contains entities like Tag, Alarm, History, and service interfaces. It depends on nothing external: it is pure C# with no references to external libraries. The Infrastructure layer contains the concrete implementations: the OPC-UA client, database repositories, the historization service. The Presentation layer contains the WPF application, ViewModels, and Views. It depends on the domain but not on the infrastructure directly, using interfaces defined in the domain and resolved through dependency injection.

This layered architecture is testable, maintainable over time, and replaceable in parts. If you later want to change the time-series database, you only touch the infrastructure layer. If you want to add a web interface alongside the desktop HMI, you add a new presentation project without touching the domain.

How to Handle OPC Classic During the SCADA Migration

OPC Classic is the previous generation of the OPC protocol: it includes OPC DA (Data Access, for real-time data), OPC HDA (Historical Data Access, for historical data), and OPC A&E (Alarms and Events). It is based on Windows DCOM technology, which makes it functional but difficult to configure, tied to specific Windows versions, and completely incompatible with non-Windows systems or cloud architectures.

Most legacy SCADA systems use OPC Classic as the communication protocol with PLCs. This creates a specific problem during migration: how do you make the new .NET system (which wants to use OPC-UA) talk to PLCs that only expose OPC Classic servers?

The answer is the OPC-UA wrapper. This is a software component installed in the field network that acts as a translator: on the bottom side, it speaks OPC Classic with PLCs and existing OPC servers; on the top side, it exposes the same data as an OPC-UA server. The new .NET system sees only OPC-UA and knows nothing about the underlying legacy protocol. The PLCs are not touched. The field network is not modified. The investment in existing OPC Classic configurations is preserved.

Several market solutions exist for this wrapper. Technosoftware offers solutions that include both client and server components with good support for OPC DA and HDA. Unified Automation is another vendor with a complete suite. Prosys OPC is another valid option, particularly popular in academic environments and mid-sized installations. Some SCADA vendors like Kepware (by PTC) offer solutions that include integrated OPC gateways.

The choice of wrapper depends on three factors: the version of OPC Classic in use (DA, HDA, A&E, or combinations), the number of tags that must pass through the gateway (free or entry-level versions often have limits), and the need for commercial support and SLAs. In a mission-critical industrial context, a vendor with commercial support is almost always preferable to an open-source solution with no continuity guarantees.

There is however a scenario where the wrapper alone is not enough: when the old OPC Classic server is installed on a Windows XP or Windows 7 machine that cannot be updated because it also hosts other critical legacy software. In this case, the wrapper must be installed on a separate machine (even a virtual one) with DCOM configured to access the remote OPC Classic server. It is configurable, but requires careful attention to DCOM permissions, firewalls, and service accounts.

One important note: migrating to OPC-UA is not just a technical decision. It is a strategic one. OPC-UA is the protocol chosen by most modern PLCs as their native interface (Siemens with S7-1500, Beckhoff, Phoenix Contact), by the industrial cloud world (Azure IoT Hub, AWS IoT), and by Industry 4.0 standards. Moving to OPC-UA during a SCADA migration means aligning infrastructure with the future of the sector, not just solving today's problem.

Testing a Legacy SCADA System Without Stopping Production

Testing in an industrial context has constraints that do not exist in traditional software development. You cannot do an experimental production deployment and watch what happens. You cannot accept a 2% error rate as "normal" and correct it on the fly. An error in a SCADA system can mean a stopped line, a missed alarm, or in the worst cases, a physical safety risk for operators.

The first testing tool is the PLC simulator. Instead of connecting the new system directly to the plant's real PLCs, you connect it to a simulator that replicates PLC behavior with synthetic values. Siemens provides PLCSIM for its S7 series. For OPC-UA, test OPC-UA servers exist such as the Prosys OPC UA Simulation Server or the Unified Automation demo server, which expose hundreds of nodes with realistically varying values. This allows development and testing of the communication layer without touching anything in the field.

The second tool is shadow mode testing. Once the new system is sufficiently mature, you connect it to the same PLCs as the old system in read-only mode. The old system continues to operate normally, but the new system reads the same data in parallel. The values read by both systems are compared to detect discrepancies: value differences, update frequency differences, missing tags. This phase is essential to validate that the new communication layer is equivalent to the old one.

The third tool is historical data validation. After migrating a module, you compare the historical series produced by the old and new systems for an overlapping period. If the old system historized a tag at one sample per second, the new one must produce the same frequency. If there are gaps or systematic differences, they must be investigated and resolved before disabling the old module.

The rollback plan deserves a dedicated paragraph. Every migration step must have a defined rollback: which steps to execute to return to the old system in case of problems, in how long, and who is responsible for executing it. The rollback must be tested in staging before every production release. It is not sufficient to say "if things go wrong we go back to the old one": you need to know exactly how, in how long, and verify that it works.

A complementary approach is defining specific acceptance criteria for every migrated module. Not "the module works" but "the module reads all 47 expected tags at a frequency of no less than 500ms, produces no connection errors for more than 2 consecutive seconds, and correctly historizes values with accurate timestamps." These criteria must be automatically verifiable, with integration tests running in a staging environment that replicates the field network.

The historical data of a SCADA system is often its most valuable and most neglected asset. Years or decades of sensor readings, alarm events, operator actions: this data serves for performance analysis, predictive maintenance, regulatory compliance auditing (especially in pharmaceuticals), and debugging recurring problems. Losing it during a migration is not acceptable.

The first problem is format. Legacy systems use proprietary Historians with undocumented formats: WonderWare InSQL (today AVEVA System Platform) uses SQL Server as a backend but with a proprietary schema. OSIsoft PI (today AVEVA PI) uses an optimized binary database. Citect uses proprietary files. Some older systems use Access databases or CSV files generated periodically.

Where a SQL Server backend exists, migration is relatively manageable: you analyze the schema, write queries to extract data with timestamps, values, and tag names, and write import scripts for the new time-series database. Where the format is completely proprietary, you use the legacy system's APIs to export data in a standard format (CSV, JSON) before importing it into the new system.

The choice of target time-series database depends on context. InfluxDB is the most widespread in the open-source IoT and SCADA world: it has a specific query language (InfluxQL, then Flux), well-supported .NET clients, and good performance for time-range queries. TimescaleDB is a PostgreSQL extension that adds time-series capabilities: the ideal choice for teams that want time-series database performance with the familiarity of SQL. SQL Server with partitioning is the pragmatic choice for those who want to stay in the Microsoft ecosystem and already have internal SQL Server expertise.

Historical data migration is not a one-time operation: it is a process to be planned in phases, validated with samples, and documented to guarantee data integrity. A common practice is to migrate the most recent data (last 12 months) first, validate it, and then proceed with older data in the background while the new system is already in production.

A specific problem in historical data migration is timestamp normalization. Legacy systems often historize data with the local SCADA server timestamp, which may have offset from UTC, drift over time, or gaps caused by system restarts. Before importing data into the new system, timestamps must be normalized, gaps identified, and documented. Gaps must not be filled with interpolated values: they must be left as-is and documented as acquisition gaps, not as absence of variation.

In the pharmaceutical sector and other regulated industries, historical data migration also requires an audit trail: documentation proving that migrated data is identical to the original data, with digital signature or hash of both the original and migrated datasets. This is an FDA 21 CFR Part 11 requirement that cannot be ignored if you want to maintain system validation.

Career Opportunities in SCADA Modernization: Why This Market Is Worth It

Italy is one of the most important manufacturing countries in Europe. The automotive district of Northern Italy, the pharmaceutical clusters of Lazio and Lombardy, the food and beverage sector of the North-East, the textile and mechanical districts of Tuscany and Emilia: all these sectors have plants with SCADA systems, and a significant percentage of these plants still run on technology from the 1990s or early 2000s.

The structural problem is that the developers who knew how to work on those systems are leaving the labor market through retirement and are not being replaced. The new generation of developers does not know VB6, Delphi, or industrial protocols. Companies find themselves in an uncomfortable position: they need to modernize their systems, but cannot find the skills to do it.

This gap creates a concrete opportunity for a C# developer with WPF skills who decides to specialize in the industrial world. You do not need to become an automation engineer. You do not need to know how to program PLCs. You need to know how to build solid .NET applications, understand the basic concepts of industrial systems (what a tag is, how OPC-UA works, what historization means), and be able to work in contexts where the tolerance for failure is much lower than in web development.

Compensation ranges for these profiles in the Italian market: a junior C# developer entering a software house specialized in industrial SCADA systems starts at 28,000-35,000 euros. A mid-level profile with 3-5 years of experience, WPF skills, OPC-UA knowledge, and experience managing migration projects sits between 45,000 and 65,000 euros. A senior or lead developer with documented experience in real-plant migrations exceeds 70,000 euros, with consulting profiles reaching 100-130 euros per hour as freelancers. These numbers are significantly higher than a pure C# developer in traditional application development, at the same seniority level.

The sectors with the highest demand in Italy in 2026 are four. The pharmaceutical sector is the most demanding in terms of quality and compliance, but it also pays the most and has the greatest urgency for modernization to comply with FDA and EMA regulations. The automotive sector (particularly the supply chain working for major OEMs) has a massive backlog of plants to modernize and relatively available budgets. Food and beverage plants are individually smaller but numerous and geographically distributed across the national territory. The utilities sector, water and energy, has extreme operational continuity requirements and public budgets that are allocated on a regular schedule for infrastructure modernization.

There is an additional competitive advantage for those entering this sector today: the entry barrier is high not because of technical difficulty, but because of the combination of skills required. Finding a developer who can write solid C# code, knows WPF, understands industrial systems, and is not intimidated by a production plant is difficult. Those who build this combination today become difficult to replace.

How to Train to Become the SCADA Developer Companies Are Looking For

The good news is that the path to entering this market is more accessible than it appears, if you start from a solid C# foundation. You do not need years of automation engineering study. You need specific skills, built in the right order, with the right level of depth.

The first level is solid C#. This does not mean knowing every design pattern or having experience with every .NET framework. It means writing clean code, understanding memory management, knowing how to use async/await correctly (essential for SCADA applications that must handle hundreds of concurrent connections), and knowing the SOLID principles well enough to apply them without pedantry. If there are doubts about these fundamentals, that is the first place to invest.

The second level is WPF and MVVM. WPF is not a dead technology: in the industrial desktop world it is the de facto standard for HMI interfaces on Windows. Knowing how to build a WPF interface with data binding, templates, triggers, and the MVVM pattern implemented correctly is a highly sought and relatively rare skill among younger developers who have grown up with the web.

The third level is OPC-UA. You do not need to become an expert in the protocol in detail. You need to know how to use the OPC Foundation .NET library to connect an OPC-UA client to a server, read nodes, subscribe to value changes, and write values. This can be learned in a few days of practice on a test OPC-UA server. Conceptual understanding of the OPC-UA node model and subscription mechanisms comes with practice.

The fourth level is knowledge of time-series databases. InfluxDB or TimescaleDB: how to insert high-frequency data, how to query time ranges, how to manage data retention. This too is learned practically, by running a local instance and writing insertion and query code.

The fifth level, the one that makes the difference in a legacy migration context, is knowledge of the legacy systems themselves. You do not need to know how to write VB6. You need to be able to read VB6 code well enough to understand what it does, extract business logic, and identify integration points with PLCs. This is acquired by reading real legacy code during migration projects, or by finding open-source VB6 examples to practice on.

The ideal path for someone starting from zero C# knowledge who wants to enter the industrial market in 12-18 months: three months on advanced C# and WPF/MVVM, two months on OPC-UA and time-series databases, then a personal project that simulates a complete SCADA system (WPF HMI, connection to a simulation OPC-UA server, historization on InfluxDB). This project, with code on GitHub, is worth more than any certification.

Structured training significantly accelerates this path. A course with a tutor who knows both the .NET world and the industrial world reduces learning time and, most importantly, avoids building skills the wrong way: architectures that do not scale, dependencies that make code untestable, OPC-UA communication patterns that work in the lab but fail on a real plant with hundreds of tags.

Frequently asked questions

The cost of a SCADA modernization depends on the system size (number of tags, PLCs, complexity of logic) and the chosen strategy. An incremental approach using the Strangler Fig pattern spreads the investment over time while reducing risk. However, the cost of NOT modernizing is often higher: one hour of production downtime at a medium-sized manufacturing plant costs between 5,000 and 50,000 euros, depending on the sector.

Yes, and it is the recommended approach. The Strangler Fig pattern allows the new .NET system to run alongside the legacy system, reading the same data in parallel. Modules are replaced one at a time, using short and controlled maintenance windows. The plant is never completely stopped: you proceed area by area or feature by feature, validating each increment before moving forward.

Old SCADA systems use OPC Classic (OPC DA/HDA), based on Windows DCOM technology. OPC-UA wrapper servers expose OPC Classic data as if they were OPC-UA servers, allowing the new .NET system to communicate using the modern protocol without touching the PLCs. Libraries such as Technosoftware OPC UA SDK and Unified Automation enable this setup. It is the most pragmatic solution for preserving the existing investment in PLCs.

The recommended architecture uses separate layers: a domain layer with entities and business rules (tags, alarms, setpoints), an infrastructure layer for OPC-UA communication and data persistence, and a presentation layer using WPF with the MVVM pattern. This separates concerns and makes the system testable and maintainable over time, unlike the monolithic forms typical of VB6.

Historical data migration requires dedicated scripts that read from the legacy Historian (WonderWare InSQL, OSIsoft PI, or proprietary databases) and write to the new time-series database (InfluxDB, TimescaleDB). Preserving original timestamps and validating data continuity before and after migration is essential. Data gaps must be documented, not fabricated.

The most sought-after profile is a C# developer with WPF experience and a basic understanding of industrial systems (OPC-UA protocols, the concept of tags, interaction with PLCs). You do not need to be an automation engineer: companies are looking for developers capable of writing solid .NET code and working alongside the OT world. Those who hold both skill sets are rare and well compensated, with packages that easily exceed 50,000 euros annually even at a mid level.

Leave your details in the form below

Matteo Migliore

Matteo Migliore is an entrepreneur and software architect with over 25 years of experience developing .NET-based solutions and evolving enterprise-grade application architectures.

Throughout his career, he has worked with organizations such as Cotonella, Il Sole 24 Ore, FIAT and NATO, leading teams in developing scalable platforms and modernizing complex legacy ecosystems.

He has trained hundreds of developers and supported companies of all sizes in turning software into a competitive advantage, reducing technical debt and achieving measurable business results.

Stai leggendo perché vuoi smettere di rattoppare software fragile.Scopri il metodo per progettare sistemi che reggono nel tempo.