机电网站模板,织梦网站如何做伪静态,莫企业网站建设方案,购物网站模板html移除 DllMain() 并成功重新编译
以下是对内容的详细复述与总结#xff1a; 问题和解决方案#xff1a; 在编译过程中遇到了一些问题#xff0c;特别是如何告知编译器不要退出程序#xff0c;而是继续处理。问题的根源在于编译过程中传递给链接器的参数设置不正确。原本尝试…移除 DllMain() 并成功重新编译
以下是对内容的详细复述与总结 问题和解决方案 在编译过程中遇到了一些问题特别是如何告知编译器不要退出程序而是继续处理。问题的根源在于编译过程中传递给链接器的参数设置不正确。原本尝试将一个“斜杠”后跟“l”的参数传递给链接器但实际上这是一个编译器的选项而不是链接器的。因此正确的做法是将该选项作为编译器的开关传递而不是链接器。经过这个调整问题得以解决。 不再需要的操作 在编译和链接过程中某些操作被发现是多余的。例如在 game.cpp 文件的底部曾插入了一个 DllMain 函数。这通常是 Windows 系统加载程序时会调用的函数用来检查程序是否正常启动。经过检查发现该 DLLMain 函数不再需要因此决定删除它。 构建目录的整理 在完成以上调整后构建目录得到了整理。对于可能是指构建或处理某些文件这一部分开发者指出它仍然需要存在但某些不必要的部分已经被清除。例如某些与可能是指某个不必要的工具或文件相关的东西不再需要因此被移除。 后续步骤 在进行这些调整后接下来的目标是进一步优化和清理当前的构建设置确保没有多余的内容或文件存在所有操作都与当前的需求保持一致。
总结来说调整主要集中在编译过程的参数设置以及不再需要的代码删除上目标是优化构建流程并清理不必要的部分使得整个开发环境更加简洁和高效。
演示热重载并决定减少更新延迟并启用调试
以下是对内容的详细复述和总结 当前进展 昨天的工作使得项目达到了一个相当不错的位置开发者能够在游戏运行时直接修改游戏行为。例如可以在游戏中实时改变颜色并看到效果。每次修改后只需要保存游戏就会定期重新加载更新内容尽管存在一定的延迟。 存在的问题 游戏的更新和重载有延迟这是因为游戏仅每隔约两秒钟检查一次是否需要重新加载内容。开发者计划优化这个过程减少延迟提高游戏反应速度。另一个问题是如果在调试模式下运行游戏尝试编译时会遇到困难。具体问题在于当游戏正在运行并处于调试状态时Visual Studio 会锁定调试信息文件这导致无法编译更新的内容。编译器无法输出新的调试文件因为它试图覆盖已经被锁定的文件。 解决方案的探索 为了解决这个问题开发者希望找到一种方法在使用调试器时能够顺利进行编译和运行而不需要停止调试。目的是能够在不停止调试的情况下继续工作甚至进行实时代码修改Live coding。 总结 当前的目标是优化游戏的实时更新机制减少延迟并解决在调试模式下无法编译的问题。开发者希望找到一种方法使得调试和编译可以在不中断运行的情况下顺利进行从而提升开发效率。
这些问题和解决方案反映了在游戏开发和调试过程中常见的挑战尤其是在实时修改和调试时需要注意的文件锁定和更新问题。
# CMake 项目的基础配置
# 创建静态库或者共享库可以选择 comment 出静态库行来生成不同的库类型。
# 如果你想要创建静态库请取消下面一行的注释
# add_library(game STATIC game.cpp) # 生成 game.lib# 创建共享库DLL
add_library(game SHARED game.cpp) # 生成 game.dll# 设置目标属性手动添加 EXPORT 标志
set_target_properties(game PROPERTIESLINK_FLAGS /EXPORT:GameUpdateAndRender /EXPORT:GameGetSoundSamples # 设置导出函数的符号
)# 创建 win32_game 可执行文件指定源文件
add_executable(win32_game WIN32 win32_game.cpp)# 为编译器添加定义的全局宏
add_compile_definitions(GAME_SLOW1 GAME_INTERNAL1)# 获取当前时间格式化为 年月日_时分秒
string(TIMESTAMP CURRENT_DATE %Y%m%d_%H%M%S)
message(Current date and time: ${CURRENT_DATE})# 为 game 目标设置 PDB 文件路径包含当前时间戳
# 这会生成动态链接库的 PDB 文件文件名包括编译时的时间戳
if(MSVC) # 如果使用 Microsoft Visual C 编译器target_link_options(game PRIVATE /PDB:${CMAKE_BINARY_DIR}/game/game_${CURRENT_DATE}.pdb)
endif()# 使用 file(GLOB ...) 查找所有 .pdb 文件并逐个删除
file(GLOB PDB_FILES ${CMAKE_BINARY_DIR}/game/game_*.pdb)# 创建清理所有 .pdb 文件的自定义目标 clean-all
add_custom_target(clean-allCOMMAND ${CMAKE_COMMAND} -E echo Cleaning up game.pdb files
)# 遍历所有 .pdb 文件并为每个文件添加删除命令
foreach(PDB_FILE ${PDB_FILES})add_custom_command(TARGET clean-allPOST_BUILD # 在构建之后执行COMMAND ${CMAKE_COMMAND} -E remove ${PDB_FILE} # 删除每个 .pdb 文件COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_SOURCE_DIR}/CMakeLists.txtCOMMENT removing ${PDB_FILE} # 注释说明正在删除的文件COMMENT Updating CMakeLists.txt timestamp # 跟新CMakeLists.txt时间戳)
endforeach()# 确保在构建 game 之前先执行 clean-all 清理操作
add_dependencies(game clean-all)# 链接 Win32 库到 win32_game
# 这里链接了三个 Windows 库User32.lib、Gdi32.lib 和 Winmm.lib
target_link_libraries(win32_game PRIVATE User32.lib Gdi32.lib Winmm.lib)# 如果使用 MSVC 编译器添加编译选项
if(MSVC) # 如果编译器是 MSVCMicrosoft Visual C# 设置 C 编译选项# /WX : 将所有警告视为错误会让编译因警告失败# /W4 : 设置警告级别为 4显示大多数警告# /wd4819 : 屏蔽警告 C4819避免文件编码问题导致的警告# /wd4201 : 屏蔽警告 C4201避免由于结构体定义引起的警告# /wd4505 : 屏蔽警告 C4505避免由于不使用的函数引起的警告# /Zi : 生成调试信息# /FC : 显示完整的文件名和行号set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} /WX /W4 /wd4819 /wd4201 /wd4505 /Zi /FC)# 设置目标属性手动关闭增量链接功能incremental linking以确保导出函数的符号set_target_properties(win32_game PROPERTIESLINK_FLAGS -incremental:no # 禁用增量链接)
endif()# 确保使用 C20 标准仅在 CMake 版本大于 3.12 时才有效
if(CMAKE_VERSION VERSION_GREATER 3.12)set_property(TARGET win32_game PROPERTY CXX_STANDARD 20)set_property(TARGET game PROPERTY CXX_STANDARD 20)
endif()现在就可以在调试模式下面跟新人更新dll
管道输出 stdout 和 stderr
命令 del *.pdb NUL 2 NUL 在 Windows 命令行中执行时具有以下含义
1. del *.pdb
这是删除当前目录下所有 .pdb 文件的命令。
del 是删除命令。*.pdb 表示当前目录下所有以 .pdb 结尾的文件即所有的 PDB 文件。
2. NUL
这是将标准输出stdout重定向到 NUL 设备。 是重定向操作符表示将命令的输出写入到指定的位置。NUL 是 Windows 中的特殊设备类似于 Unix/Linux 系统中的 /dev/null它表示“丢弃输出”。 这意味着执行 del *.pdb 时标准输出通常是删除的文件名将被丢弃而不会显示在命令行窗口中。
3. 2 NUL
这是将标准错误输出stderr重定向到 NUL 设备。
2 是重定向标准错误输出stderr的操作符。2 指的是标准错误输出流stderr的文件描述符0 是标准输入1 是标准输出2 是标准错误输出。NUL 再次表示丢弃错误输出。也就是说如果删除命令遇到错误比如没有找到任何 .pdb 文件错误信息将不会显示在命令行窗口中。
综合起来
del *.pdb NUL 2 NUL 的意思是
删除当前目录下的所有 .pdb 文件。如果删除成功删除的文件名不会显示在命令行中因为标准输出被重定向到 NUL。如果发生错误例如没有 .pdb 文件错误信息也不会显示在命令行中因为标准错误输出被重定向到 NUL。
这样使用通常是为了避免在命令执行时看到不必要的输出和错误信息确保输出干净。
发现热重载仍然存在延迟
这段代码描述了一种通过检查文件的日期戳来减少延迟的优化方法重点在于通过检查文件的修改时间来确定是否需要重新加载某个文件。这种方法相对简单且高效可以在每一帧中检查文件的修改日期从而避免延迟。
主要思路 减少延迟通过定期检查文件的日期戳来减少性能损失。这种方法通过周期性地检查文件的最后修改时间来确定是否需要重新加载文件而不需要每次都加载文件从而减少延迟。 文件日期戳文件系统中每个文件都有一个日期戳表示文件最后一次被访问、修改或创建的时间。通过获取这些信息可以判断文件是否发生变化。检查文件的日期戳比打开文件进行读取要更高效避免了额外的性能消耗。 优化实现 文件句柄与日期戳为了避免打开文件可以使用文件句柄获取文件的时间信息如创建时间、最后访问时间和最后修改时间。文件检查函数使用FindFirstFile等API函数可以获取文件的创建、访问和修改时间通过这些数据判断文件是否需要重新加载。缓存和临时处理为了避免不必要的重复计算和性能开销使用缓存技术将文件的日期戳保存并在需要时进行比较避免每次都重复操作。 错误处理与容错 如果文件不存在程序应该返回一个无效的句柄并进行适当的错误处理避免程序崩溃。在日期戳检查过程中如果文件不存在或无法访问程序会根据需要返回默认值如0确保系统继续运行而不被中断。 调试与监控 在开发和调试阶段可能会使用一些调试信息来监控文件的最后修改时间等帮助开发者识别潜在的问题。这种优化方法并不依赖于非常精确的数据因此即使日期戳并非完全精确它仍然能有效减少延迟。
总结
通过这种方法开发者能够高效地判断文件是否需要重新加载减少不必要的计算并通过检查文件的日期戳来优化性能。这种方法简洁且高效适用于那些需要频繁加载文件但又不能接受高延迟的系统。
FindFirstFileA 是一个 Windows API 函数用于查找匹配指定文件名的第一个文件。它返回一个句柄供后续调用其他函数如 FindNextFileA继续查找。
函数原型
HANDLE WINAPI FindFirstFileA(_In_ LPCSTR lpFileName,_Out_ LPWIN32_FIND_DATAA lpFindFileData
);参数说明 lpFileName (LPCSTR) 这是一个指向以 null 终止的字符串的指针表示要搜索的文件路径。该路径可以包含通配符字符如 * 或 ?用于匹配多个文件。例如 C:\\path\\to\\files\\*.txt 用于查找该路径下的所有 .txt 文件。C:\\path\\to\\files\\* 用于查找所有文件。 lpFindFileData (LPWIN32_FIND_DATAA) 这是一个指向 WIN32_FIND_DATAA 结构体的指针FindFirstFileA 函数将文件信息存储到该结构体中。这个结构体包含了文件的各种属性如文件名、文件类型、大小、最后修改时间等。 WIN32_FIND_DATAA 结构体定义如下 typedef struct _WIN32_FIND_DATAA {DWORD dwFileAttributes; // 文件的属性如普通文件、目录、只读等FILETIME ftCreationTime; // 文件创建时间FILETIME ftLastAccessTime; // 文件最后访问时间FILETIME ftLastWriteTime; // 文件最后写入时间DWORD nFileSizeHigh; // 文件大小高位DWORD nFileSizeLow; // 文件大小低位DWORD dwReserved0; // 保留字段DWORD dwReserved1; // 保留字段CHAR cFileName[MAX_PATH]; // 文件名CHAR cAlternateFileName[14]; // 文件的备用名称
} WIN32_FIND_DATAA;返回值
如果成功FindFirstFileA 返回一个有效的搜索句柄HANDLE。该句柄用于后续的查找操作如 FindNextFileA 和 FindClose。如果失败返回 INVALID_HANDLE_VALUE。你可以通过调用 GetLastError() 来获取更多的错误信息。
常见用法
FindFirstFileA 通常与 FindNextFileA 配合使用以便遍历所有匹配的文件。完成搜索后必须调用 FindClose 来关闭搜索句柄。
示例代码
#include windows.h
#include iostreamint main() {WIN32_FIND_DATAA findFileData;HANDLE hFind FindFirstFileA(C:\\path\\to\\files\\*.txt, findFileData);if (hFind INVALID_HANDLE_VALUE) {std::cerr FindFirstFileA failed! std::endl;return 1;}do {// 输出匹配文件的文件名std::cout Found file: findFileData.cFileName std::endl;} while (FindNextFileA(hFind, findFileData) ! 0); // 查找下一个文件FindClose(hFind); // 关闭搜索句柄return 0;
}函数的工作流程
调用 FindFirstFileA 时它会查找第一个匹配的文件并将文件的相关信息存储在 lpFindFileData 中。如果找到文件则 FindFirstFileA 返回一个有效的句柄你可以使用 FindNextFileA 来查找后续文件。使用 FindNextFileA 可以继续查找下一个文件直到没有更多匹配的文件为止。使用 FindClose 关闭句柄释放资源。
常见错误
INVALID_HANDLE_VALUE如果调用失败返回这个值。你可以使用 GetLastError 获取详细的错误信息。路径格式问题确保传递给 FindFirstFileA 的路径格式正确并且如果需要使用双反斜杠\\来表示文件路径。
总结
FindFirstFileA 是一个非常有用的函数尤其是用于列出文件或目录内容支持通配符并能够返回文件的详细信息。它通常与 FindNextFileA 和 FindClose 结合使用用于实现文件查找操作。 尝试将工作目录设置为数据目录发现 Win32GetLastWriteTime() 无法找到我们的 .dll 文件
目前面临的问题是如何确保程序能够正确加载DLL文件特别是在特定的构建目录和数据目录之间的路径管理上。之前的做法是在构建目录中加载这些文件而目标是将它们放回数据目录中。这样做的原因是所有艺术资产和其他必要的文件都应位于数据目录中。
在程序加载时Windows系统会自动通过搜索路径来查找执行文件所在目录下的DLL文件因此当执行文件和DLL文件位于相同目录时加载库load library会成功。这表明文件能在该路径下找到并被正确加载。
然而问题出现在程序的文件日期检查上。当文件已找到时日期检查未能通过可能是由于系统默认只在当前目录下查找文件而没有扩展到其他目录。这个问题可以通过修改路径设置来解决让程序能够在任何指定的目录中找到需要的DLL文件而不仅仅是在执行文件的目录中。
解决方案是调整文件路径使得无论实际的路径设置如何DLL文件都可以在与可执行文件相同的目录下被正确找到。这涉及到创建一个搜索路径确保DLL文件始终能够被加载哪怕路径发生了变化。
启用 WinMain() 使用 GetModuleFilenameA() 来定位我们的 .exe 文件
在当前的开发任务中目标是通过Windows操作系统的函数来查找和确认当前可执行文件的路径。为此提到了使用Windows API中的 GetModuleFileName 函数该函数可以帮助我们获取模块即可执行文件的路径。
首先提到的问题是如何准确地找到可执行文件所在的路径。这是关键因为所有相关的文件如DLL文件都需要根据正确的路径来定位。最简单的方法是使用 GetModuleFileName 函数并且通过传递一个值为零的参数可以自动获取当前可执行文件的路径而不需要显式传递模块句柄。这样可以确认当前执行文件所在的目录从而在该路径下寻找相关文件。
然而GetModuleFileName 函数有一些潜在的问题。例如如果传递的缓冲区大小不足以容纳路径字符串它可能会截断路径。这意味着如果缓冲区过小返回的路径可能会丢失部分信息导致后续操作失败。因此最好的做法是确保缓冲区的大小足够大以容纳完整的路径。
另外也提到 MAX_PATH 常量这个常量在Windows中定义了路径的最大字符数通常为260个字符。但这一限制现在已经不再适用因为Windows支持更长的路径。因此使用 MAX_PATH 时应谨慎避免可能带来的路径截断问题。
综上所述开发者在处理文件路径时应该避免使用过时的 MAX_PATH 常量并确保缓冲区大小足够避免路径截断带来的潜在问题。同时使用 GetModuleFileName 是获取可执行文件路径的一种有效方式但要注意对缓冲区大小的管理。 GetModuleHandle 是一个 Windows API 函数用于获取一个已经加载的模块如动态链接库 DLL 或可执行文件在内存中的句柄。这个句柄可以用来引用该模块在之后的操作中如获取模块路径、获取导出的函数等使用。
发现我们得到了完整路径