10

对于使用 Visual Studio 6 用 C++ 编写的大型应用程序,进入现代时代的最佳方式是什么?

我想采取一种渐进的方法,我们慢慢地移动部分代码并将新功能写入 C#,例如并将其编译成可以从遗留应用程序引用的库或 dll。

这可能吗?最好的方法是什么?

编辑:在这一点上,我们仅限于 Express 版本,我认为不允许使用我们当前应用程序中大量使用的 MFC 库。它也是一个相当大的应用程序,具有很多硬件依赖项,所以我认为不会进行大规模迁移。

Edit2:我们已经研究过用 C# 编写 COM 包装的组件,但是没有 COM 经验,这既可怕又复杂。是否可以生成一个带有直接 C 接口的 C# dll,其中隐藏了所有托管的优点?还是 COM 是必要的邪恶?

4

10 回答 10

12

我想采取一种渐进的方法,我们慢慢地移动部分代码

这是唯一现实的方法。

首先,你使用什么样的版本控制?(如果您使用允许您进行实验并查看哪些工作有效的分支版本控制,同时最大限度地减少损害您的代码的风险;其他人也可以,但您必须非常小心,具体取决于您使用的内容)。

编辑:我刚刚看到你正在使用 SVN。如果您有自由这样做,那么迁移到 mercurial 或 git 可能是值得的(更改为您可以使用代码库做的事情提供了巨大的飞跃)。

例如,将新功能写入 C# 并将其编译到可以从旧应用程序引用的库或 dll 中。

那……不一定是个好主意。C# 代码可以公开可在 C++ 中访问的 COM 接口。用 C++ 为用 C# 编写的模块编写客户端代码可能很有趣,但您可能会发现它很费力(就工作收益比而言);它也很慢且容易出错(与为用 C++ 编写的模块编写 C# 客户端代码相比)。

最好考虑用 C# 创建一个应用程序框架,并使用用 C++ 编写的模块(已经)来实现核心功能。

这可能吗?最好的方法是什么?

是的,这是可能的。

有多少人参与了这个项目?

如果有很多,最好的方法是让几个(两个?四个?)在新的应用程序框架上工作,其余的照常继续。

如果很少,您可以考虑让一个人负责,或者让更多的人兼职。

分配给每个(旧代码维护和新代码开发)的人员/工作量的百分比应取决于团队的规模和您的优先级(转换是低优先级问题吗?是否必须在给定日期之前完成?)

做到这一点的最好方法是开始调整代码模块以在多个场景中使用(旧代码和新代码)并继续并行开发(同样,这将通过使用分支分布式版本控制系统)。

以下是我的做法(迭代开发,中间有小步骤和大量有效性检查):

  1. 在旧代码库中选择一个功能模块(与 GUI 无关的东西)。

  2. 从步骤 1 中选择的模块中删除 MFC 代码(以及 VS2010 Express 中不可用的其他库 - 如 ATL)引用。

    不要尝试用自定义代码重写 MFC/ATL 功能,除非进行小的更改(也就是说,决定创建自己的 GUI 框架是不可行的,但可以决定编写自己的 COM 接口指针包装器,类似于ATL 的 CComPtr)。

    如果代码严重依赖库,最好尽可能将其分离,然后将其标记为将来使用新技术重写。无论哪种方式,对于一个严重依赖于 MFC 的库,你最好使用其他东西(C#?)重写代码。

  3. 尽可能减少与所选模块的耦合(确保代码在单独的库中,明确决定模块向客户端代码公开哪些功能)并仅通过确定的公开接口(在旧代码中)访问分隔功能。

  4. 确保旧代码库仍然适用于修改后的模块(测试 - 最终自动化此模块的测试) -如果您需要在发布新版本之前仍留在市场上,这一点至关重要。

  5. 在维护当前应用程序的同时,启动一个新项目(基于 C#?),实现 GUI 和您需要现代化的其他部分(如严重依赖 MFC 的部分)。这应该是一个薄层应用程序,最好与业务逻辑无关(应尽可能保留在遗留代码中)。

    根据旧代码的功能和您定义的接口,对部分代码使用 C++/CLI 而不是 C# 可能是有意义的(它可以使用本机 C++ 指针和托管代码,允许您在以下情况下轻松转换在托管 .NET 代码和 C++ 本机代码之间进行通信)。

  6. 使新应用程序使用在步骤 1 中选择的模块。

  7. 选择一个新模块,返回步骤 2。

优点:

  • 将执行重构(模块分离所必需的)

  • 最后,您应该对功能模块进行一系列测试(如果您还没有)。

  • 你还有一些东西要运送。

几点注意事项:

  • 如果你不使用分布式分支版本控制系统,你最好一次只处理一个模块。如果您使用分支/分布式源代码控制,您可以将不同的模块分发给不同的团队成员,并在每次移植新内容时集中更改。

  • 清楚地划分每个步骤非常重要(这样您就可以将更改回滚到上一个稳定版本,尝试新事物等等)。这是另一个在 SVN 中难以解决而在 Mercurial / Git 中容易解决的问题。

  • 在开始之前,将所有项目文件的名称更改为具有 .2005.vcproj 扩展名,并对解决方案文件执行相同操作。创建新项目文件时,对项目文件和解决方案使用 .2010.vcxproj 执行相同操作(如果转换解决方案/项目,您仍应执行此操作)。这个想法是你应该在任何时候同时拥有并行和开放的任何你想要的东西。您不必为了切换 IDE 而在源代码管理中对不同的标签/标签/日期进行源代码树更新。

Edit2:我们已经研究过用 C# 编写 COM 包装的组件,但是没有 COM 经验,这既可怕又复杂。

您仍然可以通过编写包装器代码来做到这一点(例如,用于 COM 接口的小型模板智能指针类不会出错 - 类似于 ATL 中的 CComPtr)。如果您将 COM 代码隔离在一些包装器后面,您可以编写客户端代码(与 COM 无关)而(几乎)没有问题。

是否可以生成一个带有直接 C 接口的 C# dll,其中隐藏了所有托管的优点?还是 COM 是必要的邪恶?

从来没听说过。如果您打算使用用 C# 编写的服务器代码和用 C++ 编写的客户端代码,我认为 COM 将是一个必要的邪恶。

反过来也是可能的。

于 2010-06-15T13:38:41.933 回答
6

面对同样的任务,我的策略是这样的:

  1. 确定我们希望通过 2010 年开发获得什么——它可能是

    • 改进的质量保证:单元测试、模拟是现代开发工具的一部分
    • 更流畅的 UI:WPF 提供了现代的外观和感觉。
    • 生产力:在某些领域,.NET 开发比 C++ 开发更有生产力
    • 支持:通过改进和错误修复支持新工具。
  2. 确定系统的哪些部分不会从迁移到 C# 中受益:

    • 硬件访问,低级算法代码
    • 几乎大多数定制的非 UI 工作代码 - 如果它已经工作,就没有必要把它扔掉
  3. 确定系统的哪些部分需要迁移到 c#。对于这些部分,请确保 C++ 中的当前实现是解耦和模块化的,以便可以交换这些部分。如果应用程序是单体应用程序,则需要进行大量工作来重构应用程序,以便可以将其分解并选择在 c# 中重新实现的部分。(可以什么都不重构,而只专注于在 c# 中实现新的应用程序功能。)

  4. 既然您已经确定了哪些部分将保留在 C++ 中,哪些部分将在 c# 中实现,(或者只是规定新功能在 c# 中),那么重点转向如何将 c# 和 c++ 集成到一个解决方案中

    • 使用 COM 包装器——如果您现有的 C++ 项目充分利用了 OO,那么这通常并不像看起来那么困难。使用 MSVC 6,您可以使用 ATL 类将您的类公开为 COM 组件。
    • 直接集成原生代码和 c# 代码。集成“遗留”编译代码需要一个中间 DLL - 请参阅此处了解详细信息。

混合 MFC UI 和 c# UI 可能无法实现,也不建议使用,因为它会产生两种不同风格(1990 年代灰色和 2010 年氛围)的 UI 组合。专注于实现增量迁移更简单,例如在 C# 中实现新的应用程序代码并从本机 C++ 代码调用它。这使迁移的 c# 代码量从一开始就很小。随着您对 2010 开发的深入了解,您可以采用无法增量迁移的较大块,例如 UI。

于 2010-06-15T01:35:56.793 回答
4

首先,你对现代的定义是有争议的。没有理由认为 C# 在任何意义上都比 C++ 更好。关于 C# 是否可以帮助您更好地避免内存管理错误,已经说了很多,但是对于 C++ 中的现代设施,这几乎不是这样,而且,在资源获取时间方面很容易与 C# 混淆,这可能取决于什么其他程序正在做。

于 2010-06-11T19:44:20.180 回答
3

如果您直接从 6 移动到 2010,您最终可能会遇到一些混乱的项目设置。如果这不是一个相当大的项目,并且它是您需要转换的少数项目之一,那应该没问题。只需在 2010 年打开它,然后按照转换向导进行操作即可。确保首先备份您的项目,并在完成后验证您的项目设置。

在我看来,最好的方法是通过 Visual Studio 的每次迭代逐步转换它。从 2003 年到 2010 年,我必须对 1400 个项目进行现代化改造,我发现最好的方法是将所有项目都转换为 2005 年,然后是 2008 年,最后是 2010 年。这给我带来的问题最少。

如果您只有 6 个和最新的 Visual Studio,您可能最终只需要尝试使用向导直接进入新的。在一切再次为您正确构建之前,请进行一些手动清理。

另外,再一次,首先备份它!:)

于 2010-06-02T14:39:36.840 回答
3

高级 C++ 代码调用低级 C# 代码看起来不是一个好主意。.NET 语言更好的领域是用户界面、数据库访问、网络、XML 文件处理。计算、硬件访问等低级内容最好保留为本机 C++ 代码。

迁移到 .NET,在大多数情况下,最好使用 WPF 或 Windows 窗体技术完全重写 UI。低级的东西仍然是原生的,并且使用不同的互操作性技术来连接 C# 和原生代码:PInvoke、C++/CLI 包装器或 COM 互操作性。一段时间后,您可能会决定在 C# 中重写低级本机组件,除非确实有必要。

关于在 VS2010 中编译本机 C++ 代码 - 我没有看到任何问题。只需修复所有编译错误 - 新编译器具有更严格的类型检查和语法限制,并在编译时捕获更多错误。

于 2010-06-02T14:59:19.340 回答
3

不知道为什么这么多人提倡 COM。如果您还没有大量的 COM,那么学习如何在 C++ 端进行操作将会受到伤害,然后您会从托管端使用最慢的互操作。不是我的第一选择。

理想情况下,您已经从业务逻辑重构了 UI。然后,您可以构建一个新的 UI(WPF、WinForms、ASP.NET、支持其他客户端的 Web 服务等)并通过 P/Invoke 或编写 C++/CLI 包装器调用您的业务逻辑。假设重构是可能的,@mdma 为您提供了很好的建议。

但是,如果您付钱让我进来帮助您,我的第一个问题就是您为什么要这样做?一些客户说他们不想再付钱给 C++ 开发人员,所以他们希望所有的 C++ 代码都消失。这是一个可怕的目标,因为我们都讨厌接触有效的代码。一些客户希望将他们的逻辑暴露给 ASP.NET 或 Reporting Services 之类的东西,所以对于他们,我们专注于重构。有人说“它看起来像 1999 年”,我向他们展示了 MFC 现在的样子。颜色、皮肤/主题,包括 office 和 win7 外观、功能区、浮动/停靠窗格和窗口、Windows 7 任务栏集成......如果你只是想看起来不同,看看 VS 2010 中的 MFC,你可能不需要调整任何代码。

最后,为了让非 Express 版本的 VS 2010 价格实惠,请查看 Microsoft 合作伙伴计划。如果您已将您的软件出售给至少 3 位仍在与您交谈的客户,并且可以通过 Windows 7 徽标自测(我在一两天内获得了 VB 6 应用程序),那么您可以拥有 5-10 份一年 1900 美元左右的一切(Windows、Office、VS),取决于你住在哪里。

于 2010-06-17T11:54:20.393 回答
2

首先,我会尝试保留尽可能多的代码以避免重写。在开始转换之前,我还会删除所有未使用的代码。

自 VC++ 6.0 起,Microsoft 更改了 MFC 库和 C++ 标准库。

我建议开始构建没有依赖关系的 DLL,然后查看第三方库,然后一次重建一个依赖的 DLL/EXE。

引入单元测试以确保代码的行为不会改变。

如果您使用不同版本的 VC++ 进行混合构建,则需要防止在使用不同版本的 VC 运行时的 DLL 之间传递资源(文件句柄)。

于 2010-06-02T15:04:34.023 回答
2

如果在财务上可能的话,我会强烈考虑只为您需要的 Visual Studio 版本付费,因为您很可能会在花费的时间上损失更多的钱。我对特快版本的了解不够,无法对它们给出一个好的答案,但是当集成来自用 C++ 编写的分包商的一些代码时,我使用了 C++/CLI。您可能能够重用大部分代码库并熟悉该语言,但您也可以访问托管代码和库。此外,如果您想开始用 C# 编写新代码,您也可以这样做。我遇到的最大问题是,在 VS 2010 中,C++ / CLI 中没有智能感知。

于 2010-06-17T11:37:07.920 回答
1

Visual Studio 6 以错误和缓慢而著称。进入现代时代最好通过获得一个新的编译器来完成。可能最简单的做法是将遗留应用程序写入 DLL,然后将 exe 写入 C# 并使用 P/Invoke。然后你再也不用接触旧代码了——你可以用 C# 编写越来越多的代码,而使用越来越少的旧 DLL。

如果您的旧代码非常严重 OO,您可以使用 C++/CLI 编写允许 .NET 调用 C++ 对象上的方法的包装类,如果您使用引用计数智能指针,也可以收集它们。

于 2010-06-02T14:44:55.147 回答
0

您可以使用 C# 编写带有 COM 或 COM+ (System.EnterpriseServices) 包装器的新组件,这些包装器可以从您现有的 C++ 代码中调用。

于 2010-06-02T14:46:59.197 回答