OpenGL MFC AppWizard
一篇展示如何在 Visual Studio .NET 2008 中为 OpenGL 应用程序创建自定义 AppWizard 的文章
引言
在这篇文章中,我将向您展示如何创建一个简单的 Visual Studio 2008 AppWizard。我将创建的 AppWizard 将设置一个基本的 OpenGL 应用程序。此 AppWizard 只有一个可配置属性,该属性决定是否渲染轴和网格。但是,一旦您阅读了这段代码,您应该就能添加自己的属性了!
我最基本的 OpenGL 应用程序,名为 'OpenGLApplication
',您可以从页面顶部的链接下载,它是 AppWizard
的来源。AppWizard
的目的是生成与此应用程序非常相似的项目。
背景
我编写 OpenGL 已经很多年了,但从来没有 bother 过 AppWizard——网上似乎有太少的信息了。我终于下定决心,决定深入研究。这是代码的第一个版本,所以如果有什么问题,请留下大量评论——我会定期更新代码。
应用程序本身非常简单——它是一个 MFC 应用程序,没有文档/视图架构。实际上,这意味着根本没有文档——仍然有一个视图。代码的 OpenGL 部分现在已经有几年历史了。有三个类支持 OpenGL
CDIBSurface
- 这是一个支持渲染到 DIB 表面的类。COpenGLSurface
- 派生自CDIBSurface
,它只是控制一个渲染上下文。COpenGLView
- 这是一个CView
派生类。它包含一个COpenGLSurface
。它通过 OpenGL 调用进行渲染,并在OnDraw
函数中将其绘制到屏幕上。
通过重写 DoOpenGLDraw
和 DoOpenGLResize
函数,您可以进行 OpenGL 渲染并设置视口。如果您想查看原始 OpenGL 应用程序,可以下载它。
创建 AppWizard
创建一个新的 Visual C++ AppWizard
项目。在我最初的项目中,我选择了创建五个属性页——这有点过于乐观,后来我将其减少到一个!
现在,第一件事是复制您将在新项目中包含的所有文件。在我的例子中,是来自我的 OpenGLApplication
项目的所有文件。将所有这些文件复制到*模板文件*文件夹中。然后将它们添加到项目中。模板文件是将被添加到基于此 AppWizard
的任何项目中的文件。现在打开 'Templates.inf' 文件,并确保所有文件都已列出,如下所示
readme.txt ChildView.cpp ChildView.h DIBSurface.cpp DIBSurface.h MainFrm.cpp MainFrm.h
OpenGLApplication.cpp OpenGLApplication.h OpenGLApplication.rc OpenGLSurface.cpp
OpenGLSurface.h OpenGLView.cpp OpenGLView.h Resource.h stdafx.cpp stdafx.h
targetver.h res\OpenGLApplication.ico res\OpenGLApplication.rc2 res\Toolbar.bmp
这是一组相当标准的 MFC 项目文件。完成此操作后,您就可以进行第一次测试了。
第一次测试
编译项目并打开 Visual Studio 的新实例。基于 AppWizard
创建一个新项目。它应该成功创建项目并将所有模板文件复制到其中。即时问题一——我们没有头文件或资源文件文件夹。现在来解决这个问题——首先,我们将打开 default.htm 文件并找到 'SOURCE_FILTER
' 字符串
。让我们添加一些行
<SYMBOL NAME='SOURCE_FILTER' TYPE=text
VALUE='cpp;c;cxx;def;odl;idl;hpj;bat;asm'></SYMBOL>
<SYMBOL NAME='HEADER_FILTER' TYPE=text VALUE='h;hpp;hxx;hm;inl;inc'></SYMBOL>
<SYMBOL NAME='RESOURCE_FILTER'
TYPE=text VALUE='rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe'>
</SYMBOL>
在这里,我们定义了新项目中将有哪些文件夹,以及哪些类型的文件将放入其中。但我们还没有完成,打开 default.js 文件并找到 AddFilters
函数——让它看起来像这样
function AddFilters(proj)
{
try
{
// Add the Source Files group and set the filter.
var group = proj.Object.AddFilter('Source Files');
group.Filter = wizard.FindSymbol('SOURCE_FILTER');
// Add the Header Files group and set the filter.
var group = proj.Object.AddFilter('Header Files');
group.Filter = wizard.FindSymbol('HEADER_FILTER');
// Add the Source Files group and set the filter.
var group = proj.Object.AddFilter('Resource Files');
group.Filter = wizard.FindSymbol('RESOURCE_FILTER');
}
catch(e)
{
throw e;
}
}
这就是创建正确文件夹并将正确文件自动放入其中的全部内容。构建项目并创建一个基于此 AppWizard
的新项目——现在您应该看到文件已放入文件夹中,就像在原始应用程序中一样。
自定义文件名
在上面显示的文件集中,我们希望其中一些文件以项目命名。例如,View
类将需要像 MyNewProjectView.h 和 MyNewProjectView.cpp 这样的文件名,而不是简单地 OpenGLApplicationView
。我们将深入研究 default.js 文件来处理这个问题。让我们看看 GetTargetName
函数。
function GetTargetName(strName, strProjectName)
{
try
{
// Here we can create custom filenames.
var strTarget = strName;
if (strName == 'readme.txt')
strTarget = 'ReadMe.txt';
// The ChildView class becomes the ProjectNameView class.
if (strName == 'ChildView.cpp')
strTarget = strProjectName + 'View.cpp';
if (strName == 'ChildView.h')
strTarget = strProjectName + 'View.h';
// The COpenGLApplication class becomes the ProjectNameApp
// class (but no 'App' in the headers!).
if (strName == 'OpenGLApplication.cpp')
strTarget = strProjectName + '.cpp';
if (strName == 'OpenGLApplication.h')
strTarget = strProjectName + '.h';
if (strName == 'OpenGLApplication.rc')
strTarget = strProjectName + '.rc';
if (strName == 'res\\OpenGLApplication.rc2')
strTarget = 'res\\' + strProjectName + '.rc2';
if (strName == 'res\\OpenGLApplication.ico')
strTarget = 'res\\' + strProjectName + '.ico';
return strTarget;
}
catch(e)
{
throw e;
}
}
这里发生了什么?嗯,对于放入新创建项目的每个文件,我们都可以使用此函数来修改名称。例如,通过检查 OpenGLApplication.cpp 文件,我们可以将文件名更改为 MyNewProject.cpp 或任何我们想要的名称。但是现在,我们遇到了一个新问题——那我们之前在 '#include "OpenGLApplication.h"' 中使用的地方怎么办?我们需要在源文件中找到所有将文件名的一部分替换为项目名称的实例,并像这样更新它,这是修改之前的样子
#include "stdafx.h"
#include "OpenGLApplication.h"
#include "MainFrm.h"
这是修改后的代码
#include "stdafx.h"
#include "[!output PROJECT_NAME].h"
#include "MainFrm.h"
[!output PROJECT_NAME]
将被用户选择的项目名称替换。这时事情开始变得棘手——我的 AppWizard
例如更改了六个文件的名称。它还更改了两个类的名称——如下所示
class C[!output PROJECT_NAME]View : public COpenGLView
{
public:
// Constructor / Destructor.
C[!output PROJECT_NAME]View();
virtual ~C[!output PROJECT_NAME]View();
所以您必须仔细检查模板文件中的代码,并确保您已正确更新所有内容!如果您还要更改图标等资源文件,您需要查看 rc 文件,甚至可能查看 rc2 文件!这里一个好的做法是同时打开另一个 Visual Studio 实例,并不断尝试构建项目。暂时不用太担心奇怪的链接器错误——只要确保生成的代码没有明显错误。
设置配置
现在我们遇到了下一个问题——项目无法构建。一方面,它不知道链接到 OpenGL32.lib 和 glu32.lib——但进一步调查表明,构建参数都是默认的——没有 MFC,没有调试信息,什么都没有!查看 default.js 中的 AddConfig
函数。在这里您可以更改构建配置。让我们让它看起来像这样
function AddConfig(proj, strProjectName)
{
try
{
// Create the debug settings.
var config = proj.Object.Configurations('Debug');
config.IntermediateDirectory = '$(ConfigurationName)';
config.OutputDirectory = '$(SolutionDir)$(ConfigurationName)';
config.CharacterSet = charSet.charSetUnicode;
config.useOfMfc = useOfMfc.useMfcDynamic;
// Compiler.
var CLTool = config.Tools('VCCLCompilerTool');
CLTool.UsePrecompiledHeader = pchOption.pchCreateUsingSpecific;
CLTool.PrecompiledHeaderThrough = "StdAfx.h";
CLTool.PrecompiledHeaderFile = '$(IntDir)\$(TargetName).pch';
CLTool.MinimalRebuild = true;
CLTool.BasicRuntimeChecks = basicRuntimeCheckOption.runtimeBasicCheckAll;
CLTool.WarningLevel = warningLevelOption.warningLevel_3;
CLTool.PreprocessorDefinitions = "WIN32;_WINDOWS;_DEBUG";
CLTool.DebugInformationFormat = debugEditAndContinue;
CLTool.Optimization = optimizeDisabled;
CLTool.RuntimeLibrary = runtimeLibraryOption.rtMultiThreadedDebugDll;
// Add linker dependencies.
var LinkTool = config.Tools('VCLinkerTool');
LinkTool.AdditionalDependencies = "opengl32.lib glu32.lib";
LinkTool.LinkIncremental = linkIncrementalType.linkIncrementalYes;
LinkTool.GenerateDebugInformation = true;
LinkTool.ProgramDatabaseFile = "$(TargetDir)$(TargetName).pdb";
LinkTool.SubSystem = subSystemOption.Windows;
// Create the release settings.
config = proj.Object.Configurations('Release');
config.IntermediateDirectory = '$(ConfigurationName)';
config.OutputDirectory = '$(SolutionDir)$(ConfigurationName)';
config.CharacterSet = charSet.charSetUnicode;
config.useOfMfc = useOfMfc.useMfcDynamic;
// Compiler - set the precompiled header settings.
var CLTool = config.Tools('VCCLCompilerTool');
CLTool.UsePrecompiledHeader = pchOption.pchCreateUsingSpecific;
CLTool.PrecompiledHeaderThrough = "StdAfx.h";
CLTool.PrecompiledHeaderFile = '$(IntDir)\$(TargetName).pch';
CLTool.WarningLevel = warningLevelOption.warningLevel_3;
CLTool.Optimization = optimizeOption.optimizeMaxSpeed;
CLTool.EnableIntrinsicFunctions = true;
CLTool.PreprocessorDefinitions = "WIN32;_WINDOWS;NDEBUG";
CLTool.RuntimeLibrary = runtimeLibraryOption.rtMultiThreadedDll;
// Add linker dependencies.
var LinkTool = config.Tools('VCLinkerTool');
LinkTool.AdditionalDependencies = "opengl32.lib glu32.lib";
LinkTool.LinkIncremental = linkIncrementalType.linkIncrementalNo;
LinkTool.SubSystem = subSystemOption.Windows;
}
catch(e)
{
throw e;
}
}
真麻烦。经过大量实验,这应该可以完美地设置调试和发布模式下的编译和链接。再次测试 AppWizard
——您现在应该能够构建和运行项目了。
自定义 App Wizard
到目前为止,App Wizard 应该可以工作了——也就是说,它能生成一个可用的应用程序。现在就看您自己去处理代码中的 TODO 项,并填入您的 AppWizard
名称、描述等。您也可以更改图像。
我做的最后一件事是将 AppWizard 生成的“示例 1 复选框
”更改为“渲染网格和轴”。其符号被更改为 CHECKBOX_EXAMPLE_RENDERING
。这可以在代码中条件性地使用,如下面的 ChildView.cpp 文件所示
void C[!output PROJECT_NAME]View::DoOpenGLDraw()
{
[!if CHECKBOX_EXAMPLE_RENDERING]
// Here we'll do a little bit of example rendering, by drawing a cube.
// Set the clear colour to black and clear the colour buffer.
glClearColor(0, 0, 0, 1);
// ...etc etc...
[!else]
// Do your OpenGL drawing here. Don't forget to call glFlush at the end!
[!endif]
}
这里没什么太花哨的,根据 复选框
的状态,我们会得到一些示例绘图代码,或者只是一个提醒用户添加自己的代码的注释。
试试看
这相当直接,但关于 AppWizard
的良好文档很少,特别是对于 Visual Studio .NET 2008。在这个早期阶段,您可能会在不同机器上遇到此代码的问题——但请告诉我,我会及时修复。
更新
更新 CodeProject 上的代码还需要一段时间。在此期间,我在我的一个网站上放了一个小页面来存储这段代码——因此更新将比在线上的 CodeProject 提前在 SharpGL - Other Resources 上提供。
历史
- 2009 年 1 月 14 日 - 文章第一版撰写