C++随笔:从Hello World 探秘CoreCLR的内部(1)
紧接着上次的问题,上次的问题其实很简单,就是HelloWorld.exe运行失败,而本文的目的,就是成功调试HelloWorld这个控制台应用程序。
通过我的寻找,其实是一个名为TryRun的文件出了问题,那但是我们还需要知道前因后果,而并不是单个的问题哦。首先你必须看这篇文章 把CoreCLR的调试环境配置好,然后才能去调试,我们按一下F11,开始我们的调试之旅。
如果你修改过CoreCLR的代码,别忘了生成项目,否则会出现如下错误,其实,改改底层真的很酷。千万别重新生成整个解决方案,会很费时间 = =。
碰到了上图的问题,怎么解决呢?这是由于文件不是最新的造成的原因,因为我们加载exe和pdb是从Product文件夹下面去加载,但是其实每次生成的并不在Product底下,而是在这里面\bin\obj\Windows_NT.x64.Debug\src\coreclr\hosts\corerun\Debug,EXE文件和pdb文件都需要复制的,别漏掉了~~~
终于开始我们的调试之旅了~~~代码我还是打算和以前一样,一个步骤写一部分~~下面的Argv其实指的是启动项目->属性->调试当中的“命令”部分,而newArgv其实指的是“工作目录”部分,有兴趣研究的可以去看我写的上一篇博文,有详细的介绍argv+1其实是把它的地址向高位移动wchar_t个字节,也就是32位,大家可以看下面图的地址变化。
1 2 3 4 5 6 7 8 9 10 11 | int __cdecl wmain( const int argc, const wchar_t * argv[]) { // Parse the options from the command line bool verbose = false ; bool waitForDebugger = false ; bool helpRequested = false ; int newArgc = argc - 1; const wchar_t **newArgv = argv + 1; } |
参数释义:
- Verbose - 打印出运算过程(日志),可以类比EF中从Nuget控制台里面migration时的verbose.
- WaitForDebugger 运行参数,等待调试;
- helpRequested 运行参数,帮助 ;
下面我们来看如下代码;看了如下的代码,你还能淡定的说,Lambda表达式是.NET独有的吗?如果你还没有接触过c++中的Lambda,那么建议你看看这篇文章。下面的你就可以简单的理解为,比较2个字符串是否相等。
1 2 3 | auto stringsEqual = []( const wchar_t * const a, const wchar_t * const b) -> bool { return ::_wcsicmp(a, b) == 0; }; |
当然,上面的Lambda表达式,其实是为下面的代码服务的,这些都是option,类似你在CMD里面敲了一个命令,后面还带了一些参数 比如 abc.exe -h.注意,这里的LAMBDA表达式不是写完就立即运行的,它只相当于一个方法的声明,也就是相当于你写了一个巨复杂的委托,但是没有用到过。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | auto tryParseOption = [&]( const wchar_t * arg) -> bool { if ( stringsEqual(arg, W( "/v" )) || stringsEqual(arg, W( "-v" )) ) { verbose = true ; return true ; } else if ( stringsEqual(arg, W( "/d" )) || stringsEqual(arg, W( "-d" )) ) { waitForDebugger = true ; return true ; } else if ( stringsEqual(arg, W( "/?" )) || stringsEqual(arg, W( "-?" )) || stringsEqual(arg, W( "-h" )) || stringsEqual(arg, W( "--help" )) ) { helpRequested = true ; return true ; } else { return false ; } }; |
下面的代码才开始用到,下面的代码是循环去找参数,因为有可能你的命令不止一个参数。
1 2 3 4 | while (newArgc > 0 && tryParseOption(newArgv[0])) { newArgc--; newArgv++; } |
大家可能会很感兴趣,为什么这里的arg的个数是2?,哈哈,这就要问.NET中的main函数了,我也不知道为什么,不过从下面的代码可以得知,main函数至少需要2个参数。
1 2 3 4 | if (argc < 2 || helpRequested || newArgc==0) { showHelp(); return -1; } |
经过了一系列的准备工作,我们终于可以进入TryRun方法了。
1 | auto success = TryRun(newArgc, newArgv, log , verbose, waitForDebugger, exitCode); |
下面的注释也已经写了,有关HostEnvironment的介绍,不过因为我当时的随笔记录的是比较老的东西,所以新东西的话会有所不同,不过 意思大同小异。
1 2 3 4 5 6 | // Assume failure exitCode = -1; //宿主环境(这个大家有没有印象呢?我在以下随笔中有介绍过),不过当时是在coreconsole这个文件里面 //http://www.cnblogs.com/kmsfan/p/5507149.html HostEnvironment hostEnvironment(& log ); |
首先是检查有没有命令参数,当然是从外面传进来的。
1 2 3 4 5 6 7 | //寻找指定的EXE文件,使用LoadLibraray去完成。 const wchar_t * exeName = argc > 0 ? argv[0] : nullptr ; if (exeName == nullptr ) { log << W( "No exename specified." ) << Logger::endl; return false ; } |
然后我们看到定义了如下的一些路径,当然最后一个我暂时还不知道什么意思;
1 2 3 4 | StackSString appPath; //应用程序路径 StackSString appNiPath; //包含ni结尾的应用程序的路径 StackSString managedAssemblyFullName; //托管程序集的全名 StackSString appLocalWinmetadata; |
下面是StackSString的介绍,意思是把这些string类型的数据存放在栈的空闲区域作为预处理缓冲(不止翻译得对不对)。它还可以规定最大大小,比如这个是最大512个字节的字符串。
1 2 3 4 5 6 | // ================================================================================ // StackSString is a lot like CQuickBytes. Use it to create an SString object // using some stack space as a preallocated buffer. // ================================================================================ typedef InlineSString<512> StackSString; |
关于NULL的值到底是什么,这里还是有讲究的,如果是C++,那么就是0 如果不是C++,那么就是void类型的指针。
1 2 3 4 5 6 7 8 | <br> wchar_t * filePart = NULL; //文件部分<br><br>//定义在vcruntime.h中 #ifndef NULL #ifdef __cplusplus #define NULL 0 #else #define NULL ((void *)0) #endif #endif |
下面也是一个类似的定义。
1 2 3 4 | COUNT_T size = MAX_LONGPATH; //定义初始大小 //定义在palclr.h中 #define MAX_LONGPATH 260 /* max. length of full pathname */ |
再来看看如下代码,我们可以从代码当中去看看appPathPtr是什么,那么,我想,会不会是因为HelloWorld的路径有误呢 ?经过我发现,文件还是有的,所以排除此原因。
1 2 | wchar_t * appPathPtr = appPath.OpenUnicodeBuffer(size - 1); DWORD length = WszGetFullPathName(exeName, size, appPathPtr, &filePart); |
下面就是一系列判参~~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //如果长度大于规定的最大长度 //注意这2个if是都可能执行到的~~ if (length >= size) { appPath.CloseBuffer(); size = length; appPathPtr = appPath.OpenUnicodeBuffer(size - 1); length = WszGetFullPathName(exeName, size, appPathPtr, &filePart); } if (length == 0 || length >= size) { log << W( "Failed to get full path: " ) << exeName << Logger::endl; log << W( "Error code: " ) << GetLastError() << Logger::endl; return false ; } |
今天不写太多了我就告诉大家哪里出了问题,是下面的代码出了问题,这个代码在corehost.cpp里面,属于cee_dac这个工程,但是我没有找到这个工程调试的入口,谷歌也找不到,如果有人知道,那就下面留言告诉我。
1 | hr = host->Start(); |
__EOF__
作 者:ღKawaii
出 处:https://www.cnblogs.com/kmsfan/p/5528826.html
关于博主:一个普通的小码农,为了梦想奋斗
版权声明:署名 - 非商业性使用 - 禁止演绎,协议普通文本 | 协议法律文本。
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!

出处:http://www.cnblogs.com/kmsfan
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
欢迎大家加入KMSFan之家,以及访问我的优酷空间!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?